d3d12: Add video encode implementation of pipe_video_codec

Acked-by: Jesse Natalie <jenatali@microsoft.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/16286>
This commit is contained in:
Sil Vilerino
2022-05-02 10:06:27 -07:00
committed by Marge Bot
parent d8206f6286
commit b171a6baa2
15 changed files with 4645 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,321 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef D3D12_VIDEO_ENC_H
#define D3D12_VIDEO_ENC_H
#include "d3d12_video_types.h"
#include "d3d12_video_encoder_references_manager.h"
#include "d3d12_video_dpb_storage_manager.h"
#include "d3d12_video_encoder_bitstream_builder_h264.h"
///
/// Pipe video interface starts
///
/**
* creates a video encoder
*/
struct pipe_video_codec *
d3d12_video_encoder_create_encoder(struct pipe_context *context, const struct pipe_video_codec *templ);
/**
* destroy this video encoder
*/
void
d3d12_video_encoder_destroy(struct pipe_video_codec *codec);
/**
* start encoding of a new frame
*/
void
d3d12_video_encoder_begin_frame(struct pipe_video_codec * codec,
struct pipe_video_buffer *target,
struct pipe_picture_desc *picture);
/**
* encode to a bitstream
*/
void
d3d12_video_encoder_encode_bitstream(struct pipe_video_codec * codec,
struct pipe_video_buffer *source,
struct pipe_resource * destination,
void ** feedback);
/**
* get encoder feedback
*/
void
d3d12_video_encoder_get_feedback(struct pipe_video_codec *codec, void *feedback, unsigned *size);
/**
* end encoding of the current frame
*/
void
d3d12_video_encoder_end_frame(struct pipe_video_codec * codec,
struct pipe_video_buffer *target,
struct pipe_picture_desc *picture);
/**
* flush any outstanding command buffers to the hardware
* should be called before a video_buffer is acessed by the gallium frontend again
*/
void
d3d12_video_encoder_flush(struct pipe_video_codec *codec);
///
/// Pipe video interface ends
///
enum d3d12_video_encoder_config_dirty_flags
{
d3d12_video_encoder_config_dirty_flag_none = 0x0,
d3d12_video_encoder_config_dirty_flag_codec = 0x1,
d3d12_video_encoder_config_dirty_flag_profile = 0x2,
d3d12_video_encoder_config_dirty_flag_level = 0x4,
d3d12_video_encoder_config_dirty_flag_codec_config = 0x8,
d3d12_video_encoder_config_dirty_flag_input_format = 0x10,
d3d12_video_encoder_config_dirty_flag_resolution = 0x20,
d3d12_video_encoder_config_dirty_flag_rate_control = 0x40,
d3d12_video_encoder_config_dirty_flag_slices = 0x80,
d3d12_video_encoder_config_dirty_flag_gop = 0x100,
d3d12_video_encoder_config_dirty_flag_motion_precision_limit = 0x200,
};
DEFINE_ENUM_FLAG_OPERATORS(d3d12_video_encoder_config_dirty_flags);
///
/// d3d12_video_encoder functions starts
///
struct d3d12_video_encoder
{
struct pipe_video_codec base;
struct pipe_screen * m_screen;
struct d3d12_screen * m_pD3D12Screen;
///
/// D3D12 objects and context info
///
const uint m_NodeMask = 0u;
const uint m_NodeIndex = 0u;
ComPtr<ID3D12Fence> m_spFence;
uint m_fenceValue = 1u;
ComPtr<ID3D12VideoDevice3> m_spD3D12VideoDevice;
ComPtr<ID3D12VideoEncoder> m_spVideoEncoder;
ComPtr<ID3D12VideoEncoderHeap> m_spVideoEncoderHeap;
ComPtr<ID3D12CommandQueue> m_spEncodeCommandQueue;
ComPtr<ID3D12CommandAllocator> m_spCommandAllocator;
ComPtr<ID3D12VideoEncodeCommandList2> m_spEncodeCommandList;
ComPtr<ID3D12CommandQueue> m_spCopyQueue;
std::vector<D3D12_RESOURCE_BARRIER> m_transitionsBeforeCloseCmdList;
std::unique_ptr<d3d12_video_encoder_references_manager_interface> m_upDPBManager;
std::unique_ptr<d3d12_video_dpb_storage_manager_interface> m_upDPBStorageManager;
std::unique_ptr<d3d12_video_bitstream_builder_interface> m_upBitstreamBuilder;
bool m_needsGPUFlush = false;
ComPtr<ID3D12Resource> m_spResolvedMetadataBuffer;
ComPtr<ID3D12Resource> m_spMetadataOutputBuffer;
std::vector<uint8_t> m_BitstreamHeadersBuffer;
struct
{
bool m_fArrayOfTexturesDpb;
D3D12_VIDEO_ENCODER_SUPPORT_FLAGS m_SupportFlags;
D3D12_VIDEO_ENCODER_VALIDATION_FLAGS m_ValidationFlags;
D3D12_FEATURE_DATA_VIDEO_ENCODER_RESOLUTION_SUPPORT_LIMITS m_currentResolutionSupportCaps;
union
{
D3D12_VIDEO_ENCODER_PROFILE_H264 m_H264Profile;
D3D12_VIDEO_ENCODER_PROFILE_HEVC m_HEVCProfile;
} m_encoderSuggestedProfileDesc = {};
union
{
D3D12_VIDEO_ENCODER_LEVELS_H264 m_H264LevelSetting;
D3D12_VIDEO_ENCODER_LEVEL_TIER_CONSTRAINTS_HEVC m_HEVCLevelSetting;
} m_encoderLevelSuggestedDesc = {};
// Required size for the layout-resolved metadata buffer of current frame to be encoded
size_t m_resolvedLayoutMetadataBufferRequiredSize;
// The maximum number of slices that the output of the current frame to be encoded will contain
uint32_t m_MaxSlicesInOutput;
D3D12_FEATURE_DATA_VIDEO_ENCODER_RESOURCE_REQUIREMENTS m_ResourceRequirementsCaps;
} m_currentEncodeCapabilities;
struct
{
d3d12_video_encoder_config_dirty_flags m_ConfigDirtyFlags = d3d12_video_encoder_config_dirty_flag_none;
D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC m_currentResolution = {};
D3D12_BOX m_FrameCroppingCodecConfig = {};
D3D12_FEATURE_DATA_FORMAT_INFO m_encodeFormatInfo = {};
D3D12_VIDEO_ENCODER_CODEC m_encoderCodecDesc = {};
D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAGS m_seqFlags = D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAG_NONE;
/// As the following D3D12 Encode types have pointers in their structures, we need to keep a deep copy of them
union
{
D3D12_VIDEO_ENCODER_PROFILE_H264 m_H264Profile;
D3D12_VIDEO_ENCODER_PROFILE_HEVC m_HEVCProfile;
} m_encoderProfileDesc = {};
union
{
D3D12_VIDEO_ENCODER_LEVELS_H264 m_H264LevelSetting;
D3D12_VIDEO_ENCODER_LEVEL_TIER_CONSTRAINTS_HEVC m_HEVCLevelSetting;
} m_encoderLevelDesc = {};
struct
{
D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE m_Mode;
D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAGS m_Flags;
DXGI_RATIONAL m_FrameRate;
union
{
D3D12_VIDEO_ENCODER_RATE_CONTROL_CQP m_Configuration_CQP;
D3D12_VIDEO_ENCODER_RATE_CONTROL_CBR m_Configuration_CBR;
D3D12_VIDEO_ENCODER_RATE_CONTROL_VBR m_Configuration_VBR;
D3D12_VIDEO_ENCODER_RATE_CONTROL_QVBR m_Configuration_QVBR;
} m_Config;
} m_encoderRateControlDesc = {};
union
{
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264 m_H264Config;
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC m_HEVCConfig;
} m_encoderCodecSpecificConfigDesc = {};
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE m_encoderSliceConfigMode;
union
{
D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_SLICES m_SlicesPartition_H264;
D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_SLICES m_SlicesPartition_HEVC;
} m_encoderSliceConfigDesc = {};
union
{
D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE_H264 m_H264GroupOfPictures;
D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE_HEVC m_HEVCGroupOfPictures;
} m_encoderGOPConfigDesc = {};
union
{
D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA_H264 m_H264PicData;
D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA_HEVC m_HEVCPicData;
} m_encoderPicParamsDesc = {};
D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE m_encoderMotionPrecisionLimit =
D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_MAXIMUM;
D3D12_VIDEO_ENCODER_INTRA_REFRESH m_IntraRefresh = { D3D12_VIDEO_ENCODER_INTRA_REFRESH_MODE_NONE, 0 };
uint32_t m_IntraRefreshCurrentFrameIndex = 0;
} m_currentEncodeConfig;
};
bool
d3d12_video_encoder_create_command_objects(struct d3d12_video_encoder *pD3D12Enc);
bool
d3d12_video_encoder_reconfigure_session(struct d3d12_video_encoder *pD3D12Enc,
struct pipe_video_buffer * srcTexture,
struct pipe_picture_desc * picture);
bool
d3d12_video_encoder_update_current_encoder_config_state(struct d3d12_video_encoder *pD3D12Enc,
struct pipe_video_buffer * srcTexture,
struct pipe_picture_desc * picture);
bool
d3d12_video_encoder_reconfigure_encoder_objects(struct d3d12_video_encoder *pD3D12Enc,
struct pipe_video_buffer * srcTexture,
struct pipe_picture_desc * picture);
D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA
d3d12_video_encoder_get_current_picture_param_settings(struct d3d12_video_encoder *pD3D12Enc);
D3D12_VIDEO_ENCODER_LEVEL_SETTING
d3d12_video_encoder_get_current_level_desc(struct d3d12_video_encoder *pD3D12Enc);
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION
d3d12_video_encoder_get_current_codec_config_desc(struct d3d12_video_encoder *pD3D12Enc);
D3D12_VIDEO_ENCODER_PROFILE_DESC
d3d12_video_encoder_get_current_profile_desc(struct d3d12_video_encoder *pD3D12Enc);
D3D12_VIDEO_ENCODER_RATE_CONTROL
d3d12_video_encoder_get_current_rate_control_settings(struct d3d12_video_encoder *pD3D12Enc);
D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA
d3d12_video_encoder_get_current_slice_param_settings(struct d3d12_video_encoder *pD3D12Enc);
D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE
d3d12_video_encoder_get_current_gop_desc(struct d3d12_video_encoder *pD3D12Enc);
uint32_t
d3d12_video_encoder_get_current_max_dpb_capacity(struct d3d12_video_encoder *pD3D12Enc);
void
d3d12_video_encoder_create_reference_picture_manager(struct d3d12_video_encoder *pD3D12Enc);
void
d3d12_video_encoder_update_picparams_tracking(struct d3d12_video_encoder *pD3D12Enc,
struct pipe_video_buffer * srcTexture,
struct pipe_picture_desc * picture);
void
d3d12_video_encoder_calculate_metadata_resolved_buffer_size(uint32_t maxSliceNumber, size_t &bufferSize);
uint32_t
d3d12_video_encoder_calculate_max_slices_count_in_output(
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE slicesMode,
const D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_SLICES *slicesConfig,
uint32_t MaxSubregionsNumberFromCaps,
D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC sequenceTargetResolution,
uint32_t SubregionBlockPixelsSize);
bool
d3d12_video_encoder_prepare_output_buffers(struct d3d12_video_encoder *pD3D12Enc,
struct pipe_video_buffer * srcTexture,
struct pipe_picture_desc * picture);
uint32_t
d3d12_video_encoder_build_codec_headers(struct d3d12_video_encoder *pD3D12Enc);
void
d3d12_video_encoder_extract_encode_metadata(
struct d3d12_video_encoder * pD3D12Dec,
ID3D12Resource * pResolvedMetadataBuffer,
size_t resourceMetadataSize,
D3D12_VIDEO_ENCODER_OUTPUT_METADATA & encoderMetadata,
std::vector<D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA> &pSubregionsMetadata);
D3D12_VIDEO_ENCODER_CODEC
d3d12_video_encoder_get_current_codec(struct d3d12_video_encoder *pD3D12Enc);
bool d3d12_video_encoder_negotiate_requested_features_and_d3d12_driver_caps(struct d3d12_video_encoder *pD3D12Enc, D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT &capEncoderSupportData);
bool d3d12_video_encoder_query_d3d12_driver_caps(struct d3d12_video_encoder *pD3D12Enc, D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT &capEncoderSupportData);
bool d3d12_video_encoder_check_subregion_mode_support(struct d3d12_video_encoder *pD3D12Enc, D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE requestedSlicesMode);
///
/// d3d12_video_encoder functions ends
///
#endif

View File

@@ -0,0 +1,816 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "d3d12_video_enc.h"
#include "d3d12_video_enc_h264.h"
#include "util/u_video.h"
#include "d3d12_screen.h"
#include "d3d12_format.h"
void
d3d12_video_encoder_update_current_rate_control_h264(struct d3d12_video_encoder *pD3D12Enc,
pipe_h264_enc_picture_desc *picture)
{
auto previousConfig = pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc = {};
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_FrameRate.Numerator =
picture->rate_ctrl[0].frame_rate_num;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_FrameRate.Denominator =
picture->rate_ctrl[0].frame_rate_den;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags = D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_NONE;
switch (picture->rate_ctrl[0].rate_ctrl_method) {
case PIPE_H2645_ENC_RATE_CONTROL_METHOD_VARIABLE_SKIP:
case PIPE_H2645_ENC_RATE_CONTROL_METHOD_VARIABLE:
{
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Mode = D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_VBR;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_VBR.TargetAvgBitRate =
picture->rate_ctrl[0].target_bitrate;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_VBR.PeakBitRate =
picture->rate_ctrl[0].peak_bitrate;
} break;
case PIPE_H2645_ENC_RATE_CONTROL_METHOD_CONSTANT_SKIP:
case PIPE_H2645_ENC_RATE_CONTROL_METHOD_CONSTANT:
{
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Mode = D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CBR;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR.TargetBitRate =
picture->rate_ctrl[0].target_bitrate;
/* For CBR mode, to guarantee bitrate of generated stream complies with
* target bitrate (e.g. no over +/-10%), vbv_buffer_size should be same
* as target bitrate. Controlled by OS env var D3D12_VIDEO_ENC_CBR_FORCE_VBV_EQUAL_BITRATE
*/
if (D3D12_VIDEO_ENC_CBR_FORCE_VBV_EQUAL_BITRATE) {
debug_printf("[d3d12_video_encoder_h264] d3d12_video_encoder_update_current_rate_control_h264 D3D12_VIDEO_ENC_CBR_FORCE_VBV_EQUAL_BITRATE environment variable is set, "
", forcing VBV Size = Target Bitrate = %ld (bits)\n", pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR.TargetBitRate);
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags |=
D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_VBV_SIZES;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR.VBVCapacity =
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR.TargetBitRate;
}
} break;
case PIPE_H2645_ENC_RATE_CONTROL_METHOD_DISABLE:
{
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Mode = D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CQP;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CQP
.ConstantQP_FullIntracodedFrame = picture->quant_i_frames;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CQP
.ConstantQP_InterPredictedFrame_PrevRefOnly = picture->quant_p_frames;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CQP
.ConstantQP_InterPredictedFrame_BiDirectionalRef = picture->quant_b_frames;
} break;
default:
{
debug_printf("[d3d12_video_encoder_h264] d3d12_video_encoder_update_current_rate_control_h264 invalid RC "
"config, using default RC CQP mode\n");
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Mode = D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CQP;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CQP
.ConstantQP_FullIntracodedFrame = 30;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CQP
.ConstantQP_InterPredictedFrame_PrevRefOnly = 30;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CQP
.ConstantQP_InterPredictedFrame_BiDirectionalRef = 30;
} break;
}
if (memcmp(&previousConfig,
&pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc,
sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc)) != 0) {
pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_rate_control;
}
}
void
d3d12_video_encoder_update_current_frame_pic_params_info_h264(struct d3d12_video_encoder *pD3D12Enc,
struct pipe_video_buffer *srcTexture,
struct pipe_picture_desc *picture,
D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA &picParams,
bool &bUsedAsReference)
{
struct pipe_h264_enc_picture_desc *h264Pic = (struct pipe_h264_enc_picture_desc *) picture;
d3d12_video_bitstream_builder_h264 *pH264BitstreamBuilder =
dynamic_cast<d3d12_video_bitstream_builder_h264 *>(pD3D12Enc->m_upBitstreamBuilder.get());
assert(pH264BitstreamBuilder != nullptr);
bUsedAsReference = !h264Pic->not_referenced;
picParams.pH264PicData->pic_parameter_set_id = pH264BitstreamBuilder->get_active_pps_id();
picParams.pH264PicData->idr_pic_id = h264Pic->idr_pic_id;
picParams.pH264PicData->FrameType = d3d12_video_encoder_convert_frame_type(h264Pic->picture_type);
picParams.pH264PicData->PictureOrderCountNumber = h264Pic->pic_order_cnt;
picParams.pH264PicData->FrameDecodingOrderNumber = h264Pic->frame_num;
picParams.pH264PicData->List0ReferenceFramesCount = 0;
picParams.pH264PicData->pList0ReferenceFrames = nullptr;
picParams.pH264PicData->List1ReferenceFramesCount = 0;
picParams.pH264PicData->pList1ReferenceFrames = nullptr;
if (picParams.pH264PicData->FrameType == D3D12_VIDEO_ENCODER_FRAME_TYPE_H264_P_FRAME) {
picParams.pH264PicData->List0ReferenceFramesCount = h264Pic->num_ref_idx_l0_active_minus1 + 1;
picParams.pH264PicData->pList0ReferenceFrames = h264Pic->ref_idx_l0_list;
} else if (picParams.pH264PicData->FrameType == D3D12_VIDEO_ENCODER_FRAME_TYPE_H264_B_FRAME) {
picParams.pH264PicData->List0ReferenceFramesCount = h264Pic->num_ref_idx_l0_active_minus1 + 1;
picParams.pH264PicData->pList0ReferenceFrames = h264Pic->ref_idx_l0_list;
picParams.pH264PicData->List1ReferenceFramesCount = h264Pic->num_ref_idx_l1_active_minus1 + 1;
picParams.pH264PicData->pList1ReferenceFrames = h264Pic->ref_idx_l1_list;
}
}
D3D12_VIDEO_ENCODER_FRAME_TYPE_H264
d3d12_video_encoder_convert_frame_type(enum pipe_h2645_enc_picture_type picType)
{
switch (picType) {
case PIPE_H2645_ENC_PICTURE_TYPE_P:
{
return D3D12_VIDEO_ENCODER_FRAME_TYPE_H264_P_FRAME;
} break;
case PIPE_H2645_ENC_PICTURE_TYPE_B:
{
return D3D12_VIDEO_ENCODER_FRAME_TYPE_H264_B_FRAME;
} break;
case PIPE_H2645_ENC_PICTURE_TYPE_I:
{
return D3D12_VIDEO_ENCODER_FRAME_TYPE_H264_I_FRAME;
} break;
case PIPE_H2645_ENC_PICTURE_TYPE_IDR:
{
return D3D12_VIDEO_ENCODER_FRAME_TYPE_H264_IDR_FRAME;
} break;
default:
{
unreachable("Unsupported pipe_h2645_enc_picture_type");
} break;
}
}
///
/// Tries to configurate the encoder using the requested slice configuration
/// or falls back to single slice encoding.
///
bool
d3d12_video_encoder_negotiate_current_h264_slices_configuration(struct d3d12_video_encoder *pD3D12Enc,
pipe_h264_enc_picture_desc *picture)
{
///
/// Initialize single slice by default
///
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE requestedSlicesMode =
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME;
D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_SLICES requestedSlicesConfig = {};
requestedSlicesConfig.NumberOfSlicesPerFrame = 1;
///
/// Try to see if can accomodate for multi-slice request by user
///
if (picture->num_slice_descriptors > 1) {
/* Last slice can be less for rounding frame size and leave some error for mb rounding */
bool bUniformSizeSlices = true;
const double rounding_delta = 1.0;
for (uint32_t sliceIdx = 1; (sliceIdx < picture->num_slice_descriptors - 1) && bUniformSizeSlices; sliceIdx++) {
int64_t curSlice = picture->slices_descriptors[sliceIdx].num_macroblocks;
int64_t prevSlice = picture->slices_descriptors[sliceIdx - 1].num_macroblocks;
bUniformSizeSlices = bUniformSizeSlices && (std::abs(curSlice - prevSlice) <= rounding_delta);
}
uint32_t mbPerScanline =
pD3D12Enc->m_currentEncodeConfig.m_currentResolution.Width / D3D12_VIDEO_H264_MB_IN_PIXELS;
bool bSliceAligned = ((picture->slices_descriptors[0].num_macroblocks % mbPerScanline) == 0);
if (!bUniformSizeSlices &&
d3d12_video_encoder_check_subregion_mode_support(
pD3D12Enc,
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_PARTITIONING_SUBREGIONS_PER_FRAME)) {
if (D3D12_VIDEO_ENC_FALLBACK_SLICE_CONFIG) { // Check if fallback mode is enabled, or we should just fail
// without support
// Not supported to have custom slice sizes in D3D12 Video Encode fallback to uniform multi-slice
debug_printf(
"[d3d12_video_encoder_h264] WARNING: Requested slice control mode is not supported: All slices must "
"have the same number of macroblocks. Falling back to encoding uniform %d slices per frame.\n",
picture->num_slice_descriptors);
requestedSlicesMode =
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_PARTITIONING_SUBREGIONS_PER_FRAME;
requestedSlicesConfig.NumberOfSlicesPerFrame = picture->num_slice_descriptors;
debug_printf("[d3d12_video_encoder_h264] Using multi slice encoding mode: "
"D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_PARTITIONING_SUBREGIONS_PER_FRAME "
"with %d slices per frame.\n",
requestedSlicesConfig.NumberOfSlicesPerFrame);
} else {
debug_printf("[d3d12_video_encoder_h264] Requested slice control mode is not supported: All slices must "
"have the same number of macroblocks. To continue with uniform slices as a fallback, must "
"enable the OS environment variable D3D12_VIDEO_ENC_FALLBACK_SLICE_CONFIG");
return false;
}
} else if (bUniformSizeSlices && bSliceAligned &&
d3d12_video_encoder_check_subregion_mode_support(
pD3D12Enc,
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_PARTITIONING_ROWS_PER_SUBREGION)) {
// Number of macroblocks per slice is aligned to a scanline width, in which case we can
// use D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_PARTITIONING_ROWS_PER_SUBREGION
requestedSlicesMode = D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_PARTITIONING_ROWS_PER_SUBREGION;
requestedSlicesConfig.NumberOfRowsPerSlice = (picture->slices_descriptors[0].num_macroblocks / mbPerScanline);
debug_printf("[d3d12_video_encoder_h264] Using multi slice encoding mode: "
"D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_PARTITIONING_ROWS_PER_SUBREGION with "
"%d macroblocks rows per slice.\n",
requestedSlicesConfig.NumberOfRowsPerSlice);
} else if (bUniformSizeSlices &&
d3d12_video_encoder_check_subregion_mode_support(
pD3D12Enc,
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_PARTITIONING_SUBREGIONS_PER_FRAME)) {
requestedSlicesMode =
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_PARTITIONING_SUBREGIONS_PER_FRAME;
requestedSlicesConfig.NumberOfSlicesPerFrame = picture->num_slice_descriptors;
debug_printf("[d3d12_video_encoder_h264] Using multi slice encoding mode: "
"D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_PARTITIONING_SUBREGIONS_PER_FRAME "
"with %d slices per frame.\n",
requestedSlicesConfig.NumberOfSlicesPerFrame);
} else if (D3D12_VIDEO_ENC_FALLBACK_SLICE_CONFIG) { // Check if fallback mode is enabled, or we should just fail
// without support
// Fallback to single slice encoding (assigned by default when initializing variables requestedSlicesMode,
// requestedSlicesConfig)
debug_printf(
"[d3d12_video_encoder_h264] WARNING: Slice mode for %d slices with bUniformSizeSlices: %d - bSliceAligned "
"%d not supported by the D3D12 driver, falling back to encoding a single slice per frame.\n",
picture->num_slice_descriptors,
bUniformSizeSlices,
bSliceAligned);
} else {
debug_printf("[d3d12_video_encoder_h264] Requested slice control mode is not supported: All slices must "
"have the same number of macroblocks. To continue with uniform slices as a fallback, must "
"enable the OS environment variable D3D12_VIDEO_ENC_FALLBACK_SLICE_CONFIG");
return false;
}
}
if (!d3d12_video_encoder_compare_slice_config_h264_hevc(
pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigMode,
pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigDesc.m_SlicesPartition_H264,
requestedSlicesMode,
requestedSlicesConfig)) {
pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_slices;
}
pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigDesc.m_SlicesPartition_H264 = requestedSlicesConfig;
pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigMode = requestedSlicesMode;
return true;
}
D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE
d3d12_video_encoder_convert_h264_motion_configuration(struct d3d12_video_encoder *pD3D12Enc,
pipe_h264_enc_picture_desc *picture)
{
return D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_MAXIMUM;
}
D3D12_VIDEO_ENCODER_LEVELS_H264
d3d12_video_encoder_convert_level_h264(uint32_t h264SpecLevel)
{
switch (h264SpecLevel) {
case 10:
{
return D3D12_VIDEO_ENCODER_LEVELS_H264_1;
} break;
case 11:
{
return D3D12_VIDEO_ENCODER_LEVELS_H264_11;
} break;
case 12:
{
return D3D12_VIDEO_ENCODER_LEVELS_H264_12;
} break;
case 13:
{
return D3D12_VIDEO_ENCODER_LEVELS_H264_13;
} break;
case 20:
{
return D3D12_VIDEO_ENCODER_LEVELS_H264_2;
} break;
case 21:
{
return D3D12_VIDEO_ENCODER_LEVELS_H264_21;
} break;
case 22:
{
return D3D12_VIDEO_ENCODER_LEVELS_H264_22;
} break;
case 30:
{
return D3D12_VIDEO_ENCODER_LEVELS_H264_3;
} break;
case 31:
{
return D3D12_VIDEO_ENCODER_LEVELS_H264_31;
} break;
case 32:
{
return D3D12_VIDEO_ENCODER_LEVELS_H264_32;
} break;
case 40:
{
return D3D12_VIDEO_ENCODER_LEVELS_H264_4;
} break;
case 41:
{
return D3D12_VIDEO_ENCODER_LEVELS_H264_41;
} break;
case 42:
{
return D3D12_VIDEO_ENCODER_LEVELS_H264_42;
} break;
case 50:
{
return D3D12_VIDEO_ENCODER_LEVELS_H264_5;
} break;
case 51:
{
return D3D12_VIDEO_ENCODER_LEVELS_H264_51;
} break;
case 52:
{
return D3D12_VIDEO_ENCODER_LEVELS_H264_52;
} break;
case 60:
{
return D3D12_VIDEO_ENCODER_LEVELS_H264_6;
} break;
case 61:
{
return D3D12_VIDEO_ENCODER_LEVELS_H264_61;
} break;
case 62:
{
return D3D12_VIDEO_ENCODER_LEVELS_H264_62;
} break;
default:
{
unreachable("Unsupported H264 level");
} break;
}
}
void
d3d12_video_encoder_convert_from_d3d12_level_h264(D3D12_VIDEO_ENCODER_LEVELS_H264 level12,
uint32_t &specLevel,
uint32_t &constraint_set3_flag)
{
specLevel = 0;
constraint_set3_flag = 0;
switch (level12) {
case D3D12_VIDEO_ENCODER_LEVELS_H264_1:
{
specLevel = 10;
} break;
case D3D12_VIDEO_ENCODER_LEVELS_H264_1b:
{
specLevel = 11;
constraint_set3_flag = 1;
} break;
case D3D12_VIDEO_ENCODER_LEVELS_H264_11:
{
specLevel = 11;
} break;
case D3D12_VIDEO_ENCODER_LEVELS_H264_12:
{
specLevel = 12;
} break;
case D3D12_VIDEO_ENCODER_LEVELS_H264_13:
{
specLevel = 13;
} break;
case D3D12_VIDEO_ENCODER_LEVELS_H264_2:
{
specLevel = 20;
} break;
case D3D12_VIDEO_ENCODER_LEVELS_H264_21:
{
specLevel = 21;
} break;
case D3D12_VIDEO_ENCODER_LEVELS_H264_22:
{
specLevel = 22;
} break;
case D3D12_VIDEO_ENCODER_LEVELS_H264_3:
{
specLevel = 30;
} break;
case D3D12_VIDEO_ENCODER_LEVELS_H264_31:
{
specLevel = 31;
} break;
case D3D12_VIDEO_ENCODER_LEVELS_H264_32:
{
specLevel = 32;
} break;
case D3D12_VIDEO_ENCODER_LEVELS_H264_4:
{
specLevel = 40;
} break;
case D3D12_VIDEO_ENCODER_LEVELS_H264_41:
{
specLevel = 41;
} break;
case D3D12_VIDEO_ENCODER_LEVELS_H264_42:
{
specLevel = 42;
} break;
case D3D12_VIDEO_ENCODER_LEVELS_H264_5:
{
specLevel = 50;
} break;
case D3D12_VIDEO_ENCODER_LEVELS_H264_51:
{
specLevel = 51;
} break;
case D3D12_VIDEO_ENCODER_LEVELS_H264_52:
{
specLevel = 52;
} break;
case D3D12_VIDEO_ENCODER_LEVELS_H264_6:
{
specLevel = 60;
} break;
case D3D12_VIDEO_ENCODER_LEVELS_H264_61:
{
specLevel = 61;
} break;
case D3D12_VIDEO_ENCODER_LEVELS_H264_62:
{
specLevel = 62;
} break;
default:
{
unreachable("Unsupported D3D12_VIDEO_ENCODER_LEVELS_H264 value");
} break;
}
}
bool
d3d12_video_encoder_update_h264_gop_configuration(struct d3d12_video_encoder *pD3D12Enc,
pipe_h264_enc_picture_desc *picture)
{
// Only update GOP when it begins
if (picture->gop_cnt == 1) {
uint32_t GOPCoeff = picture->i_remain;
uint32_t GOPLength = picture->gop_size / GOPCoeff;
uint32_t PPicturePeriod = std::ceil(GOPLength / (double) (picture->p_remain / GOPCoeff)) - 1;
if (picture->pic_order_cnt_type == 1u) {
debug_printf("[d3d12_video_encoder_h264] Upper layer is requesting pic_order_cnt_type %d but D3D12 Video "
"only supports pic_order_cnt_type = 0 or pic_order_cnt_type = 2\n",
picture->pic_order_cnt_type);
return false;
}
const uint32_t max_pic_order_cnt_lsb = 2 * GOPLength;
const uint32_t max_max_frame_num = GOPLength;
double log2_max_frame_num_minus4 = std::max(0.0, std::ceil(std::log2(max_max_frame_num)) - 4);
double log2_max_pic_order_cnt_lsb_minus4 = std::max(0.0, std::ceil(std::log2(max_pic_order_cnt_lsb)) - 4);
assert(log2_max_frame_num_minus4 < UCHAR_MAX);
assert(log2_max_pic_order_cnt_lsb_minus4 < UCHAR_MAX);
assert(picture->pic_order_cnt_type < UCHAR_MAX);
// Set dirty flag if m_H264GroupOfPictures changed
auto previousGOPConfig = pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_H264GroupOfPictures;
pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_H264GroupOfPictures = {
GOPLength,
PPicturePeriod,
static_cast<uint8_t>(picture->pic_order_cnt_type),
static_cast<uint8_t>(log2_max_frame_num_minus4),
static_cast<uint8_t>(log2_max_pic_order_cnt_lsb_minus4)
};
if (memcmp(&previousGOPConfig,
&pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_H264GroupOfPictures,
sizeof(D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE_H264)) != 0) {
pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_gop;
}
}
return true;
}
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264
d3d12_video_encoder_convert_h264_codec_configuration(struct d3d12_video_encoder *pD3D12Enc,
pipe_h264_enc_picture_desc *picture)
{
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264 config = {
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264_FLAG_NONE,
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264_DIRECT_MODES_DISABLED,
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264_SLICES_DEBLOCKING_MODE_0_ALL_LUMA_CHROMA_SLICE_BLOCK_EDGES_ALWAYS_FILTERED,
};
if (picture->pic_ctrl.enc_cabac_enable) {
config.ConfigurationFlags |= D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264_FLAG_ENABLE_CABAC_ENCODING;
}
return config;
}
bool
d3d12_video_encoder_update_current_encoder_config_state_h264(struct d3d12_video_encoder *pD3D12Enc,
struct pipe_video_buffer *srcTexture,
struct pipe_picture_desc *picture)
{
struct pipe_h264_enc_picture_desc *h264Pic = (struct pipe_h264_enc_picture_desc *) picture;
// Reset reconfig dirty flags
pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags = d3d12_video_encoder_config_dirty_flag_none;
// Reset sequence changes flags
pD3D12Enc->m_currentEncodeConfig.m_seqFlags = D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAG_NONE;
// Set codec
if (pD3D12Enc->m_currentEncodeConfig.m_encoderCodecDesc != D3D12_VIDEO_ENCODER_CODEC_H264) {
pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_codec;
}
pD3D12Enc->m_currentEncodeConfig.m_encoderCodecDesc = D3D12_VIDEO_ENCODER_CODEC_H264;
// Set input format
DXGI_FORMAT targetFmt = d3d12_convert_pipe_video_profile_to_dxgi_format(pD3D12Enc->base.profile);
if (pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo.Format != targetFmt) {
pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_input_format;
}
pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo = {};
pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo.Format = targetFmt;
HRESULT hr = pD3D12Enc->m_pD3D12Screen->dev->CheckFeatureSupport(D3D12_FEATURE_FORMAT_INFO,
&pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo,
sizeof(pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo));
if (FAILED(hr)) {
debug_printf("CheckFeatureSupport failed with HR %x\n", hr);
return false;
}
// Set resolution
if ((pD3D12Enc->m_currentEncodeConfig.m_currentResolution.Width != srcTexture->width) ||
(pD3D12Enc->m_currentEncodeConfig.m_currentResolution.Height != srcTexture->height)) {
pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_resolution;
}
pD3D12Enc->m_currentEncodeConfig.m_currentResolution.Width = srcTexture->width;
pD3D12Enc->m_currentEncodeConfig.m_currentResolution.Height = srcTexture->height;
// Set resolution codec dimensions (ie. cropping)
if (h264Pic->pic_ctrl.enc_frame_cropping_flag) {
pD3D12Enc->m_currentEncodeConfig.m_FrameCroppingCodecConfig.left = h264Pic->pic_ctrl.enc_frame_crop_left_offset;
pD3D12Enc->m_currentEncodeConfig.m_FrameCroppingCodecConfig.right = h264Pic->pic_ctrl.enc_frame_crop_right_offset;
pD3D12Enc->m_currentEncodeConfig.m_FrameCroppingCodecConfig.top = h264Pic->pic_ctrl.enc_frame_crop_top_offset;
pD3D12Enc->m_currentEncodeConfig.m_FrameCroppingCodecConfig.bottom =
h264Pic->pic_ctrl.enc_frame_crop_bottom_offset;
} else {
memset(&pD3D12Enc->m_currentEncodeConfig.m_FrameCroppingCodecConfig,
0,
sizeof(pD3D12Enc->m_currentEncodeConfig.m_FrameCroppingCodecConfig));
}
// Set profile
auto targetProfile = d3d12_video_encoder_convert_profile_to_d3d12_enc_profile_h264(pD3D12Enc->base.profile);
if (pD3D12Enc->m_currentEncodeConfig.m_encoderProfileDesc.m_H264Profile != targetProfile) {
pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_profile;
}
pD3D12Enc->m_currentEncodeConfig.m_encoderProfileDesc.m_H264Profile = targetProfile;
// Set level
auto targetLevel = d3d12_video_encoder_convert_level_h264(pD3D12Enc->base.level);
if (pD3D12Enc->m_currentEncodeConfig.m_encoderLevelDesc.m_H264LevelSetting != targetLevel) {
pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_level;
}
pD3D12Enc->m_currentEncodeConfig.m_encoderLevelDesc.m_H264LevelSetting = targetLevel;
// Set codec config
auto targetCodecConfig = d3d12_video_encoder_convert_h264_codec_configuration(pD3D12Enc, h264Pic);
if (memcmp(&pD3D12Enc->m_currentEncodeConfig.m_encoderCodecSpecificConfigDesc.m_H264Config,
&targetCodecConfig,
sizeof(D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264)) != 0) {
pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_codec_config;
}
pD3D12Enc->m_currentEncodeConfig.m_encoderCodecSpecificConfigDesc.m_H264Config = targetCodecConfig;
// Set rate control
d3d12_video_encoder_update_current_rate_control_h264(pD3D12Enc, h264Pic);
// Set slices config
if(!d3d12_video_encoder_negotiate_current_h264_slices_configuration(pD3D12Enc, h264Pic)) {
debug_printf("d3d12_video_encoder_negotiate_current_h264_slices_configuration failed!\n");
return false;
}
// Set GOP config
if(!d3d12_video_encoder_update_h264_gop_configuration(pD3D12Enc, h264Pic)) {
debug_printf("d3d12_video_encoder_update_h264_gop_configuration failed!\n");
return false;
}
// m_currentEncodeConfig.m_encoderPicParamsDesc pic params are set in d3d12_video_encoder_reconfigure_encoder_objects
// after re-allocating objects if needed
// Set motion estimation config
auto targetMotionLimit = d3d12_video_encoder_convert_h264_motion_configuration(pD3D12Enc, h264Pic);
if (pD3D12Enc->m_currentEncodeConfig.m_encoderMotionPrecisionLimit != targetMotionLimit) {
pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |=
d3d12_video_encoder_config_dirty_flag_motion_precision_limit;
}
pD3D12Enc->m_currentEncodeConfig.m_encoderMotionPrecisionLimit = targetMotionLimit;
///
/// Check for video encode support detailed capabilities
///
// Will call for d3d12 driver support based on the initial requested features, then
// try to fallback if any of them is not supported and return the negotiated d3d12 settings
D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT capEncoderSupportData = {};
if (!d3d12_video_encoder_negotiate_requested_features_and_d3d12_driver_caps(pD3D12Enc, capEncoderSupportData)) {
debug_printf("[d3d12_video_encoder_h264] After negotiating caps, D3D12_FEATURE_VIDEO_ENCODER_SUPPORT "
"arguments are not supported - "
"ValidationFlags: 0x%x - SupportFlags: 0x%x\n",
capEncoderSupportData.ValidationFlags,
capEncoderSupportData.SupportFlags);
return false;
}
///
// Calculate current settings based on the returned values from the caps query
//
pD3D12Enc->m_currentEncodeCapabilities.m_MaxSlicesInOutput =
d3d12_video_encoder_calculate_max_slices_count_in_output(
pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigMode,
&pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigDesc.m_SlicesPartition_H264,
pD3D12Enc->m_currentEncodeCapabilities.m_currentResolutionSupportCaps.MaxSubregionsNumber,
pD3D12Enc->m_currentEncodeConfig.m_currentResolution,
pD3D12Enc->m_currentEncodeCapabilities.m_currentResolutionSupportCaps.SubregionBlockPixelsSize);
//
// Validate caps support returned values against current settings
//
if (pD3D12Enc->m_currentEncodeConfig.m_encoderProfileDesc.m_H264Profile !=
pD3D12Enc->m_currentEncodeCapabilities.m_encoderSuggestedProfileDesc.m_H264Profile) {
debug_printf("[d3d12_video_encoder_h264] Warning: Requested D3D12_VIDEO_ENCODER_PROFILE_H264 by upper layer: %d "
"mismatches UMD suggested D3D12_VIDEO_ENCODER_PROFILE_H264: %d\n",
pD3D12Enc->m_currentEncodeConfig.m_encoderProfileDesc.m_H264Profile,
pD3D12Enc->m_currentEncodeCapabilities.m_encoderSuggestedProfileDesc.m_H264Profile);
}
if (pD3D12Enc->m_currentEncodeConfig.m_encoderLevelDesc.m_H264LevelSetting !=
pD3D12Enc->m_currentEncodeCapabilities.m_encoderLevelSuggestedDesc.m_H264LevelSetting) {
debug_printf("[d3d12_video_encoder_h264] Warning: Requested D3D12_VIDEO_ENCODER_LEVELS_H264 by upper layer: %d "
"mismatches UMD suggested D3D12_VIDEO_ENCODER_LEVELS_H264: %d\n",
pD3D12Enc->m_currentEncodeConfig.m_encoderLevelDesc.m_H264LevelSetting,
pD3D12Enc->m_currentEncodeCapabilities.m_encoderLevelSuggestedDesc.m_H264LevelSetting);
}
if (pD3D12Enc->m_currentEncodeCapabilities.m_MaxSlicesInOutput >
pD3D12Enc->m_currentEncodeCapabilities.m_currentResolutionSupportCaps.MaxSubregionsNumber) {
debug_printf("[d3d12_video_encoder_h264] Desired number of subregions is not supported (higher than max "
"reported slice number in query caps)\n.");
return false;
}
return true;
}
D3D12_VIDEO_ENCODER_PROFILE_H264
d3d12_video_encoder_convert_profile_to_d3d12_enc_profile_h264(enum pipe_video_profile profile)
{
switch (profile) {
case PIPE_VIDEO_PROFILE_MPEG4_AVC_CONSTRAINED_BASELINE:
case PIPE_VIDEO_PROFILE_MPEG4_AVC_BASELINE:
case PIPE_VIDEO_PROFILE_MPEG4_AVC_MAIN:
{
return D3D12_VIDEO_ENCODER_PROFILE_H264_MAIN;
} break;
case PIPE_VIDEO_PROFILE_MPEG4_AVC_HIGH:
{
return D3D12_VIDEO_ENCODER_PROFILE_H264_HIGH;
} break;
case PIPE_VIDEO_PROFILE_MPEG4_AVC_HIGH10:
{
return D3D12_VIDEO_ENCODER_PROFILE_H264_HIGH_10;
} break;
default:
{
unreachable("Unsupported pipe_video_profile");
} break;
}
}
D3D12_VIDEO_ENCODER_CODEC
d3d12_video_encoder_convert_codec_to_d3d12_enc_codec(enum pipe_video_profile profile)
{
switch (u_reduce_video_profile(profile)) {
case PIPE_VIDEO_FORMAT_MPEG4_AVC:
{
return D3D12_VIDEO_ENCODER_CODEC_H264;
} break;
case PIPE_VIDEO_FORMAT_HEVC:
{
return D3D12_VIDEO_ENCODER_CODEC_HEVC;
} break;
case PIPE_VIDEO_FORMAT_MPEG12:
case PIPE_VIDEO_FORMAT_MPEG4:
case PIPE_VIDEO_FORMAT_VC1:
case PIPE_VIDEO_FORMAT_JPEG:
case PIPE_VIDEO_FORMAT_VP9:
case PIPE_VIDEO_FORMAT_UNKNOWN:
default:
{
unreachable("Unsupported pipe_video_profile");
} break;
}
}
bool
d3d12_video_encoder_compare_slice_config_h264_hevc(
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE targetMode,
D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_SLICES targetConfig,
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE otherMode,
D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_SLICES otherConfig)
{
return (targetMode == otherMode) &&
(memcmp(&targetConfig,
&otherConfig,
sizeof(D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_SLICES)) == 0);
}
uint32_t
d3d12_video_encoder_build_codec_headers_h264(struct d3d12_video_encoder *pD3D12Enc)
{
D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA currentPicParams =
d3d12_video_encoder_get_current_picture_param_settings(pD3D12Enc);
auto profDesc = d3d12_video_encoder_get_current_profile_desc(pD3D12Enc);
auto levelDesc = d3d12_video_encoder_get_current_level_desc(pD3D12Enc);
auto codecConfigDesc = d3d12_video_encoder_get_current_codec_config_desc(pD3D12Enc);
auto MaxDPBCapacity = d3d12_video_encoder_get_current_max_dpb_capacity(pD3D12Enc);
size_t writtenSPSBytesCount = 0;
bool isFirstFrame = (pD3D12Enc->m_fenceValue == 1);
bool writeNewSPS = isFirstFrame // on first frame
|| ((pD3D12Enc->m_currentEncodeConfig.m_seqFlags & // also on resolution change
D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAG_RESOLUTION_CHANGE) != 0);
d3d12_video_bitstream_builder_h264 *pH264BitstreamBuilder =
dynamic_cast<d3d12_video_bitstream_builder_h264 *>(pD3D12Enc->m_upBitstreamBuilder.get());
assert(pH264BitstreamBuilder);
uint32_t active_seq_parameter_set_id = pH264BitstreamBuilder->get_active_sps_id();
if (writeNewSPS) {
// For every new SPS for reconfiguration, increase the active_sps_id
if (!isFirstFrame) {
active_seq_parameter_set_id++;
pH264BitstreamBuilder->set_active_sps_id(active_seq_parameter_set_id);
}
pH264BitstreamBuilder->build_sps(*profDesc.pH264Profile,
*levelDesc.pH264LevelSetting,
pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo.Format,
*codecConfigDesc.pH264Config,
pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_H264GroupOfPictures,
active_seq_parameter_set_id,
MaxDPBCapacity, // max_num_ref_frames
pD3D12Enc->m_currentEncodeConfig.m_currentResolution,
pD3D12Enc->m_currentEncodeConfig.m_FrameCroppingCodecConfig,
pD3D12Enc->m_BitstreamHeadersBuffer,
pD3D12Enc->m_BitstreamHeadersBuffer.begin(),
writtenSPSBytesCount);
}
size_t writtenPPSBytesCount = 0;
pH264BitstreamBuilder->build_pps(*profDesc.pH264Profile,
*codecConfigDesc.pH264Config,
*currentPicParams.pH264PicData,
currentPicParams.pH264PicData->pic_parameter_set_id,
active_seq_parameter_set_id,
pD3D12Enc->m_BitstreamHeadersBuffer,
pD3D12Enc->m_BitstreamHeadersBuffer.begin() + writtenSPSBytesCount,
writtenPPSBytesCount);
// Shrink buffer to fit the headers
if (pD3D12Enc->m_BitstreamHeadersBuffer.size() > (writtenPPSBytesCount + writtenSPSBytesCount)) {
pD3D12Enc->m_BitstreamHeadersBuffer.resize(writtenPPSBytesCount + writtenSPSBytesCount);
}
return pD3D12Enc->m_BitstreamHeadersBuffer.size();
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef D3D12_VIDEO_ENC_H264_H
#define D3D12_VIDEO_ENC_H264_H
#include "d3d12_video_types.h"
bool
d3d12_video_encoder_update_current_encoder_config_state_h264(struct d3d12_video_encoder *pD3D12Enc,
struct pipe_video_buffer * srcTexture,
struct pipe_picture_desc * picture);
void
d3d12_video_encoder_update_current_rate_control_h264(struct d3d12_video_encoder *pD3D12Enc,
pipe_h264_enc_picture_desc *picture);
bool
d3d12_video_encoder_negotiate_current_h264_slices_configuration(struct d3d12_video_encoder *pD3D12Enc,
pipe_h264_enc_picture_desc *picture);
bool
d3d12_video_encoder_update_h264_gop_configuration(struct d3d12_video_encoder *pD3D12Enc,
pipe_h264_enc_picture_desc *picture);
D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE
d3d12_video_encoder_convert_h264_motion_configuration(struct d3d12_video_encoder *pD3D12Enc,
pipe_h264_enc_picture_desc *picture);
D3D12_VIDEO_ENCODER_LEVELS_H264
d3d12_video_encoder_convert_level_h264(uint32_t h264SpecLevel);
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264
d3d12_video_encoder_convert_h264_codec_configuration(struct d3d12_video_encoder *pD3D12Enc,
pipe_h264_enc_picture_desc *picture);
void
d3d12_video_encoder_update_current_frame_pic_params_info_h264(struct d3d12_video_encoder *pD3D12Enc,
struct pipe_video_buffer * srcTexture,
struct pipe_picture_desc * picture,
D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA &picParams,
bool &bUsedAsReference);
D3D12_VIDEO_ENCODER_FRAME_TYPE_H264
d3d12_video_encoder_convert_frame_type(enum pipe_h2645_enc_picture_type picType);
uint32_t
d3d12_video_encoder_build_codec_headers_h264(struct d3d12_video_encoder *pD3D12Enc);
bool
d3d12_video_encoder_compare_slice_config_h264_hevc(
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE targetMode,
D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_SLICES targetConfig,
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE otherMode,
D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_SLICES otherConfig);
#endif

View File

@@ -0,0 +1,276 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <climits>
#include "d3d12_video_encoder_bitstream.h"
d3d12_video_encoder_bitstream::d3d12_video_encoder_bitstream()
{
m_pBitsBuffer = nullptr;
m_uiBitsBufferSize = 0;
m_iBitsToGo = 32;
m_uintEncBuffer = 0;
m_bExternalBuffer = false;
m_bBufferOverflow = false;
m_bPreventStartCode = false;
m_bAllowReallocate = false;
}
d3d12_video_encoder_bitstream::~d3d12_video_encoder_bitstream()
{
if (!m_bExternalBuffer) {
if (m_pBitsBuffer) {
delete[](m_pBitsBuffer);
(m_pBitsBuffer) = NULL;
}
}
}
int32_t
d3d12_video_encoder_bitstream::get_exp_golomb0_code_len(uint32_t uiVal)
{
int32_t iLen = 0;
uiVal++;
if (uiVal >= 0x10000) {
uiVal >>= 16;
iLen += 16;
}
if (uiVal >= 0x100) {
uiVal >>= 8;
iLen += 8;
}
assert(uiVal < 256);
return iLen + m_iLog_2_N[uiVal];
}
void
d3d12_video_encoder_bitstream::exp_Golomb_ue(uint32_t uiVal)
{
if (uiVal != UINT32_MAX) {
int32_t iLen = get_exp_golomb0_code_len(uiVal);
put_bits((iLen << 1) + 1, uiVal + 1);
} else {
put_bits(32, 0);
put_bits(1, 1);
put_bits(32, 1);
}
}
void
d3d12_video_encoder_bitstream::exp_Golomb_se(int32_t iVal)
{
if (iVal > 0) {
exp_Golomb_ue((iVal << 1) - 1);
} else {
exp_Golomb_ue(((-iVal) << 1) - (iVal == INT_MIN));
}
}
void
d3d12_video_encoder_bitstream::setup_bitstream(uint32_t uiInitBufferSize, uint8_t *pBuffer)
{
m_pBitsBuffer = pBuffer;
m_uiBitsBufferSize = uiInitBufferSize;
m_uiOffset = 0;
memset(m_pBitsBuffer, 0, m_uiBitsBufferSize);
m_bExternalBuffer = true;
m_bAllowReallocate = false;
}
bool
d3d12_video_encoder_bitstream::create_bitstream(uint32_t uiInitBufferSize)
{
assert((uiInitBufferSize) >= 4 && !(uiInitBufferSize & 3));
m_pBitsBuffer = (uint8_t *) new uint8_t[uiInitBufferSize];
if (nullptr == m_pBitsBuffer) {
return false;
}
m_uiBitsBufferSize = uiInitBufferSize;
m_uiOffset = 0;
memset(m_pBitsBuffer, 0, m_uiBitsBufferSize);
m_bExternalBuffer = false;
return true;
}
bool
d3d12_video_encoder_bitstream::reallocate_buffer()
{
uint32_t uiBufferSize = m_uiBitsBufferSize * 3 / 2;
uint8_t *pNewBuffer = (uint8_t *) new uint8_t[uiBufferSize];
if (nullptr == pNewBuffer) {
return false;
}
memcpy(pNewBuffer, m_pBitsBuffer, m_uiOffset * sizeof(uint8_t));
if (m_pBitsBuffer) {
delete[](m_pBitsBuffer);
(m_pBitsBuffer) = NULL;
}
m_pBitsBuffer = pNewBuffer;
m_uiBitsBufferSize = uiBufferSize;
return true;
}
bool
d3d12_video_encoder_bitstream::verify_buffer(uint32_t uiBytesToWrite)
{
if (!m_bBufferOverflow) {
if (m_uiOffset + uiBytesToWrite > m_uiBitsBufferSize) {
if (!m_bAllowReallocate || !reallocate_buffer()) {
m_bBufferOverflow = true;
return false;
}
}
return true;
}
return false;
}
void
d3d12_video_encoder_bitstream::inc_current_offset(int32_t dwOffset)
{
assert(32 == m_iBitsToGo && m_uiOffset < m_uiBitsBufferSize);
m_uiOffset += dwOffset;
}
void
d3d12_video_encoder_bitstream::get_current_buffer_position_and_size(uint8_t **ppCurrBufPos, int32_t *pdwLeftBufSize)
{
assert(32 == m_iBitsToGo && m_uiOffset < m_uiBitsBufferSize);
*ppCurrBufPos = m_pBitsBuffer + m_uiOffset;
*pdwLeftBufSize = m_uiBitsBufferSize - m_uiOffset;
}
void
d3d12_video_encoder_bitstream::attach(uint8_t *pBitsBuffer, uint32_t uiBufferSize)
{
m_pBitsBuffer = pBitsBuffer;
m_uiBitsBufferSize = uiBufferSize;
m_bExternalBuffer = true;
m_bBufferOverflow = false;
m_bAllowReallocate = false;
clear();
}
void
d3d12_video_encoder_bitstream::write_byte_start_code_prevention(uint8_t u8Val)
{
int32_t iOffset = m_uiOffset;
uint8_t *pBuffer = m_pBitsBuffer + iOffset;
if (m_bPreventStartCode && iOffset > 1) {
if (((u8Val & 0xfc) | pBuffer[-2] | pBuffer[-1]) == 0) {
*pBuffer++ = 3;
iOffset++;
}
}
*pBuffer = u8Val;
iOffset++;
m_uiOffset = iOffset;
}
#define WRITE_BYTE(byte) write_byte_start_code_prevention(byte)
void
d3d12_video_encoder_bitstream::put_bits(int32_t uiBitsCount, uint32_t iBitsVal)
{
assert(uiBitsCount <= 32);
if (uiBitsCount < m_iBitsToGo) {
m_uintEncBuffer |= (iBitsVal << (m_iBitsToGo - uiBitsCount));
m_iBitsToGo -= uiBitsCount;
} else if (verify_buffer(4)) {
int32_t iLeftOverBits = uiBitsCount - m_iBitsToGo;
m_uintEncBuffer |= (iBitsVal >> iLeftOverBits);
uint8_t *temp = (uint8_t *) (&m_uintEncBuffer);
WRITE_BYTE(*(temp + 3));
WRITE_BYTE(*(temp + 2));
WRITE_BYTE(*(temp + 1));
WRITE_BYTE(*temp);
m_uintEncBuffer = 0;
m_iBitsToGo = 32 - iLeftOverBits;
if (iLeftOverBits > 0) {
m_uintEncBuffer = (iBitsVal << (32 - iLeftOverBits));
}
}
}
void
d3d12_video_encoder_bitstream::flush()
{
bool isAligned = is_byte_aligned(); // causes side-effects in object state, don't put inside assert()
assert(isAligned);
uint32_t temp = (uint32_t)(32 - m_iBitsToGo);
if (!verify_buffer(temp >> 3)) {
return;
}
while (temp > 0) {
WRITE_BYTE((uint8_t)(m_uintEncBuffer >> 24));
m_uintEncBuffer <<= 8;
temp -= 8;
}
m_iBitsToGo = 32;
m_uintEncBuffer = 0;
}
void
d3d12_video_encoder_bitstream::append_byte_stream(d3d12_video_encoder_bitstream *pStream)
{
bool isStreamAligned =
pStream->is_byte_aligned(); // causes side-effects in object state, don't put inside assert()
assert(isStreamAligned);
bool isThisAligned = is_byte_aligned(); // causes side-effects in object state, don't put inside assert()
assert(isThisAligned);
assert(m_iBitsToGo == 32);
uint8_t *pDst = m_pBitsBuffer + m_uiOffset;
uint8_t *pSrc = pStream->get_bitstream_buffer();
uint32_t uiLen = (uint32_t) pStream->get_byte_count();
if (!verify_buffer(uiLen)) {
return;
}
memcpy(pDst, pSrc, uiLen);
m_uiOffset += uiLen;
}

View File

@@ -0,0 +1,119 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef D3D12_VIDEO_ENC_BITSTREAM_H
#define D3D12_VIDEO_ENC_BITSTREAM_H
#include "d3d12_video_types.h"
class d3d12_video_encoder_bitstream
{
public:
d3d12_video_encoder_bitstream();
~d3d12_video_encoder_bitstream();
public:
void get_current_buffer_position_and_size(uint8_t **ppCurrBufPos, int32_t *pdwLeftBufSize);
void inc_current_offset(int32_t dwOffset);
bool create_bitstream(uint32_t uiInitBufferSize);
void setup_bitstream(uint32_t uiInitBufferSize, uint8_t *pBuffer);
void attach(uint8_t *pBitsBuffer, uint32_t uiBufferSize);
void put_bits(int32_t uiBitsCount, uint32_t iBitsVal);
void flush();
void exp_Golomb_ue(uint32_t uiVal);
void exp_Golomb_se(int32_t iVal);
inline void clear()
{
m_iBitsToGo = 32;
m_uiOffset = 0;
m_uintEncBuffer = 0;
};
void append_byte_stream(d3d12_video_encoder_bitstream *pStream);
void set_start_code_prevention(bool bSCP)
{
m_bPreventStartCode = bSCP;
}
int32_t get_bits_count()
{
return m_uiOffset * 8 + (32 - m_iBitsToGo);
}
int32_t get_byte_count()
{
return m_uiOffset + ((32 - m_iBitsToGo) >> 3);
}
uint8_t *get_bitstream_buffer()
{
return m_pBitsBuffer;
}
bool is_byte_aligned()
{
if (m_bBufferOverflow) {
m_iBitsToGo = 32;
}
return !(m_iBitsToGo & 7);
}
int32_t get_num_bits_for_byte_align()
{
return (m_iBitsToGo & 7);
}
bool get_start_code_prevention_status()
{
return m_bPreventStartCode;
}
bool verify_buffer(uint32_t uiBytesToWrite);
public:
bool m_bBufferOverflow;
bool m_bAllowReallocate;
private:
void write_byte_start_code_prevention(uint8_t u8Val);
bool reallocate_buffer();
int32_t get_exp_golomb0_code_len(uint32_t uiVal);
const uint8_t m_iLog_2_N[256] = {
0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
};
private:
uint8_t *m_pBitsBuffer;
uint32_t m_uiBitsBufferSize;
uint32_t m_uiOffset;
bool m_bExternalBuffer;
uint32_t m_uintEncBuffer;
int32_t m_iBitsToGo;
bool m_bPreventStartCode;
};
#endif

View File

@@ -0,0 +1,37 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef D3D12_VIDEO_ENC_BITSTREAM_BUILDER_H
#define D3D12_VIDEO_ENC_BITSTREAM_BUILDER_H
#include "d3d12_video_types.h"
class d3d12_video_bitstream_builder_interface
{
public:
virtual ~d3d12_video_bitstream_builder_interface()
{ }
};
#endif

View File

@@ -0,0 +1,257 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "d3d12_video_encoder_bitstream_builder_h264.h"
inline H264_SPEC_PROFILES
Convert12ToSpecH264Profiles(D3D12_VIDEO_ENCODER_PROFILE_H264 profile12)
{
switch (profile12) {
case D3D12_VIDEO_ENCODER_PROFILE_H264_MAIN:
{
return H264_PROFILE_MAIN;
} break;
case D3D12_VIDEO_ENCODER_PROFILE_H264_HIGH:
{
return H264_PROFILE_HIGH;
} break;
case D3D12_VIDEO_ENCODER_PROFILE_H264_HIGH_10:
{
return H264_PROFILE_HIGH10;
} break;
default:
{
unreachable("Unsupported D3D12_VIDEO_ENCODER_PROFILE_H264");
} break;
}
}
void
d3d12_video_bitstream_builder_h264::build_sps(const D3D12_VIDEO_ENCODER_PROFILE_H264 & profile,
const D3D12_VIDEO_ENCODER_LEVELS_H264 & level,
const DXGI_FORMAT & inputFmt,
const D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264 & codecConfig,
const D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE_H264 &gopConfig,
uint32_t seq_parameter_set_id,
uint32_t max_num_ref_frames,
D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC sequenceTargetResolution,
D3D12_BOX frame_cropping_codec_config,
std::vector<uint8_t> & headerBitstream,
std::vector<uint8_t>::iterator placingPositionStart,
size_t & writtenBytes)
{
H264_SPEC_PROFILES profile_idc = Convert12ToSpecH264Profiles(profile);
uint32_t constraint_set3_flag = 0;
uint32_t level_idc = 0;
d3d12_video_encoder_convert_from_d3d12_level_h264(
level,
level_idc,
constraint_set3_flag /*Always 0 except if level is 11 or 1b in which case 0 means 11, 1 means 1b*/);
// constraint_set3_flag is for Main profile only and levels 11 or 1b: levels 11 if off, level 1b if on. Always 0 for
// HIGH/HIGH10 profiles
if ((profile == D3D12_VIDEO_ENCODER_PROFILE_H264_HIGH) || (profile == D3D12_VIDEO_ENCODER_PROFILE_H264_HIGH_10)) {
// Force 0 for high profiles
constraint_set3_flag = 0;
}
assert((inputFmt == DXGI_FORMAT_NV12) || (inputFmt == DXGI_FORMAT_P010));
// Assume NV12 YUV 420 8 bits
uint32_t bit_depth_luma_minus8 = 0;
uint32_t bit_depth_chroma_minus8 = 0;
// In case is 420 10 bits fix it
if (inputFmt == DXGI_FORMAT_P010) {
bit_depth_luma_minus8 = 2;
bit_depth_chroma_minus8 = 2;
}
// Calculate sequence resolution sizes in MBs
// Always in MBs since we don't support interlace in D3D12 Encode
uint32_t pic_width_in_mbs_minus1 = static_cast<uint32_t>(std::ceil(sequenceTargetResolution.Width / 16.0)) - 1;
uint32_t pic_height_in_map_units_minus1 =
static_cast<uint32_t>(std::ceil(sequenceTargetResolution.Height / 16.0)) - 1;
uint32_t frame_cropping_flag = 0;
if (frame_cropping_codec_config.left
|| frame_cropping_codec_config.right
|| frame_cropping_codec_config.top
|| frame_cropping_codec_config.bottom
) {
frame_cropping_flag = 1;
}
H264_SPS spsStructure = { static_cast<uint32_t>(profile_idc),
constraint_set3_flag,
level_idc,
seq_parameter_set_id,
bit_depth_luma_minus8,
bit_depth_chroma_minus8,
gopConfig.log2_max_frame_num_minus4,
gopConfig.pic_order_cnt_type,
gopConfig.log2_max_pic_order_cnt_lsb_minus4,
max_num_ref_frames,
0, // gaps_in_frame_num_value_allowed_flag
pic_width_in_mbs_minus1,
pic_height_in_map_units_minus1,
((codecConfig.ConfigurationFlags &
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264_FLAG_USE_ADAPTIVE_8x8_TRANSFORM) != 0) ?
1u :
0u, // direct_8x8_inference_flag
frame_cropping_flag,
frame_cropping_codec_config.left,
frame_cropping_codec_config.right,
frame_cropping_codec_config.top,
frame_cropping_codec_config.bottom };
// Print built PPS structure
debug_printf(
"[D3D12 d3d12_video_bitstream_builder_h264] H264_SPS Structure generated before writing to bitstream:\n");
print_sps(spsStructure);
// Convert the H264 SPS structure into bytes
m_h264Encoder.sps_to_nalu_bytes(&spsStructure, headerBitstream, placingPositionStart, writtenBytes);
}
void
d3d12_video_bitstream_builder_h264::write_end_of_stream_nalu(std::vector<uint8_t> & headerBitstream,
std::vector<uint8_t>::iterator placingPositionStart,
size_t & writtenBytes)
{
m_h264Encoder.write_end_of_stream_nalu(headerBitstream, placingPositionStart, writtenBytes);
}
void
d3d12_video_bitstream_builder_h264::write_end_of_sequence_nalu(std::vector<uint8_t> & headerBitstream,
std::vector<uint8_t>::iterator placingPositionStart,
size_t & writtenBytes)
{
m_h264Encoder.write_end_of_sequence_nalu(headerBitstream, placingPositionStart, writtenBytes);
}
void
d3d12_video_bitstream_builder_h264::build_pps(const D3D12_VIDEO_ENCODER_PROFILE_H264 & profile,
const D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264 & codecConfig,
const D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA_H264 &pictureControl,
uint32_t pic_parameter_set_id,
uint32_t seq_parameter_set_id,
std::vector<uint8_t> & headerBitstream,
std::vector<uint8_t>::iterator placingPositionStart,
size_t & writtenBytes)
{
BOOL bIsHighProfile =
((profile == D3D12_VIDEO_ENCODER_PROFILE_H264_HIGH) || (profile == D3D12_VIDEO_ENCODER_PROFILE_H264_HIGH_10));
H264_PPS ppsStructure = {
pic_parameter_set_id,
seq_parameter_set_id,
((codecConfig.ConfigurationFlags & D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264_FLAG_ENABLE_CABAC_ENCODING) != 0) ?
1u :
0u, // entropy_coding_mode_flag
0, // pic_order_present_flag (bottom_field_pic_order_in_frame_present_flag) - will use pic_cnt 0 or 2, always
// off ; used with pic_cnt_type 1 and deltas.
static_cast<uint32_t>(std::max(static_cast<int32_t>(pictureControl.List0ReferenceFramesCount) - 1,
0)), // num_ref_idx_l0_active_minus1
static_cast<uint32_t>(std::max(static_cast<int32_t>(pictureControl.List1ReferenceFramesCount) - 1,
0)), // num_ref_idx_l1_active_minus1
((codecConfig.ConfigurationFlags &
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264_FLAG_USE_CONSTRAINED_INTRAPREDICTION) != 0) ?
1u :
0u, // constrained_intra_pred_flag
((codecConfig.ConfigurationFlags &
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264_FLAG_USE_ADAPTIVE_8x8_TRANSFORM) != 0) ?
1u :
0u // transform_8x8_mode_flag
};
// Print built PPS structure
debug_printf(
"[D3D12 d3d12_video_bitstream_builder_h264] H264_PPS Structure generated before writing to bitstream:\n");
print_pps(ppsStructure);
// Convert the H264 SPS structure into bytes
m_h264Encoder.pps_to_nalu_bytes(&ppsStructure, headerBitstream, bIsHighProfile, placingPositionStart, writtenBytes);
}
void
d3d12_video_bitstream_builder_h264::print_pps(const H264_PPS &pps)
{
// Be careful that build_pps also wraps some other NALU bytes in pps_to_nalu_bytes so bitstream returned by build_pps
// won't be exactly the bytes from the H264_PPS struct
static_assert(sizeof(H264_PPS) ==
(sizeof(uint32_t) *
8), "Update the number of uint32_t in struct in assert and add case below if structure changes");
// Declared fields from definition in d3d12_video_encoder_bitstream_builder_h264.h
debug_printf("[D3D12 d3d12_video_bitstream_builder_h264] H264_PPS values below:\n");
debug_printf("pic_parameter_set_id: %d\n", pps.pic_parameter_set_id);
debug_printf("seq_parameter_set_id: %d\n", pps.seq_parameter_set_id);
debug_printf("entropy_coding_mode_flag: %d\n", pps.entropy_coding_mode_flag);
debug_printf("pic_order_present_flag: %d\n", pps.pic_order_present_flag);
debug_printf("num_ref_idx_l0_active_minus1: %d\n", pps.num_ref_idx_l0_active_minus1);
debug_printf("num_ref_idx_l1_active_minus1: %d\n", pps.num_ref_idx_l1_active_minus1);
debug_printf("constrained_intra_pred_flag: %d\n", pps.constrained_intra_pred_flag);
debug_printf("transform_8x8_mode_flag: %d\n", pps.transform_8x8_mode_flag);
debug_printf(
"[D3D12 d3d12_video_bitstream_builder_h264] H264_PPS values end\n--------------------------------------\n");
}
void
d3d12_video_bitstream_builder_h264::print_sps(const H264_SPS &sps)
{
// Be careful when calling this method that build_sps also wraps some other NALU bytes in sps_to_nalu_bytes so
// bitstream returned by build_sps won't be exactly the bytes from the H264_SPS struct From definition in
// d3d12_video_encoder_bitstream_builder_h264.h
static_assert(sizeof(H264_SPS) ==
(sizeof(uint32_t) *
19), "Update the number of uint32_t in struct in assert and add case below if structure changes");
// Declared fields from definition in d3d12_video_encoder_bitstream_builder_h264.h
debug_printf("[D3D12 d3d12_video_bitstream_builder_h264] H264_SPS values below:\n");
debug_printf("profile_idc: %d\n", sps.profile_idc);
debug_printf("constraint_set3_flag: %d\n", sps.constraint_set3_flag);
debug_printf("level_idc: %d\n", sps.level_idc);
debug_printf("seq_parameter_set_id: %d\n", sps.seq_parameter_set_id);
debug_printf("bit_depth_luma_minus8: %d\n", sps.bit_depth_luma_minus8);
debug_printf("bit_depth_chroma_minus8: %d\n", sps.bit_depth_chroma_minus8);
debug_printf("log2_max_frame_num_minus4: %d\n", sps.log2_max_frame_num_minus4);
debug_printf("pic_order_cnt_type: %d\n", sps.pic_order_cnt_type);
debug_printf("log2_max_pic_order_cnt_lsb_minus4: %d\n", sps.log2_max_pic_order_cnt_lsb_minus4);
debug_printf("max_num_ref_frames: %d\n", sps.max_num_ref_frames);
debug_printf("gaps_in_frame_num_value_allowed_flag: %d\n", sps.gaps_in_frame_num_value_allowed_flag);
debug_printf("pic_width_in_mbs_minus1: %d\n", sps.pic_width_in_mbs_minus1);
debug_printf("pic_height_in_map_units_minus1: %d\n", sps.pic_height_in_map_units_minus1);
debug_printf("direct_8x8_inference_flag: %d\n", sps.direct_8x8_inference_flag);
debug_printf("frame_cropping_flag: %d\n", sps.frame_cropping_flag);
debug_printf("frame_cropping_rect_left_offset: %d\n", sps.frame_cropping_rect_left_offset);
debug_printf("frame_cropping_rect_right_offset: %d\n", sps.frame_cropping_rect_right_offset);
debug_printf("frame_cropping_rect_top_offset: %d\n", sps.frame_cropping_rect_top_offset);
debug_printf("frame_cropping_rect_bottom_offset: %d\n", sps.frame_cropping_rect_bottom_offset);
debug_printf(
"[D3D12 d3d12_video_bitstream_builder_h264] H264_SPS values end\n--------------------------------------\n");
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef D3D12_VIDEO_ENC_BITSTREAM_BUILDER_H264_H
#define D3D12_VIDEO_ENC_BITSTREAM_BUILDER_H264_H
#include "d3d12_video_encoder_nalu_writer_h264.h"
#include "d3d12_video_encoder_bitstream_builder.h"
class d3d12_video_bitstream_builder_h264 : public d3d12_video_bitstream_builder_interface
{
public:
d3d12_video_bitstream_builder_h264() {};
~d3d12_video_bitstream_builder_h264() {};
void build_sps(const D3D12_VIDEO_ENCODER_PROFILE_H264 & profile,
const D3D12_VIDEO_ENCODER_LEVELS_H264 & level,
const DXGI_FORMAT & inputFmt,
const D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264 & codecConfig,
const D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE_H264 &gopConfig,
uint32_t seq_parameter_set_id,
uint32_t max_num_ref_frames,
D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC sequenceTargetResolution,
D3D12_BOX frame_cropping_codec_config,
std::vector<uint8_t> & headerBitstream,
std::vector<uint8_t>::iterator placingPositionStart,
size_t & writtenBytes);
void build_pps(const D3D12_VIDEO_ENCODER_PROFILE_H264 & profile,
const D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264 & codecConfig,
const D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA_H264 &pictureControl,
uint32_t pic_parameter_set_id,
uint32_t seq_parameter_set_id,
std::vector<uint8_t> & headerBitstream,
std::vector<uint8_t>::iterator placingPositionStart,
size_t & writtenBytes);
void write_end_of_stream_nalu(std::vector<uint8_t> & headerBitstream,
std::vector<uint8_t>::iterator placingPositionStart,
size_t & writtenBytes);
void write_end_of_sequence_nalu(std::vector<uint8_t> & headerBitstream,
std::vector<uint8_t>::iterator placingPositionStart,
size_t & writtenBytes);
void print_pps(const H264_PPS &pps);
void print_sps(const H264_SPS &sps);
uint32_t m_activeSPSIndex = 0;
uint32_t m_activePPSIndex = 0;
uint32_t get_active_sps_id()
{
return m_activeSPSIndex;
};
uint32_t get_active_pps_id()
{
return m_activePPSIndex;
};
void set_active_sps_id(uint32_t active_sps_id)
{
m_activeSPSIndex = active_sps_id;
debug_printf("[d3d12_video_bitstream_builder_h264] Setting new active SPS ID: %d ", m_activeSPSIndex);
};
void set_active_pps_id(uint32_t active_pps_id)
{
m_activePPSIndex = active_pps_id;
debug_printf("[d3d12_video_bitstream_builder_h264] Setting new active PPS ID: %d ", m_activePPSIndex);
};
private:
d3d12_video_nalu_writer_h264 m_h264Encoder;
};
#endif

View File

@@ -0,0 +1,400 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "d3d12_video_encoder_nalu_writer_h264.h"
#include <algorithm>
void
d3d12_video_nalu_writer_h264::rbsp_trailing(d3d12_video_encoder_bitstream *pBitstream)
{
pBitstream->put_bits(1, 1);
int32_t iLeft = pBitstream->get_num_bits_for_byte_align();
if (iLeft) {
pBitstream->put_bits(iLeft, 0);
}
bool isAligned = pBitstream->is_byte_aligned(); // causes side-effects in object state, don't put inside assert()
assert(isAligned);
}
uint32_t
d3d12_video_nalu_writer_h264::write_sps_bytes(d3d12_video_encoder_bitstream *pBitstream, H264_SPS *pSPS)
{
int32_t iBytesWritten = pBitstream->get_byte_count();
// Standard constraint to be between 0 and 31 inclusive
assert(pSPS->seq_parameter_set_id >= 0);
assert(pSPS->seq_parameter_set_id < 32);
pBitstream->put_bits(8, pSPS->profile_idc);
pBitstream->put_bits(1, 0); // constraint_set0_flag
pBitstream->put_bits(1, 0); // constraint_set1_flag
pBitstream->put_bits(1, 0); // constraint_set2_flag
pBitstream->put_bits(1, pSPS->constraint_set3_flag);
pBitstream->put_bits(1, 0); // constraint_set4_flag
pBitstream->put_bits(1, 0); // constraint_set5_flag
pBitstream->put_bits(2, 0);
pBitstream->put_bits(8, pSPS->level_idc);
pBitstream->exp_Golomb_ue(pSPS->seq_parameter_set_id);
// Only support profiles defined in D3D12 Video Encode
// If adding new profile support, check that the chroma_format_idc and bit depth are set correctly below
// for the new additions
assert((pSPS->profile_idc == H264_PROFILE_MAIN) || (pSPS->profile_idc == H264_PROFILE_HIGH) ||
(pSPS->profile_idc == H264_PROFILE_HIGH10));
if ((pSPS->profile_idc == H264_PROFILE_HIGH) || (pSPS->profile_idc == H264_PROFILE_HIGH10)) {
// chroma_format_idc always 4.2.0
pBitstream->exp_Golomb_ue(1);
// Assume no separate_colour_plane_flag given chroma_format_idc = 1
pBitstream->exp_Golomb_ue(pSPS->bit_depth_luma_minus8);
pBitstream->exp_Golomb_ue(pSPS->bit_depth_chroma_minus8);
// qpprime_y_zero_transform_bypass_flag
pBitstream->put_bits(1, 0);
// seq_scaling_matrix_present_flag)
pBitstream->put_bits(1, 0);
}
pBitstream->exp_Golomb_ue(pSPS->log2_max_frame_num_minus4);
pBitstream->exp_Golomb_ue(pSPS->pic_order_cnt_type);
if (pSPS->pic_order_cnt_type == 0) {
pBitstream->exp_Golomb_ue(pSPS->log2_max_pic_order_cnt_lsb_minus4);
}
pBitstream->exp_Golomb_ue(pSPS->max_num_ref_frames);
pBitstream->put_bits(1, pSPS->gaps_in_frame_num_value_allowed_flag);
pBitstream->exp_Golomb_ue(pSPS->pic_width_in_mbs_minus1);
pBitstream->exp_Golomb_ue(pSPS->pic_height_in_map_units_minus1);
// No support for interlace in D3D12 Video Encode
// frame_mbs_only_flag coded as 1
pBitstream->put_bits(1, 1); // frame_mbs_only_flag
pBitstream->put_bits(1, pSPS->direct_8x8_inference_flag);
// no cropping
pBitstream->put_bits(1, pSPS->frame_cropping_flag); // frame_cropping_flag
if (pSPS->frame_cropping_flag) {
pBitstream->exp_Golomb_ue(pSPS->frame_cropping_rect_left_offset);
pBitstream->exp_Golomb_ue(pSPS->frame_cropping_rect_right_offset);
pBitstream->exp_Golomb_ue(pSPS->frame_cropping_rect_top_offset);
pBitstream->exp_Golomb_ue(pSPS->frame_cropping_rect_bottom_offset);
}
// We're not including the VUI so this better be zero.
pBitstream->put_bits(1, 0); // vui_paramenters_present_flag
rbsp_trailing(pBitstream);
pBitstream->flush();
iBytesWritten = pBitstream->get_byte_count() - iBytesWritten;
return (uint32_t) iBytesWritten;
}
uint32_t
d3d12_video_nalu_writer_h264::write_pps_bytes(d3d12_video_encoder_bitstream *pBitstream,
H264_PPS * pPPS,
BOOL bIsHighProfile)
{
int32_t iBytesWritten = pBitstream->get_byte_count();
// Standard constraint to be between 0 and 31 inclusive
assert(pPPS->seq_parameter_set_id >= 0);
assert(pPPS->seq_parameter_set_id < 32);
// Standard constraint to be between 0 and 255 inclusive
assert(pPPS->pic_parameter_set_id >= 0);
assert(pPPS->pic_parameter_set_id < 256);
pBitstream->exp_Golomb_ue(pPPS->pic_parameter_set_id);
pBitstream->exp_Golomb_ue(pPPS->seq_parameter_set_id);
pBitstream->put_bits(1, pPPS->entropy_coding_mode_flag);
pBitstream->put_bits(1, pPPS->pic_order_present_flag); // bottom_field_pic_order_in_frame_present_flag
pBitstream->exp_Golomb_ue(0); // num_slice_groups_minus1
pBitstream->exp_Golomb_ue(pPPS->num_ref_idx_l0_active_minus1);
pBitstream->exp_Golomb_ue(pPPS->num_ref_idx_l1_active_minus1);
pBitstream->put_bits(1, 0); // weighted_pred_flag
pBitstream->put_bits(2, 0); // weighted_bipred_idc
pBitstream->exp_Golomb_se(0); // pic_init_qp_minus26
pBitstream->exp_Golomb_se(0); // pic_init_qs_minus26
pBitstream->exp_Golomb_se(0); // chroma_qp_index_offset
pBitstream->put_bits(1, 1); // deblocking_filter_control_present_flag
pBitstream->put_bits(1, pPPS->constrained_intra_pred_flag);
pBitstream->put_bits(1, 0); // redundant_pic_cnt_present_flag
if (bIsHighProfile) {
pBitstream->put_bits(1, pPPS->transform_8x8_mode_flag);
pBitstream->put_bits(1, 0); // pic_scaling_matrix_present_flag
pBitstream->exp_Golomb_se(0); // second_chroma_qp_index_offset
}
rbsp_trailing(pBitstream);
pBitstream->flush();
iBytesWritten = pBitstream->get_byte_count() - iBytesWritten;
return (uint32_t) iBytesWritten;
}
uint32_t
d3d12_video_nalu_writer_h264::wrap_sps_nalu(d3d12_video_encoder_bitstream *pNALU, d3d12_video_encoder_bitstream *pRBSP)
{
return wrap_rbsp_into_nalu(pNALU, pRBSP, NAL_REFIDC_REF, NAL_TYPE_SPS);
}
uint32_t
d3d12_video_nalu_writer_h264::wrap_pps_nalu(d3d12_video_encoder_bitstream *pNALU, d3d12_video_encoder_bitstream *pRBSP)
{
return wrap_rbsp_into_nalu(pNALU, pRBSP, NAL_REFIDC_REF, NAL_TYPE_PPS);
}
void
d3d12_video_nalu_writer_h264::write_nalu_end(d3d12_video_encoder_bitstream *pNALU)
{
pNALU->flush();
pNALU->set_start_code_prevention(FALSE);
int32_t iNALUnitLen = pNALU->get_byte_count();
if (FALSE == pNALU->m_bBufferOverflow && 0x00 == pNALU->get_bitstream_buffer()[iNALUnitLen - 1]) {
pNALU->put_bits(8, 0x03);
pNALU->flush();
}
}
uint32_t
d3d12_video_nalu_writer_h264::wrap_rbsp_into_nalu(d3d12_video_encoder_bitstream *pNALU,
d3d12_video_encoder_bitstream *pRBSP,
uint32_t iNaluIdc,
uint32_t iNaluType)
{
bool isAligned = pRBSP->is_byte_aligned(); // causes side-effects in object state, don't put inside assert()
assert(isAligned);
int32_t iBytesWritten = pNALU->get_byte_count();
pNALU->set_start_code_prevention(FALSE);
// NAL start code
pNALU->put_bits(24, 0);
pNALU->put_bits(8, 1);
// NAL header
pNALU->put_bits(1, 0);
pNALU->put_bits(2, iNaluIdc);
pNALU->put_bits(5, iNaluType);
pNALU->flush();
// NAL body
pRBSP->flush();
if (pRBSP->get_start_code_prevention_status()) {
// Direct copying.
pNALU->append_byte_stream(pRBSP);
} else {
// Copy with start code prevention.
pNALU->set_start_code_prevention(TRUE);
int32_t iLength = pRBSP->get_byte_count();
uint8_t *pBuffer = pRBSP->get_bitstream_buffer();
for (int32_t i = 0; i < iLength; i++) {
pNALU->put_bits(8, pBuffer[i]);
}
}
isAligned = pNALU->is_byte_aligned(); // causes side-effects in object state, don't put inside assert()
assert(isAligned);
write_nalu_end(pNALU);
pNALU->flush();
iBytesWritten = pNALU->get_byte_count() - iBytesWritten;
return (uint32_t) iBytesWritten;
}
void
d3d12_video_nalu_writer_h264::sps_to_nalu_bytes(H264_SPS * pSPS,
std::vector<uint8_t> & headerBitstream,
std::vector<uint8_t>::iterator placingPositionStart,
size_t & writtenBytes)
{
// Wrap SPS into NALU and copy full NALU into output byte array
d3d12_video_encoder_bitstream rbsp, nalu;
if (!rbsp.create_bitstream(MAX_COMPRESSED_SPS)) {
debug_printf("rbsp.create_bitstream(MAX_COMPRESSED_SPS) failed\n");
assert(false);
}
if (!nalu.create_bitstream(2 * MAX_COMPRESSED_SPS)) {
debug_printf("nalu.create_bitstream(2 * MAX_COMPRESSED_SPS) failed\n");
assert(false);
}
rbsp.set_start_code_prevention(TRUE);
if (write_sps_bytes(&rbsp, pSPS) <= 0u) {
debug_printf("write_sps_bytes(&rbsp, pSPS) didn't write any bytes.\n");
assert(false);
}
if (wrap_sps_nalu(&nalu, &rbsp) <= 0u) {
debug_printf("wrap_sps_nalu(&nalu, &rbsp) didn't write any bytes.\n");
assert(false);
}
// Deep copy nalu into headerBitstream, nalu gets out of scope here and its destructor frees the nalu object buffer
// memory.
uint8_t *naluBytes = nalu.get_bitstream_buffer();
size_t naluByteSize = nalu.get_byte_count();
auto startDstIndex = std::distance(headerBitstream.begin(), placingPositionStart);
if (headerBitstream.size() < (startDstIndex + naluByteSize)) {
headerBitstream.resize(startDstIndex + naluByteSize);
}
std::copy_n(&naluBytes[0], naluByteSize, &headerBitstream.data()[startDstIndex]);
writtenBytes = naluByteSize;
}
void
d3d12_video_nalu_writer_h264::pps_to_nalu_bytes(H264_PPS * pPPS,
std::vector<uint8_t> & headerBitstream,
BOOL bIsHighProfile,
std::vector<uint8_t>::iterator placingPositionStart,
size_t & writtenBytes)
{
// Wrap PPS into NALU and copy full NALU into output byte array
d3d12_video_encoder_bitstream rbsp, nalu;
if (!rbsp.create_bitstream(MAX_COMPRESSED_PPS)) {
debug_printf("rbsp.create_bitstream(MAX_COMPRESSED_PPS) failed\n");
assert(false);
}
if (!nalu.create_bitstream(2 * MAX_COMPRESSED_PPS)) {
debug_printf("nalu.create_bitstream(2 * MAX_COMPRESSED_PPS) failed\n");
assert(false);
}
rbsp.set_start_code_prevention(TRUE);
if (write_pps_bytes(&rbsp, pPPS, bIsHighProfile) <= 0u) {
debug_printf("write_pps_bytes(&rbsp, pPPS, bIsHighProfile) didn't write any bytes.\n");
assert(false);
}
if (wrap_pps_nalu(&nalu, &rbsp) <= 0u) {
debug_printf("wrap_pps_nalu(&nalu, &rbsp) didn't write any bytes.\n");
assert(false);
}
// Deep copy nalu into headerBitstream, nalu gets out of scope here and its destructor frees the nalu object buffer
// memory.
uint8_t *naluBytes = nalu.get_bitstream_buffer();
size_t naluByteSize = nalu.get_byte_count();
auto startDstIndex = std::distance(headerBitstream.begin(), placingPositionStart);
if (headerBitstream.size() < (startDstIndex + naluByteSize)) {
headerBitstream.resize(startDstIndex + naluByteSize);
}
std::copy_n(&naluBytes[0], naluByteSize, &headerBitstream.data()[startDstIndex]);
writtenBytes = naluByteSize;
}
void
d3d12_video_nalu_writer_h264::write_end_of_stream_nalu(std::vector<uint8_t> & headerBitstream,
std::vector<uint8_t>::iterator placingPositionStart,
size_t & writtenBytes)
{
d3d12_video_encoder_bitstream rbsp, nalu;
if (!rbsp.create_bitstream(8)) {
debug_printf("rbsp.create_bitstream(8) failed\n");
assert(false);
}
if (!nalu.create_bitstream(2 * MAX_COMPRESSED_PPS)) {
debug_printf("nalu.create_bitstream(2 * MAX_COMPRESSED_PPS) failed\n");
assert(false);
}
rbsp.set_start_code_prevention(TRUE);
if (wrap_rbsp_into_nalu(&nalu, &rbsp, NAL_REFIDC_REF, NAL_TYPE_END_OF_STREAM) <= 0u) {
debug_printf(
"wrap_rbsp_into_nalu(&nalu, &rbsp, NAL_REFIDC_REF, NAL_TYPE_END_OF_STREAM) didn't write any bytes.\n");;
assert(false);
}
// Deep copy nalu into headerBitstream, nalu gets out of scope here and its destructor frees the nalu object buffer
// memory.
uint8_t *naluBytes = nalu.get_bitstream_buffer();
size_t naluByteSize = nalu.get_byte_count();
auto startDstIndex = std::distance(headerBitstream.begin(), placingPositionStart);
if (headerBitstream.size() < (startDstIndex + naluByteSize)) {
headerBitstream.resize(startDstIndex + naluByteSize);
}
std::copy_n(&naluBytes[0], naluByteSize, &headerBitstream.data()[startDstIndex]);
writtenBytes = naluByteSize;
}
void
d3d12_video_nalu_writer_h264::write_end_of_sequence_nalu(std::vector<uint8_t> & headerBitstream,
std::vector<uint8_t>::iterator placingPositionStart,
size_t & writtenBytes)
{
d3d12_video_encoder_bitstream rbsp, nalu;
if (!rbsp.create_bitstream(8)) {
debug_printf("rbsp.create_bitstream(8) failed.\n");
assert(false);
}
if (!nalu.create_bitstream(2 * MAX_COMPRESSED_PPS)) {
debug_printf("nalu.create_bitstream(2 * MAX_COMPRESSED_PPS) failed.\n");
assert(false);
}
rbsp.set_start_code_prevention(TRUE);
if (wrap_rbsp_into_nalu(&nalu, &rbsp, NAL_REFIDC_REF, NAL_TYPE_END_OF_SEQUENCE) <= 0u) {
debug_printf(
"wrap_rbsp_into_nalu(&nalu, &rbsp, NAL_REFIDC_REF, NAL_TYPE_END_OF_SEQUENCE) didn't write any bytes.\n");
assert(false);
}
// Deep copy nalu into headerBitstream, nalu gets out of scope here and its destructor frees the nalu object buffer
// memory.
uint8_t *naluBytes = nalu.get_bitstream_buffer();
size_t naluByteSize = nalu.get_byte_count();
auto startDstIndex = std::distance(headerBitstream.begin(), placingPositionStart);
if (headerBitstream.size() < (startDstIndex + naluByteSize)) {
headerBitstream.resize(startDstIndex + naluByteSize);
}
std::copy_n(&naluBytes[0], naluByteSize, &headerBitstream.data()[startDstIndex]);
writtenBytes = naluByteSize;
}

View File

@@ -0,0 +1,151 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef D3D12_VIDEO_ENC_NALU_WRITER_H264_H
#define D3D12_VIDEO_ENC_NALU_WRITER_H264_H
#include "d3d12_video_encoder_bitstream.h"
enum H264_NALREF_IDC
{
NAL_REFIDC_REF = 3,
NAL_REFIDC_NONREF = 0
};
enum H264_NALU_TYPE
{
NAL_TYPE_UNSPECIFIED = 0,
NAL_TYPE_SLICE = 1,
NAL_TYPE_SLICEDATA_A = 2,
NAL_TYPE_SLICEDATA_B = 3,
NAL_TYPE_SLICEDATA_C = 4,
NAL_TYPE_IDR = 5,
NAL_TYPE_SEI = 6,
NAL_TYPE_SPS = 7,
NAL_TYPE_PPS = 8,
NAL_TYPE_ACCESS_UNIT_DEMILITER = 9,
NAL_TYPE_END_OF_SEQUENCE = 10,
NAL_TYPE_END_OF_STREAM = 11,
NAL_TYPE_FILLER_DATA = 12,
NAL_TYPE_SPS_EXTENSION = 13,
NAL_TYPE_PREFIX = 14,
/* 15...18 RESERVED */
NAL_TYPE_AUXILIARY_SLICE = 19,
/* 20...23 RESERVED */
/* 24...31 UNSPECIFIED */
};
struct H264_SPS
{
uint32_t profile_idc;
uint32_t constraint_set3_flag;
uint32_t level_idc;
uint32_t seq_parameter_set_id;
uint32_t bit_depth_luma_minus8;
uint32_t bit_depth_chroma_minus8;
uint32_t log2_max_frame_num_minus4;
uint32_t pic_order_cnt_type;
uint32_t log2_max_pic_order_cnt_lsb_minus4;
uint32_t max_num_ref_frames;
uint32_t gaps_in_frame_num_value_allowed_flag;
uint32_t pic_width_in_mbs_minus1;
uint32_t pic_height_in_map_units_minus1;
uint32_t direct_8x8_inference_flag;
uint32_t frame_cropping_flag;
uint32_t frame_cropping_rect_left_offset;
uint32_t frame_cropping_rect_right_offset;
uint32_t frame_cropping_rect_top_offset;
uint32_t frame_cropping_rect_bottom_offset;
};
struct H264_PPS
{
uint32_t pic_parameter_set_id;
uint32_t seq_parameter_set_id;
uint32_t entropy_coding_mode_flag;
uint32_t pic_order_present_flag;
uint32_t num_ref_idx_l0_active_minus1;
uint32_t num_ref_idx_l1_active_minus1;
uint32_t constrained_intra_pred_flag;
uint32_t transform_8x8_mode_flag;
};
enum H264_SPEC_PROFILES
{
H264_PROFILE_MAIN = 77,
H264_PROFILE_HIGH = 100,
H264_PROFILE_HIGH10 = 110,
};
#define MAX_COMPRESSED_PPS 256
#define MAX_COMPRESSED_SPS 256
class d3d12_video_nalu_writer_h264
{
public:
d3d12_video_nalu_writer_h264()
{ }
~d3d12_video_nalu_writer_h264()
{ }
// Writes the H264 SPS structure into a bitstream passed in headerBitstream
// Function resizes bitstream accordingly and puts result in byte vector
void sps_to_nalu_bytes(H264_SPS * pSPS,
std::vector<uint8_t> & headerBitstream,
std::vector<uint8_t>::iterator placingPositionStart,
size_t & writtenBytes);
// Writes the H264 PPS structure into a bitstream passed in headerBitstream
// Function resizes bitstream accordingly and puts result in byte vector
void pps_to_nalu_bytes(H264_PPS * pPPS,
std::vector<uint8_t> & headerBitstream,
BOOL bIsFREXTProfile,
std::vector<uint8_t>::iterator placingPositionStart,
size_t & writtenBytes);
void write_end_of_stream_nalu(std::vector<uint8_t> & headerBitstream,
std::vector<uint8_t>::iterator placingPositionStart,
size_t & writtenBytes);
void write_end_of_sequence_nalu(std::vector<uint8_t> & headerBitstream,
std::vector<uint8_t>::iterator placingPositionStart,
size_t & writtenBytes);
private:
// Writes from structure into bitstream with RBSP trailing but WITHOUT NAL unit wrap (eg. nal_idc_type, etc)
uint32_t write_sps_bytes(d3d12_video_encoder_bitstream *pBitstream, H264_SPS *pSPS);
uint32_t write_pps_bytes(d3d12_video_encoder_bitstream *pBitstream, H264_PPS *pPPS, BOOL bIsFREXTProfile);
// Adds NALU wrapping into structures and ending NALU control bits
uint32_t wrap_sps_nalu(d3d12_video_encoder_bitstream *pNALU, d3d12_video_encoder_bitstream *pRBSP);
uint32_t wrap_pps_nalu(d3d12_video_encoder_bitstream *pNALU, d3d12_video_encoder_bitstream *pRBSP);
// Helpers
void write_nalu_end(d3d12_video_encoder_bitstream *pNALU);
void rbsp_trailing(d3d12_video_encoder_bitstream *pBitstream);
uint32_t wrap_rbsp_into_nalu(d3d12_video_encoder_bitstream *pNALU,
d3d12_video_encoder_bitstream *pRBSP,
uint32_t iNaluIdc,
uint32_t iNaluType);
};
#endif

View File

@@ -0,0 +1,44 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef D3D12_VIDEO_ENCODE_REFERENCES_MANAGER_INTERFACE_H
#define D3D12_VIDEO_ENCODE_REFERENCES_MANAGER_INTERFACE_H
#include "d3d12_video_types.h"
class d3d12_video_encoder_references_manager_interface
{
public:
virtual void begin_frame(D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA, bool bUsedAsReference) = 0;
virtual void end_frame() = 0;
virtual D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE get_current_frame_recon_pic_output_allocation() = 0;
virtual void
get_current_frame_picture_control_data(D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA &codecAllocation) = 0;
virtual bool is_current_frame_used_as_reference() = 0;
virtual D3D12_VIDEO_ENCODE_REFERENCE_FRAMES get_current_reference_frames() = 0;
virtual ~d3d12_video_encoder_references_manager_interface()
{ }
};
#endif

View File

@@ -0,0 +1,422 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "d3d12_video_encoder_references_manager_h264.h"
#include <algorithm>
#include <string>
#include "d3d12_screen.h"
using namespace std;
d3d12_video_encoder_references_manager_h264::d3d12_video_encoder_references_manager_h264(
bool gopHasIorPFrames, d3d12_video_dpb_storage_manager_interface &rDpbStorageManager, uint32_t MaxDPBCapacity)
: m_MaxDPBCapacity(MaxDPBCapacity),
m_rDPBStorageManager(rDpbStorageManager),
m_CurrentFrameReferencesData({}),
m_gopHasInterFrames(gopHasIorPFrames)
{
assert((m_MaxDPBCapacity + 1 /*extra for cur frame output recon pic*/) ==
m_rDPBStorageManager.get_number_of_tracked_allocations());
debug_printf("[D3D12 Video Encoder Picture Manager H264] Completed construction of "
"d3d12_video_encoder_references_manager_h264 instance, settings are\n");
debug_printf("[D3D12 Video Encoder Picture Manager H264] m_MaxDPBCapacity: %d\n", m_MaxDPBCapacity);
}
void
d3d12_video_encoder_references_manager_h264::reset_gop_tracking_and_dpb()
{
// Reset m_CurrentFrameReferencesData tracking
m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.clear();
m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.reserve(m_MaxDPBCapacity);
m_CurrentFrameReferencesData.ReconstructedPicTexture = { nullptr, 0 };
// Reset DPB storage
uint32_t numPicsBeforeClearInDPB = m_rDPBStorageManager.get_number_of_pics_in_dpb();
uint32_t cFreedResources = m_rDPBStorageManager.clear_decode_picture_buffer();
assert(numPicsBeforeClearInDPB == cFreedResources);
// Initialize if needed the reconstructed picture allocation for the first IDR picture in the GOP
// This needs to be done after initializing the GOP tracking state above since it makes decisions based on the
// current picture type.
prepare_current_frame_recon_pic_allocation();
// After clearing the DPB, outstanding used allocations should be 1u only for the first allocation for the
// reconstructed picture of the initial IDR in the GOP
assert(m_rDPBStorageManager.get_number_of_in_use_allocations() == m_gopHasInterFrames ? 1u : 0u);
assert(m_rDPBStorageManager.get_number_of_tracked_allocations() <=
(m_MaxDPBCapacity + 1)); // pool is not extended beyond maximum expected usage
}
// Calculates the picture control structure for the current frame
void
d3d12_video_encoder_references_manager_h264::get_current_frame_picture_control_data(
D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA &codecAllocation)
{
// Update reference picture control structures (L0/L1 and DPB descriptors lists based on current frame and next frame
// in GOP) for next frame
debug_printf("[D3D12 Video Encoder Picture Manager H264] %d resources IN USE out of a total of %d ALLOCATED "
"resources at frame with POC: %d\n",
m_rDPBStorageManager.get_number_of_in_use_allocations(),
m_rDPBStorageManager.get_number_of_tracked_allocations(),
m_curFrameState.PictureOrderCountNumber);
// See casts below
assert(m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.size() < UINT32_MAX);
bool needsL0List = (m_curFrameState.FrameType == D3D12_VIDEO_ENCODER_FRAME_TYPE_H264_P_FRAME) ||
(m_curFrameState.FrameType == D3D12_VIDEO_ENCODER_FRAME_TYPE_H264_B_FRAME);
bool needsL1List = (m_curFrameState.FrameType == D3D12_VIDEO_ENCODER_FRAME_TYPE_H264_B_FRAME);
assert(codecAllocation.DataSize == sizeof(D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA_H264));
// See D3D12 Encode spec below
// pList0ReferenceFrames
// List of past frame reference frames to be used for this frame. Each integer value in this array indices into
// pReferenceFramesReconPictureDescriptors to reference pictures kept in the DPB.
// pList1ReferenceFrames
// List of future frame reference frames to be used for this frame. Each integer value in this array indices into
// pReferenceFramesReconPictureDescriptors to reference pictures kept in the DPB.
// Need to map from frame_num in the receiving ref_idx_l0_list/ref_idx_l1_list to the position with that
// FrameDecodingOrderNumber in the DPB descriptor
if (needsL0List && (m_curFrameState.List0ReferenceFramesCount > 0)) {
std::vector<uint32_t> tmpL0(m_curFrameState.List0ReferenceFramesCount, 0);
memcpy(tmpL0.data(),
m_curFrameState.pList0ReferenceFrames,
m_curFrameState.List0ReferenceFramesCount * sizeof(m_curFrameState.pList0ReferenceFrames[0]));
for (size_t l0Idx = 0; l0Idx < m_curFrameState.List0ReferenceFramesCount; l0Idx++) {
// tmpL0[l0Idx] has frame_num's (FrameDecodingOrderNumber)
// m_curFrameState.pList0ReferenceFrames[l0Idx] needs to have the index j of
// pReferenceFramesReconPictureDescriptors where
// pReferenceFramesReconPictureDescriptors[j].FrameDecodingOrderNumber == tmpL0[l0Idx]
auto value = tmpL0[l0Idx];
auto foundItemIt = std::find_if(m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.begin(),
m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.end(),
[&value](const D3D12_VIDEO_ENCODER_REFERENCE_PICTURE_DESCRIPTOR_H264 &p) {
return p.FrameDecodingOrderNumber == value;
});
assert(foundItemIt != m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.end());
m_curFrameState.pList0ReferenceFrames[l0Idx] =
std::distance(m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.begin(), foundItemIt);
}
}
if (needsL1List && (m_curFrameState.List1ReferenceFramesCount > 0)) {
std::vector<uint32_t> tmpL1(m_curFrameState.List1ReferenceFramesCount, 0);
memcpy(tmpL1.data(),
m_curFrameState.pList1ReferenceFrames,
m_curFrameState.List1ReferenceFramesCount * sizeof(m_curFrameState.pList1ReferenceFrames[0]));
for (size_t l1Idx = 0; l1Idx < m_curFrameState.List1ReferenceFramesCount; l1Idx++) {
// tmpL1[l1Idx] has frame_num's (FrameDecodingOrderNumber)
// m_curFrameState.pList1ReferenceFrames[l1Idx] needs to have the index j of
// pReferenceFramesReconPictureDescriptors where
// pReferenceFramesReconPictureDescriptors[j].FrameDecodingOrderNumber == tmpL1[l1Idx]
auto value = tmpL1[l1Idx];
auto foundItemIt = std::find_if(m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.begin(),
m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.end(),
[&value](const D3D12_VIDEO_ENCODER_REFERENCE_PICTURE_DESCRIPTOR_H264 &p) {
return p.FrameDecodingOrderNumber == value;
});
assert(foundItemIt != m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.end());
m_curFrameState.pList1ReferenceFrames[l1Idx] =
std::distance(m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.begin(), foundItemIt);
}
}
m_curFrameState.List0ReferenceFramesCount = needsL0List ? m_curFrameState.List0ReferenceFramesCount : 0;
m_curFrameState.pList0ReferenceFrames = needsL0List ? m_curFrameState.pList0ReferenceFrames : nullptr,
m_curFrameState.List1ReferenceFramesCount = needsL1List ? m_curFrameState.List1ReferenceFramesCount : 0,
m_curFrameState.pList1ReferenceFrames = needsL1List ? m_curFrameState.pList1ReferenceFrames : nullptr,
m_curFrameState.ReferenceFramesReconPictureDescriptorsCount =
needsL0List ? static_cast<uint32_t>(m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.size()) :
0,
m_curFrameState.pReferenceFramesReconPictureDescriptors =
needsL0List ? m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.data() : nullptr,
*codecAllocation.pH264PicData = m_curFrameState;
print_l0_l1_lists();
print_dpb();
}
// Returns the resource allocation for a reconstructed picture output for the current frame
D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE
d3d12_video_encoder_references_manager_h264::get_current_frame_recon_pic_output_allocation()
{
return m_CurrentFrameReferencesData.ReconstructedPicTexture;
}
D3D12_VIDEO_ENCODE_REFERENCE_FRAMES
d3d12_video_encoder_references_manager_h264::get_current_reference_frames()
{
D3D12_VIDEO_ENCODE_REFERENCE_FRAMES retVal = { 0,
// ppTexture2Ds
nullptr,
// pSubresources
nullptr };
// Return nullptr for fully intra frames (eg IDR)
// and return references information for inter frames (eg.P/B) and I frame that doesn't flush DPB
if ((m_curFrameState.FrameType != D3D12_VIDEO_ENCODER_FRAME_TYPE_H264_IDR_FRAME) &&
(m_curFrameState.FrameType != D3D12_VIDEO_ENCODER_FRAME_TYPE_H264_I_FRAME) && m_gopHasInterFrames) {
auto curRef = m_rDPBStorageManager.get_current_reference_frames();
retVal.NumTexture2Ds = curRef.NumTexture2Ds;
retVal.ppTexture2Ds = curRef.ppTexture2Ds;
retVal.pSubresources = curRef.pSubresources;
}
return retVal;
}
void
d3d12_video_encoder_references_manager_h264::prepare_current_frame_recon_pic_allocation()
{
m_CurrentFrameReferencesData.ReconstructedPicTexture = { nullptr, 0 };
// If all GOP are intra frames, no point in doing reference pic allocations
if (is_current_frame_used_as_reference() && m_gopHasInterFrames) {
auto reconPic = m_rDPBStorageManager.get_new_tracked_picture_allocation();
m_CurrentFrameReferencesData.ReconstructedPicTexture.pReconstructedPicture = reconPic.pReconstructedPicture;
m_CurrentFrameReferencesData.ReconstructedPicTexture.ReconstructedPictureSubresource =
reconPic.ReconstructedPictureSubresource;
}
}
void
d3d12_video_encoder_references_manager_h264::update_fifo_dpb_push_front_cur_recon_pic()
{
// Keep the order of the dpb storage and dpb descriptors in a circular buffer
// order such that the DPB array consists of a sequence of frames in DECREASING encoding order
// eg. last frame encoded at first, followed by one to last frames encoded, and at the end
// the most distant frame encoded (currentFrameEncodeOrderNumber - MaxDPBSize)
// If current pic was not used as reference, current reconstructed picture resource is empty,
// No need to to anything in that case.
// Otherwise extract the reconstructed picture result and add it to the DPB
// If GOP are all intra frames, do nothing also.
if (is_current_frame_used_as_reference() && m_gopHasInterFrames) {
debug_printf("[D3D12 Video Encoder Picture Manager H264] MaxDPBCapacity is %d - Number of pics in DPB is %d "
"when trying to put frame with POC %d at front of the DPB\n",
m_MaxDPBCapacity,
m_rDPBStorageManager.get_number_of_pics_in_dpb(),
m_curFrameState.PictureOrderCountNumber);
// Release least recently used in DPB if we filled the m_MaxDPBCapacity allowed
if (m_rDPBStorageManager.get_number_of_pics_in_dpb() == m_MaxDPBCapacity) {
bool untrackedRes = false;
m_rDPBStorageManager.remove_reference_frame(m_rDPBStorageManager.get_number_of_pics_in_dpb() - 1,
&untrackedRes); // Remove last entry
// Verify that resource was untracked since this class is using the pool completely for allocations
assert(untrackedRes);
m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.pop_back(); // Remove last entry
}
// Add new dpb to front of DPB
D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE recAlloc = get_current_frame_recon_pic_output_allocation();
d3d12_video_reconstructed_picture refFrameDesc = {};
refFrameDesc.pReconstructedPicture = recAlloc.pReconstructedPicture;
refFrameDesc.ReconstructedPictureSubresource = recAlloc.ReconstructedPictureSubresource;
refFrameDesc.pVideoHeap = nullptr; // D3D12 Video Encode does not need the D3D12VideoEncoderHeap struct for H264
// (used for no-key-frame resolution change in VC1, AV1, etc)
m_rDPBStorageManager.insert_reference_frame(refFrameDesc, 0);
// Prepare D3D12_VIDEO_ENCODER_REFERENCE_PICTURE_DESCRIPTOR_H264 for added DPB member
D3D12_VIDEO_ENCODER_REFERENCE_PICTURE_DESCRIPTOR_H264 newDPBDescriptor = {
// uint32_t ReconstructedPictureResourceIndex;
0, // the associated reconstructed picture is also being pushed_front in m_rDPBStorageManager
// BOOL IsLongTermReference;
false,
// uint32_t LongTermPictureIdx;
0,
// uint32_t PictureOrderCountNumber;
m_curFrameState.PictureOrderCountNumber,
// uint32_t FrameDecodingOrderNumber;
m_curFrameState.FrameDecodingOrderNumber,
// uint32_t TemporalLayerIndex;
0 // NO B-hierarchy in this impl of the picture manager
};
// Add DPB entry
m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.insert(
m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.begin(),
newDPBDescriptor);
// Update the indices for ReconstructedPictureResourceIndex in pReferenceFramesReconPictureDescriptors
// to be in identity mapping with m_rDPBStorageManager indices
// after pushing the elements to the right in the push_front operation
for (uint32_t dpbResIdx = 1;
dpbResIdx < m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.size();
dpbResIdx++) {
auto &dpbDesc = m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors[dpbResIdx];
dpbDesc.ReconstructedPictureResourceIndex = dpbResIdx;
}
}
// Number of allocations, disregarding if they are used or not, should not exceed this limit due to reuse policies on
// DPB items removal.
assert(m_rDPBStorageManager.get_number_of_tracked_allocations() <= (m_MaxDPBCapacity + 1));
}
void
d3d12_video_encoder_references_manager_h264::print_l0_l1_lists()
{
if ((D3D12_DEBUG_VERBOSE & d3d12_debug) &&
((m_curFrameState.FrameType == D3D12_VIDEO_ENCODER_FRAME_TYPE_H264_P_FRAME) ||
(m_curFrameState.FrameType == D3D12_VIDEO_ENCODER_FRAME_TYPE_H264_B_FRAME))) {
std::string list0ContentsString;
for (uint32_t idx = 0; idx < m_curFrameState.List0ReferenceFramesCount; idx++) {
uint32_t value = m_curFrameState.pList0ReferenceFrames[idx];
list0ContentsString += "{ DPBidx: ";
list0ContentsString += std::to_string(value);
list0ContentsString += " - POC: ";
list0ContentsString += std::to_string(
m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors[value].PictureOrderCountNumber);
list0ContentsString += " - FrameDecodingOrderNumber: ";
list0ContentsString += std::to_string(
m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors[value].FrameDecodingOrderNumber);
list0ContentsString += "}\n";
}
debug_printf(
"[D3D12 Video Encoder Picture Manager H264] L0 list for frame with POC %d - frame_num (%d) is: \n %s \n",
m_curFrameState.PictureOrderCountNumber,
m_curFrameState.FrameDecodingOrderNumber,
list0ContentsString.c_str());
std::string list1ContentsString;
for (uint32_t idx = 0; idx < m_curFrameState.List1ReferenceFramesCount; idx++) {
uint32_t value = m_curFrameState.pList1ReferenceFrames[idx];
list1ContentsString += "{ DPBidx: ";
list1ContentsString += std::to_string(value);
list1ContentsString += " - POC: ";
list1ContentsString += std::to_string(
m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors[value].PictureOrderCountNumber);
list1ContentsString += " - FrameDecodingOrderNumber: ";
list1ContentsString += std::to_string(
m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors[value].FrameDecodingOrderNumber);
list1ContentsString += "}\n";
}
debug_printf(
"[D3D12 Video Encoder Picture Manager H264] L1 list for frame with POC %d - frame_num (%d) is: \n %s \n",
m_curFrameState.PictureOrderCountNumber,
m_curFrameState.FrameDecodingOrderNumber,
list1ContentsString.c_str());
}
}
void
d3d12_video_encoder_references_manager_h264::print_dpb()
{
if (D3D12_DEBUG_VERBOSE & d3d12_debug) {
std::string dpbContents;
for (uint32_t dpbResIdx = 0;
dpbResIdx < m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors.size();
dpbResIdx++) {
auto &dpbDesc = m_CurrentFrameReferencesData.pReferenceFramesReconPictureDescriptors[dpbResIdx];
auto dpbEntry = m_rDPBStorageManager.get_reference_frame(dpbDesc.ReconstructedPictureResourceIndex);
dpbContents += "{ DPBidx: ";
dpbContents += std::to_string(dpbResIdx);
dpbContents += " - POC: ";
dpbContents += std::to_string(dpbDesc.PictureOrderCountNumber);
dpbContents += " - FrameDecodingOrderNumber: ";
dpbContents += std::to_string(dpbDesc.FrameDecodingOrderNumber);
dpbContents += " - DPBStorageIdx: ";
dpbContents += std::to_string(dpbDesc.ReconstructedPictureResourceIndex);
dpbContents += " - DPBStorageResourcePtr: ";
char strBuf[256];
memset(&strBuf, '\0', 256);
sprintf(strBuf, "%p", dpbEntry.pReconstructedPicture);
dpbContents += std::string(strBuf);
dpbContents += " - DPBStorageSubresource: ";
dpbContents += std::to_string(dpbEntry.ReconstructedPictureSubresource);
dpbContents += "}\n";
}
debug_printf("[D3D12 Video Encoder Picture Manager H264] DPB has %d frames - DPB references for frame with POC "
"%d (frame_num: %d) are: \n %s \n",
m_rDPBStorageManager.get_number_of_pics_in_dpb(),
m_curFrameState.PictureOrderCountNumber,
m_curFrameState.FrameDecodingOrderNumber,
dpbContents.c_str());
}
}
// Advances state to next frame in GOP; subsequent calls to GetCurrentFrame* point to the advanced frame status
void
d3d12_video_encoder_references_manager_h264::end_frame()
{
debug_printf("[D3D12 Video Encoder Picture Manager H264] %d resources IN USE out of a total of %d ALLOCATED "
"resources at end_frame for frame with POC: %d\n",
m_rDPBStorageManager.get_number_of_in_use_allocations(),
m_rDPBStorageManager.get_number_of_tracked_allocations(),
m_curFrameState.PictureOrderCountNumber);
// Adds last used (if not null) get_current_frame_recon_pic_output_allocation to DPB for next EncodeFrame if
// necessary updates pReferenceFramesReconPictureDescriptors and updates the dpb storage
update_fifo_dpb_push_front_cur_recon_pic();
}
bool
d3d12_video_encoder_references_manager_h264::is_current_frame_used_as_reference()
{
return m_isCurrentFrameUsedAsReference;
}
void
d3d12_video_encoder_references_manager_h264::begin_frame(D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA curFrameData,
bool bUsedAsReference)
{
m_curFrameState = *curFrameData.pH264PicData;
m_isCurrentFrameUsedAsReference = bUsedAsReference;
debug_printf("Marking frame_num %d (POC %d) as reference ? %d\n",
curFrameData.pH264PicData->FrameDecodingOrderNumber,
curFrameData.pH264PicData->PictureOrderCountNumber,
bUsedAsReference);
// Advance the GOP tracking state
bool isDPBFlushNeeded = (m_curFrameState.FrameType == D3D12_VIDEO_ENCODER_FRAME_TYPE_H264_IDR_FRAME);
if (isDPBFlushNeeded) {
reset_gop_tracking_and_dpb();
} else {
// Get new allocation from DPB storage for reconstructed picture
// This is only necessary for the frames that come after an IDR
// since in the initial state already has this initialized
// and re-initialized by reset_gop_tracking_and_dpb above
prepare_current_frame_recon_pic_allocation();
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef D3D12_VIDEO_ENCODE_FIFO_REFERENCES_MANAGER_H264_H
#define D3D12_VIDEO_ENCODE_FIFO_REFERENCES_MANAGER_H264_H
#include "d3d12_video_types.h"
#include "d3d12_video_encoder_references_manager.h"
#include "d3d12_video_dpb_storage_manager.h"
class d3d12_video_encoder_references_manager_h264 : public d3d12_video_encoder_references_manager_interface
{
public:
void end_frame();
void begin_frame(D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA curFrameData, bool bUsedAsReference);
D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE get_current_frame_recon_pic_output_allocation();
void get_current_frame_picture_control_data(D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA &codecAllocation);
bool is_current_frame_used_as_reference();
D3D12_VIDEO_ENCODE_REFERENCE_FRAMES get_current_reference_frames();
d3d12_video_encoder_references_manager_h264(bool gopHasInterCodedFrames,
d3d12_video_dpb_storage_manager_interface &rDpbStorageManager,
uint32_t MaxDPBCapacity);
~d3d12_video_encoder_references_manager_h264()
{ }
private:
// Class helpers
void prepare_current_frame_recon_pic_allocation();
void reset_gop_tracking_and_dpb();
void update_fifo_dpb_push_front_cur_recon_pic();
void print_dpb();
void print_l0_l1_lists();
// Class members
uint32_t m_MaxDPBCapacity = 0;
struct current_frame_references_data
{
std::vector<D3D12_VIDEO_ENCODER_REFERENCE_PICTURE_DESCRIPTOR_H264> pReferenceFramesReconPictureDescriptors;
D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE ReconstructedPicTexture;
};
d3d12_video_dpb_storage_manager_interface &m_rDPBStorageManager;
current_frame_references_data m_CurrentFrameReferencesData;
bool m_gopHasInterFrames = false;
bool m_isCurrentFrameUsedAsReference = false;
D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA_H264 m_curFrameState = {};
};
#endif

View File

@@ -51,6 +51,12 @@ files_libd3d12 = files(
'd3d12_video_dec_references_mgr.cpp',
'd3d12_video_dec_h264.cpp',
'd3d12_video_buffer.cpp',
'd3d12_video_enc.cpp',
'd3d12_video_enc_h264.cpp',
'd3d12_video_encoder_references_manager_h264.cpp',
'd3d12_video_encoder_nalu_writer_h264.cpp',
'd3d12_video_encoder_bitstream_builder_h264.cpp',
'd3d12_video_encoder_bitstream.cpp',
'd3d12_video_texture_array_dpb_manager.cpp',
'd3d12_video_array_of_textures_dpb_manager.cpp',
)