d3d12: AV1 Encode

Reviewed-by: Jesse Natalie <jenatali@microsoft.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/23811>
This commit is contained in:
Sil Vilerino 2022-12-20 13:29:47 -05:00 committed by Marge Bot
parent 314871d57b
commit 64da736286
15 changed files with 6023 additions and 304 deletions

File diff suppressed because it is too large Load diff

View file

@ -29,6 +29,10 @@
#include "d3d12_video_dpb_storage_manager.h"
#include "d3d12_video_encoder_bitstream_builder_h264.h"
#include "d3d12_video_encoder_bitstream_builder_hevc.h"
#if D3D12_PREVIEW_SDK_VERSION >= 711
#include "d3d12_video_encoder_bitstream_builder_av1.h"
#endif
#include <list>
///
/// Pipe video interface starts
@ -126,18 +130,33 @@ struct D3D12EncodeCapabilities
{
D3D12_VIDEO_ENCODER_PROFILE_H264 m_H264Profile;
D3D12_VIDEO_ENCODER_PROFILE_HEVC m_HEVCProfile;
#if D3D12_PREVIEW_SDK_VERSION >= 711
D3D12_VIDEO_ENCODER_AV1_PROFILE m_AV1Profile;
#endif
} m_encoderSuggestedProfileDesc = {};
union
{
D3D12_VIDEO_ENCODER_LEVELS_H264 m_H264LevelSetting;
D3D12_VIDEO_ENCODER_LEVEL_TIER_CONSTRAINTS_HEVC m_HEVCLevelSetting;
#if D3D12_PREVIEW_SDK_VERSION >= 711
D3D12_VIDEO_ENCODER_AV1_LEVEL_TIER_CONSTRAINTS m_AV1LevelSetting;
#endif
} m_encoderLevelSuggestedDesc = {};
union
struct
{
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_H264 m_H264CodecCaps;
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC m_HEVCCodecCaps;
union{
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_H264 m_H264CodecCaps;
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC m_HEVCCodecCaps;
#if D3D12_PREVIEW_SDK_VERSION >= 711
D3D12_VIDEO_ENCODER_AV1_CODEC_CONFIGURATION_SUPPORT m_AV1CodecCaps;
#endif
};
#if D3D12_PREVIEW_SDK_VERSION >= 711
D3D12_VIDEO_ENCODER_AV1_FRAME_SUBREGION_LAYOUT_CONFIG_SUPPORT m_AV1TileCaps;
D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAGS RequiredNotRequestedFeatureFlags;
#endif
} m_encoderCodecSpecificConfigCaps = {};
// The maximum number of slices that the output of the current frame to be encoded will contain
@ -147,6 +166,23 @@ struct D3D12EncodeCapabilities
};
struct D3D12EncodeRateControlState
{
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;
#if D3D12_PREVIEW_SDK_VERSION >= 711
D3D12_VIDEO_ENCODER_RATE_CONTROL_QVBR1 m_Configuration_QVBR1;
#endif
} m_Config;
};
struct D3D12EncodeConfiguration
{
d3d12_video_encoder_config_dirty_flags m_ConfigDirtyFlags = d3d12_video_encoder_config_dirty_flag_none;
@ -166,52 +202,62 @@ struct D3D12EncodeConfiguration
{
D3D12_VIDEO_ENCODER_PROFILE_H264 m_H264Profile;
D3D12_VIDEO_ENCODER_PROFILE_HEVC m_HEVCProfile;
#if D3D12_PREVIEW_SDK_VERSION >= 711
D3D12_VIDEO_ENCODER_AV1_PROFILE m_AV1Profile;
#endif
} m_encoderProfileDesc = {};
union
{
D3D12_VIDEO_ENCODER_LEVELS_H264 m_H264LevelSetting;
D3D12_VIDEO_ENCODER_LEVEL_TIER_CONSTRAINTS_HEVC m_HEVCLevelSetting;
#if D3D12_PREVIEW_SDK_VERSION >= 711
D3D12_VIDEO_ENCODER_AV1_LEVEL_TIER_CONSTRAINTS m_AV1LevelSetting;
#endif
} 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 = {};
struct D3D12EncodeRateControlState m_encoderRateControlDesc = {};
union
{
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264 m_H264Config;
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC m_HEVCConfig;
#if D3D12_PREVIEW_SDK_VERSION >= 711
D3D12_VIDEO_ENCODER_AV1_CODEC_CONFIGURATION m_AV1Config;
#endif
} m_encoderCodecSpecificConfigDesc = {};
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE m_encoderSliceConfigMode = {};
union
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;
#if D3D12_PREVIEW_SDK_VERSION >= 711
struct {
D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_TILES TilesPartition;
uint8_t TilesGroupsCount;
av1_tile_group_t TilesGroups[128];
} m_TilesConfig_AV1;
#endif
} m_encoderSliceConfigDesc = {};
union
{
D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE_H264 m_H264GroupOfPictures;
D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE_HEVC m_HEVCGroupOfPictures;
#if D3D12_PREVIEW_SDK_VERSION >= 711
D3D12_VIDEO_ENCODER_AV1_SEQUENCE_STRUCTURE m_AV1SequenceStructure;
#endif
} m_encoderGOPConfigDesc = {};
union
{
D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA_H264 m_H264PicData;
D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA_HEVC m_HEVCPicData;
#if D3D12_PREVIEW_SDK_VERSION >= 711
D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_CODEC_DATA m_AV1PicData;
#endif
} m_encoderPicParamsDesc = {};
D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE m_encoderMotionPrecisionLimit =
@ -220,6 +266,78 @@ struct D3D12EncodeConfiguration
D3D12_VIDEO_ENCODER_INTRA_REFRESH m_IntraRefresh = { D3D12_VIDEO_ENCODER_INTRA_REFRESH_MODE_NONE, 0 };
uint32_t m_IntraRefreshCurrentFrameIndex = 0;
struct D3D12AV1CodecSpecificState
{
std::list<UINT/*PictureIndex*/> pendingShowableFrames;
} m_encoderCodecSpecificStateDescAV1;
};
struct EncodedBitstreamResolvedMetadata
{
ComPtr<ID3D12Resource> spBuffer;
uint64_t bufferSize = 0;
ComPtr<ID3D12Resource> m_spMetadataOutputBuffer;
/*
* We need to store a snapshot of the encoder state
* below as when get_feedback processes this other
* async queued frames might have changed it
*/
/*
* byte size of pre encode uploaded bitstream headers
* We need it in metadata as will be read in get_feedback
* to calculate the final size while other async encode
* operations (with potentially different headers) are being
* encoded in the GPU
*/
uint64_t preEncodeGeneratedHeadersByteSize = 0;
/*
* Indicates if the encoded frame needs header generation after GPU execution
* If false, preEncodeGeneratedHeadersByteSize indicates the size of the generated
* headers (if any)
*
* If true, indicates the headers must be generated at get_feedback time.
*/
bool postEncodeHeadersNeeded = false;
/* Indicates if the current metadata has been read by get_feedback */
bool bRead = true;
/* associated encoded frame state snapshot*/
struct D3D12EncodeCapabilities m_associatedEncodeCapabilities = {};
struct D3D12EncodeConfiguration m_associatedEncodeConfig = {};
/*
* Associated frame compressed bitstream buffer
* If needed get_feedback will have to generate
* headers and re-pack the compressed bitstream
*/
pipe_resource* comp_bit_destination;
/*
* Staging bitstream for when headers must be
* packed in get_feedback, it contains the encoded
* stream from EncodeFrame.
*/
ComPtr<ID3D12Resource> spStagingBitstream;
/* codec specific associated configuration flags */
union {
struct {
bool enable_frame_obu;
bool obu_has_size_field;
bool temporal_delim_rendered;
} AV1HeadersInfo;
} m_CodecSpecificData;
/*
* Scratch CPU buffer memory to generate any extra headers
* in between the GPU spStagingBitstream contents
*/
std::vector<uint8_t> m_StagingBitstreamConstruction;
};
struct d3d12_video_encoder
@ -250,14 +368,6 @@ struct d3d12_video_encoder
std::shared_ptr<d3d12_video_dpb_storage_manager_interface> m_upDPBStorageManager;
std::unique_ptr<d3d12_video_bitstream_builder_interface> m_upBitstreamBuilder;
struct EncodedBitstreamResolvedMetadata
{
ComPtr<ID3D12Resource> spBuffer;
uint64_t bufferSize = 0;
uint64_t codecHeadersSize = 0;
ComPtr<ID3D12Resource> m_spMetadataOutputBuffer;
};
std::vector<uint8_t> m_BitstreamHeadersBuffer;
std::vector<uint8_t> m_StagingHeadersBuffer;
std::vector<EncodedBitstreamResolvedMetadata> m_spEncodedFrameMetadata;
@ -318,7 +428,7 @@ d3d12_video_encoder_update_picparams_tracking(struct d3d12_video_encoder *pD3D12
struct pipe_video_buffer * srcTexture,
struct pipe_picture_desc * picture);
void
d3d12_video_encoder_calculate_metadata_resolved_buffer_size(uint32_t maxSliceNumber, uint64_t &bufferSize);
d3d12_video_encoder_calculate_metadata_resolved_buffer_size(enum pipe_video_format codec, uint32_t maxSliceNumber, uint64_t &bufferSize);
uint32_t
d3d12_video_encoder_calculate_max_slices_count_in_output(
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE slicesMode,
@ -330,8 +440,10 @@ 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_build_pre_encode_codec_headers(struct d3d12_video_encoder *pD3D12Enc,
bool &postEncodeHeadersNeeded,
uint64_t &preEncodeGeneratedHeadersByteSize);
void
d3d12_video_encoder_extract_encode_metadata(
struct d3d12_video_encoder * pD3D12Dec,
@ -343,10 +455,26 @@ d3d12_video_encoder_extract_encode_metadata(
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);
uint64_t d3d12_video_encoder_pool_current_index(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_SUPPORT1 &capEncoderSupportData);
bool
d3d12_video_encoder_query_d3d12_driver_caps(struct d3d12_video_encoder *pD3D12Enc,
D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT1 &capEncoderSupportData);
bool
d3d12_video_encoder_check_subregion_mode_support(struct d3d12_video_encoder *pD3D12Enc,
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE requestedSlicesMode);
uint64_t
d3d12_video_encoder_pool_current_index(struct d3d12_video_encoder *pD3D12Enc);
unsigned
d3d12_video_encoder_build_post_encode_codec_bitstream(struct d3d12_video_encoder * pD3D12Enc,
uint64_t associated_fence_value,
EncodedBitstreamResolvedMetadata& associatedMetadata);
void
d3d12_video_encoder_store_current_picture_references(d3d12_video_encoder *pD3D12Enc,
uint64_t current_metadata_slot);
///
/// d3d12_video_encoder functions ends

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,77 @@
/*
* 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_AV1_H
#define D3D12_VIDEO_ENC_AV1_H
#include "d3d12_video_types.h"
const uint32_t UNUSED_VIRTUAL_DPB_SLOT_PHYSICAL_INDEX = 0xFF; // As per D3D12 spec
size_t
d3d12_video_encoder_calculate_metadata_resolved_buffer_size_av1(uint32_t maxSliceNumber);
bool
d3d12_video_encoder_update_current_encoder_config_state_av1(struct d3d12_video_encoder *pD3D12Enc,
struct pipe_video_buffer *srcTexture,
struct pipe_picture_desc *picture);
void
d3d12_video_encoder_update_current_frame_pic_params_info_av1(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);
unsigned
d3d12_video_encoder_build_post_encode_codec_bitstream_av1(struct d3d12_video_encoder *pD3D12Enc,
uint64_t associated_fence_value,
EncodedBitstreamResolvedMetadata &associatedMetadata);
D3D12_VIDEO_ENCODER_AV1_RESTORATION_TILESIZE
d3d12_video_encoder_looprestorationsize_uint_to_d3d12_av1(uint32_t pixel_size);
unsigned
d3d12_video_encoder_looprestorationsize_d3d12_to_uint_av1(D3D12_VIDEO_ENCODER_AV1_RESTORATION_TILESIZE d3d12_type);
void
upload_tile_group_obu(struct d3d12_video_encoder *pD3D12Enc,
size_t tile_group_obu_size,
size_t decode_tile_elements_size,
std::vector<uint8_t> &staging_bitstream_buffer,
size_t staging_bitstream_buffer_offset,
pipe_resource *src_driver_bitstream,
pipe_resource *comp_bit_destination,
size_t comp_bit_destination_offset,
const D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA *pFrameSubregionMetadata,
size_t TileSizeBytes, // Pass already +1'd from TileSizeBytesMinus1
const D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_TILES &TilesPartition,
const av1_tile_group_t &tileGroup,
size_t &written_bytes_to_staging_bitstream_buffer);
void
d3d12_video_encoder_store_current_picture_references_av1(d3d12_video_encoder *pD3D12Enc,
uint64_t current_metadata_slot);
#endif

View file

@ -101,14 +101,74 @@ d3d12_video_encoder_update_current_rate_control_h264(struct d3d12_video_encoder
case PIPE_H2645_ENC_RATE_CONTROL_METHOD_QUALITY_VARIABLE:
{
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Mode = D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_QVBR;
#if D3D12_PREVIEW_SDK_VERSION >= 711
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1.TargetAvgBitRate =
picture->rate_ctrl[0].target_bitrate;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1.PeakBitRate =
picture->rate_ctrl[0].peak_bitrate;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1.ConstantQualityTarget =
picture->rate_ctrl[0].vbr_quality_factor;
#else
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR.TargetAvgBitRate =
picture->rate_ctrl[0].target_bitrate;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR.PeakBitRate =
picture->rate_ctrl[0].peak_bitrate;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR.ConstantQualityTarget =
picture->rate_ctrl[0].vbr_quality_factor;
#endif
#if D3D12_PREVIEW_SDK_VERSION >= 711
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 = VBV Initial Capacity = Target Bitrate = %" PRIu64 " (bits)\n", pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1.TargetAvgBitRate);
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags |=
D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_VBV_SIZES;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags |=
D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_EXTENDED_QVBR1_SUPPORT;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1.VBVCapacity =
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1.TargetAvgBitRate;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1.InitialVBVFullness =
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1.TargetAvgBitRate;
} else if (picture->rate_ctrl[0].app_requested_hrd_buffer) {
debug_printf("[d3d12_video_encoder_h264] d3d12_video_encoder_update_current_rate_control_h264 HRD required by app,"
" setting VBV Size = %d (bits) - VBV Initial Capacity %d (bits)\n", picture->rate_ctrl[0].vbv_buffer_size, picture->rate_ctrl[0].vbv_buf_initial_size);
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags |=
D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_VBV_SIZES;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags |=
D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_EXTENDED_QVBR1_SUPPORT;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1.VBVCapacity =
picture->rate_ctrl[0].vbv_buffer_size;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1.InitialVBVFullness =
picture->rate_ctrl[0].vbv_buf_initial_size;
}
#endif
#if D3D12_PREVIEW_SDK_VERSION >= 711
if (picture->rate_ctrl[0].max_au_size > 0) {
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags |=
D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_MAX_FRAME_SIZE;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1.MaxFrameBitSize =
picture->rate_ctrl[0].max_au_size;
debug_printf(
"[d3d12_video_encoder_h264] d3d12_video_encoder_update_current_rate_control_h264 "
"Upper layer requested explicit MaxFrameBitSize: %" PRIu64 "\n",
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1.MaxFrameBitSize);
}
if (picture->rate_ctrl[0].app_requested_qp_range) {
debug_printf(
"[d3d12_video_encoder_h264] d3d12_video_encoder_update_current_rate_control_h264 "
"Upper layer requested explicit MinQP: %d MaxQP: %d\n",
picture->rate_ctrl[0].min_qp, picture->rate_ctrl[0].max_qp);
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags |=
D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_QP_RANGE;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1.MinQP =
picture->rate_ctrl[0].min_qp;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1.MaxQP =
picture->rate_ctrl[0].max_qp;
}
#else
if (picture->rate_ctrl[0].max_au_size > 0) {
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags |=
D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_MAX_FRAME_SIZE;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR.MaxFrameBitSize =
@ -132,7 +192,7 @@ d3d12_video_encoder_update_current_rate_control_h264(struct d3d12_video_encoder
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR.MaxQP =
picture->rate_ctrl[0].max_qp;
}
#endif
} break;
case PIPE_H2645_ENC_RATE_CONTROL_METHOD_CONSTANT_SKIP:
case PIPE_H2645_ENC_RATE_CONTROL_METHOD_CONSTANT:
@ -807,13 +867,13 @@ d3d12_video_encoder_update_current_encoder_config_state_h264(struct d3d12_video_
// 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 "
D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT1 capEncoderSupportData1 = {};
if (!d3d12_video_encoder_negotiate_requested_features_and_d3d12_driver_caps(pD3D12Enc, capEncoderSupportData1)) {
debug_printf("[d3d12_video_encoder_h264] After negotiating caps, D3D12_FEATURE_VIDEO_ENCODER_SUPPORT1 "
"arguments are not supported - "
"ValidationFlags: 0x%x - SupportFlags: 0x%x\n",
capEncoderSupportData.ValidationFlags,
capEncoderSupportData.SupportFlags);
capEncoderSupportData1.ValidationFlags,
capEncoderSupportData1.SupportFlags);
return false;
}
@ -894,6 +954,12 @@ d3d12_video_encoder_convert_codec_to_d3d12_enc_codec(enum pipe_video_profile pro
{
return D3D12_VIDEO_ENCODER_CODEC_HEVC;
} break;
#if D3D12_PREVIEW_SDK_VERSION >= 711
case PIPE_VIDEO_FORMAT_AV1:
{
return D3D12_VIDEO_ENCODER_CODEC_AV1;
} break;
#endif
case PIPE_VIDEO_FORMAT_MPEG12:
case PIPE_VIDEO_FORMAT_MPEG4:
case PIPE_VIDEO_FORMAT_VC1:

View file

@ -101,13 +101,72 @@ d3d12_video_encoder_update_current_rate_control_hevc(struct d3d12_video_encoder
case PIPE_H2645_ENC_RATE_CONTROL_METHOD_QUALITY_VARIABLE:
{
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Mode = D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_QVBR;
#if D3D12_PREVIEW_SDK_VERSION >= 711
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1.TargetAvgBitRate =
picture->rc.target_bitrate;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1.PeakBitRate =
picture->rc.peak_bitrate;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1.ConstantQualityTarget =
picture->rc.vbr_quality_factor;
#else
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR.TargetAvgBitRate =
picture->rc.target_bitrate;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR.PeakBitRate =
picture->rc.peak_bitrate;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR.ConstantQualityTarget =
picture->rc.vbr_quality_factor;
#endif
#if D3D12_PREVIEW_SDK_VERSION >= 711
if (D3D12_VIDEO_ENC_CBR_FORCE_VBV_EQUAL_BITRATE) {
debug_printf("[d3d12_video_encoder_hevc] d3d12_video_encoder_update_current_rate_control_hevc D3D12_VIDEO_ENC_CBR_FORCE_VBV_EQUAL_BITRATE environment variable is set, "
", forcing VBV Size = VBV Initial Capacity = Target Bitrate = %" PRIu64 " (bits)\n", pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1.TargetAvgBitRate);
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags |=
D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_VBV_SIZES;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags |=
D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_EXTENDED_QVBR1_SUPPORT;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1.VBVCapacity =
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1.TargetAvgBitRate;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1.InitialVBVFullness =
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1.TargetAvgBitRate;
} else if (picture->rc.app_requested_hrd_buffer) {
debug_printf("[d3d12_video_encoder_hevc] d3d12_video_encoder_update_current_rate_control_hevc HRD required by app,"
" setting VBV Size = %d (bits) - VBV Initial Capacity %d (bits)\n", picture->rc.vbv_buffer_size, picture->rc.vbv_buf_initial_size);
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags |=
D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_VBV_SIZES;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags |=
D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_EXTENDED_QVBR1_SUPPORT;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1.VBVCapacity =
picture->rc.vbv_buffer_size;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1.InitialVBVFullness =
picture->rc.vbv_buf_initial_size;
}
#endif
#if D3D12_PREVIEW_SDK_VERSION >= 711
if (picture->rc.max_au_size > 0) {
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags |=
D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_MAX_FRAME_SIZE;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1.MaxFrameBitSize =
picture->rc.max_au_size;
debug_printf(
"[d3d12_video_encoder_hevc] d3d12_video_encoder_update_current_rate_control_hevc "
"Upper layer requested explicit MaxFrameBitSize: %" PRIu64 "\n",
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1.MaxFrameBitSize);
}
if (picture->rc.app_requested_qp_range) {
debug_printf(
"[d3d12_video_encoder_hevc] d3d12_video_encoder_update_current_rate_control_hevc "
"Upper layer requested explicit MinQP: %d MaxQP: %d\n",
picture->rc.min_qp, picture->rc.max_qp);
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags |=
D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_QP_RANGE;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1.MinQP =
picture->rc.min_qp;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1.MaxQP =
picture->rc.max_qp;
}
#else
if (picture->rc.max_au_size > 0) {
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags |=
D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_MAX_FRAME_SIZE;
@ -132,7 +191,7 @@ d3d12_video_encoder_update_current_rate_control_hevc(struct d3d12_video_encoder
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR.MaxQP =
picture->rc.max_qp;
}
#endif
} break;
case PIPE_H2645_ENC_RATE_CONTROL_METHOD_CONSTANT_SKIP:
case PIPE_H2645_ENC_RATE_CONTROL_METHOD_CONSTANT:
@ -723,13 +782,13 @@ d3d12_video_encoder_update_current_encoder_config_state_hevc(struct d3d12_video_
// 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_hevc] After negotiating caps, D3D12_FEATURE_VIDEO_ENCODER_SUPPORT "
D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT1 capEncoderSupportData1 = {};
if (!d3d12_video_encoder_negotiate_requested_features_and_d3d12_driver_caps(pD3D12Enc, capEncoderSupportData1)) {
debug_printf("[d3d12_video_encoder_hevc] After negotiating caps, D3D12_FEATURE_VIDEO_ENCODER_SUPPORT1 "
"arguments are not supported - "
"ValidationFlags: 0x%x - SupportFlags: 0x%x\n",
capEncoderSupportData.ValidationFlags,
capEncoderSupportData.SupportFlags);
capEncoderSupportData1.ValidationFlags,
capEncoderSupportData1.SupportFlags);
return false;
}

View file

@ -26,22 +26,22 @@
d3d12_video_encoder_bitstream::d3d12_video_encoder_bitstream()
{
m_pBitsBuffer = nullptr;
m_uiBitsBufferSize = 0;
m_uiOffset = 0;
m_iBitsToGo = 32;
m_uintEncBuffer = 0;
m_bExternalBuffer = false;
m_bBufferOverflow = false;
m_pBitsBuffer = nullptr;
m_uiBitsBufferSize = 0;
m_uiOffset = 0;
m_iBitsToGo = 32;
m_uintEncBuffer = 0;
m_bExternalBuffer = false;
m_bBufferOverflow = false;
m_bPreventStartCode = false;
m_bAllowReallocate = false;
m_bAllowReallocate = false;
}
d3d12_video_encoder_bitstream::~d3d12_video_encoder_bitstream()
{
if (!m_bExternalBuffer) {
if (m_pBitsBuffer) {
delete[](m_pBitsBuffer);
delete[] (m_pBitsBuffer);
(m_pBitsBuffer) = NULL;
}
}
@ -91,13 +91,13 @@ d3d12_video_encoder_bitstream::exp_Golomb_se(int32_t iVal)
}
void
d3d12_video_encoder_bitstream::setup_bitstream(uint32_t uiInitBufferSize, uint8_t *pBuffer)
d3d12_video_encoder_bitstream::setup_bitstream(uint32_t uiInitBufferSize, uint8_t *pBuffer, size_t initial_byte_offset)
{
m_pBitsBuffer = pBuffer;
m_pBitsBuffer = pBuffer;
m_uiBitsBufferSize = uiInitBufferSize;
m_uiOffset = 0;
memset(m_pBitsBuffer, 0, m_uiBitsBufferSize);
m_bExternalBuffer = true;
m_uiOffset = initial_byte_offset;
memset(m_pBitsBuffer + initial_byte_offset, 0, m_uiBitsBufferSize - initial_byte_offset);
m_bExternalBuffer = true;
m_bAllowReallocate = false;
}
@ -113,7 +113,7 @@ d3d12_video_encoder_bitstream::create_bitstream(uint32_t uiInitBufferSize)
}
m_uiBitsBufferSize = uiInitBufferSize;
m_uiOffset = 0;
m_uiOffset = 0;
memset(m_pBitsBuffer, 0, m_uiBitsBufferSize);
m_bExternalBuffer = false;
@ -124,7 +124,7 @@ bool
d3d12_video_encoder_bitstream::reallocate_buffer()
{
uint32_t uiBufferSize = m_uiBitsBufferSize * 3 / 2;
uint8_t *pNewBuffer = (uint8_t *) new uint8_t[uiBufferSize];
uint8_t *pNewBuffer = (uint8_t *) new uint8_t[uiBufferSize];
if (nullptr == pNewBuffer) {
return false;
@ -132,10 +132,10 @@ d3d12_video_encoder_bitstream::reallocate_buffer()
memcpy(pNewBuffer, m_pBitsBuffer, m_uiOffset * sizeof(uint8_t));
if (m_pBitsBuffer) {
delete[](m_pBitsBuffer);
delete[] (m_pBitsBuffer);
(m_pBitsBuffer) = NULL;
}
m_pBitsBuffer = pNewBuffer;
m_pBitsBuffer = pNewBuffer;
m_uiBitsBufferSize = uiBufferSize;
return true;
}
@ -168,17 +168,17 @@ 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;
*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_pBitsBuffer = pBitsBuffer;
m_uiBitsBufferSize = uiBufferSize;
m_bExternalBuffer = true;
m_bBufferOverflow = false;
m_bExternalBuffer = true;
m_bBufferOverflow = false;
m_bAllowReallocate = false;
clear();
@ -187,7 +187,7 @@ d3d12_video_encoder_bitstream::attach(uint8_t *pBitsBuffer, uint32_t uiBufferSiz
void
d3d12_video_encoder_bitstream::write_byte_start_code_prevention(uint8_t u8Val)
{
int32_t iOffset = m_uiOffset;
int32_t iOffset = m_uiOffset;
uint8_t *pBuffer = m_pBitsBuffer + iOffset;
if (m_bPreventStartCode && iOffset > 1) {
@ -208,6 +208,7 @@ d3d12_video_encoder_bitstream::write_byte_start_code_prevention(uint8_t u8Val)
void
d3d12_video_encoder_bitstream::put_bits(int32_t uiBitsCount, uint32_t iBitsVal)
{
assert(uiBitsCount > 0);
assert(uiBitsCount <= 32);
if (uiBitsCount < m_iBitsToGo) {
@ -224,7 +225,7 @@ d3d12_video_encoder_bitstream::put_bits(int32_t uiBitsCount, uint32_t iBitsVal)
WRITE_BYTE(*temp);
m_uintEncBuffer = 0;
m_iBitsToGo = 32 - iLeftOverBits;
m_iBitsToGo = 32 - iLeftOverBits;
if (iLeftOverBits > 0) {
m_uintEncBuffer = (iBitsVal << (32 - iLeftOverBits));
@ -238,19 +239,19 @@ d3d12_video_encoder_bitstream::flush()
ASSERTED 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);
uint32_t temp = (uint32_t) (32 - m_iBitsToGo);
if (!verify_buffer(temp >> 3)) {
return;
}
while (temp > 0) {
WRITE_BYTE((uint8_t)(m_uintEncBuffer >> 24));
WRITE_BYTE((uint8_t) (m_uintEncBuffer >> 24));
m_uintEncBuffer <<= 8;
temp -= 8;
}
m_iBitsToGo = 32;
m_iBitsToGo = 32;
m_uintEncBuffer = 0;
}
@ -264,8 +265,8 @@ d3d12_video_encoder_bitstream::append_byte_stream(d3d12_video_encoder_bitstream
assert(isThisAligned);
assert(m_iBitsToGo == 32);
uint8_t *pDst = m_pBitsBuffer + m_uiOffset;
uint8_t *pSrc = pStream->get_bitstream_buffer();
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)) {
@ -275,3 +276,84 @@ d3d12_video_encoder_bitstream::append_byte_stream(d3d12_video_encoder_bitstream
memcpy(pDst, pSrc, uiLen);
m_uiOffset += uiLen;
}
void
d3d12_video_encoder_bitstream::put_aligning_bits()
{
int32_t iLeft = get_num_bits_for_byte_align();
if (iLeft)
put_bits(iLeft, 0); // trailing_zero_bit
ASSERTED bool isAligned = is_byte_aligned(); // causes side-effects in object state, don't put inside assert()
assert(isAligned);
}
void
d3d12_video_encoder_bitstream::put_trailing_bits()
{
// trailing_one_bit shall be equal to 1.
// When the syntax element trailing_one_bit is read, it is a requirement that nbBits is greater than zero.
put_bits(1, 1); // trailing_one_bit
int32_t nbBits = get_num_bits_for_byte_align();
while (nbBits > 0) {
put_bits(1, 0); // trailing_zero_bit
nbBits--;
}
ASSERTED bool isAligned = is_byte_aligned(); // causes side-effects in object state, don't put inside assert()
assert(isAligned);
}
void
d3d12_video_encoder_bitstream::put_su_bits(uint16_t uiBitsCount, int32_t iBitsVal)
{
put_bits(uiBitsCount, calculate_su_bits(uiBitsCount, iBitsVal));
}
void
d3d12_video_encoder_bitstream::put_ns_bits(uint16_t uiBitsCount, uint32_t iBitsVal)
{
if (uiBitsCount > 1) {
uint32_t width = 0;
uint32_t tmp = uiBitsCount;
while (tmp) {
tmp = (tmp >> 1);
width++;
}
uint32_t m = (1 << width) - uiBitsCount;
if (iBitsVal < m)
put_bits(width - 1, iBitsVal);
else
put_bits(width, iBitsVal + m);
}
}
uint16_t
d3d12_video_encoder_bitstream::calculate_su_bits(uint16_t uiBitsCount, int32_t iBitsVal)
{
int16_t mask_sign = 1 << (uiBitsCount - 1);
if (iBitsVal & mask_sign)
iBitsVal = iBitsVal - 2 * mask_sign;
return iBitsVal;
}
void
d3d12_video_encoder_bitstream::put_le_bytes(size_t uiBytesCount, uint32_t iBitsVal)
{
assert(uiBytesCount <= sizeof(iBitsVal));
for (size_t i = 0; i < uiBytesCount; i++) {
put_bits(8, static_cast<uint8_t>(iBitsVal & 0xFF));
iBitsVal >>= 8;
}
}
void
d3d12_video_encoder_bitstream::put_leb128_bytes(uint64_t iBitsVal)
{
do {
uint8_t cur_byte = (iBitsVal & 0x7F);
iBitsVal >>= 7;
if (iBitsVal != 0)
cur_byte |= 0x80;
put_bits(8, cur_byte);
} while (iBitsVal != 0);
}

View file

@ -36,17 +36,24 @@ class d3d12_video_encoder_bitstream
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 setup_bitstream(uint32_t uiInitBufferSize, uint8_t *pBuffer, size_t initial_byte_offset);
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);
void put_aligning_bits();
void put_trailing_bits();
void put_su_bits(uint16_t uiBitsCount, int32_t iBitsVal);
void put_ns_bits(uint16_t uiBitsCount, uint32_t iBitsVal);
uint16_t calculate_su_bits(uint16_t uiBitsCount, int32_t iBitsVal);
void put_le_bytes(size_t uiBytesCount, uint32_t iBitsVal);
void put_leb128_bytes(uint64_t iBitsVal);
inline void clear()
{
m_iBitsToGo = 32;
m_uiOffset = 0;
m_iBitsToGo = 32;
m_uiOffset = 0;
m_uintEncBuffer = 0;
};
@ -90,8 +97,8 @@ class d3d12_video_encoder_bitstream
bool m_bAllowReallocate;
private:
void write_byte_start_code_prevention(uint8_t u8Val);
bool reallocate_buffer();
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] = {
@ -109,9 +116,9 @@ class d3d12_video_encoder_bitstream
uint32_t m_uiBitsBufferSize;
uint32_t m_uiOffset;
bool m_bExternalBuffer;
bool m_bExternalBuffer;
uint32_t m_uintEncBuffer;
int32_t m_iBitsToGo;
int32_t m_iBitsToGo;
bool m_bPreventStartCode;
};

View file

@ -0,0 +1,923 @@
/*
* 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_av1.h"
void
d3d12_video_bitstream_builder_av1::write_obu_header(d3d12_video_encoder_bitstream *pBit,
av1_obutype_t obu_type,
uint32_t obu_extension_flag,
uint32_t temporal_id,
uint32_t spatial_id)
{
pBit->put_bits(1, 0); // obu_forbidden_bit
pBit->put_bits(4, obu_type); // type
pBit->put_bits(1, obu_extension_flag);
pBit->put_bits(1, 1); // obu_has_size_field
pBit->put_bits(1, 0); // reserved
if (obu_extension_flag) {
// obu_extension_header()
pBit->put_bits(3, temporal_id);
pBit->put_bits(2, spatial_id);
pBit->put_bits(3, 0); // extension_header_reserved_3bits
}
}
void
d3d12_video_bitstream_builder_av1::pack_obu_header_size(d3d12_video_encoder_bitstream *pBit, uint64_t val)
{
pBit->put_leb128_bytes(val);
}
void
d3d12_video_bitstream_builder_av1::write_seq_data(d3d12_video_encoder_bitstream *pBit, const av1_seq_header_t *pSeqHdr)
{
pBit->put_bits(3, pSeqHdr->seq_profile);
pBit->put_bits(1, 0); // still_picture default 0
pBit->put_bits(1, 0); // reduced_still_picture_header
pBit->put_bits(1, 0); // timing_info_present_flag
pBit->put_bits(1, 0); // initial_display_delay_present_flag
pBit->put_bits(5, pSeqHdr->operating_points_cnt_minus_1);
for (uint8_t i = 0; i <= pSeqHdr->operating_points_cnt_minus_1; i++) {
pBit->put_bits(8, pSeqHdr->operating_point_idc[i] >> 4);
pBit->put_bits(4, pSeqHdr->operating_point_idc[i] & 0x9f);
pBit->put_bits(5, pSeqHdr->seq_level_idx[i]);
if (pSeqHdr->seq_level_idx[i] > 7)
pBit->put_bits(1, pSeqHdr->seq_tier[i]);
}
pBit->put_bits(4, d3d12_video_bitstream_builder_av1::frame_width_bits_minus_1); // frame_width_bits_minus_1
pBit->put_bits(4, d3d12_video_bitstream_builder_av1::frame_height_bits_minus_1); // frame_height_bits_minus_1
pBit->put_bits(d3d12_video_bitstream_builder_av1::frame_width_bits_minus_1 + 1,
pSeqHdr->max_frame_width - 1); // max_frame_width_minus_1
pBit->put_bits(d3d12_video_bitstream_builder_av1::frame_height_bits_minus_1 + 1,
pSeqHdr->max_frame_height - 1); // max_frame_height_minus_1
pBit->put_bits(1, 0); // frame_id_numbers_present_flag
pBit->put_bits(1, pSeqHdr->use_128x128_superblock); // use_128x128_superblock
pBit->put_bits(1, pSeqHdr->enable_filter_intra); // enable_filter_intra
pBit->put_bits(1, pSeqHdr->enable_intra_edge_filter); // enable_intra_edge_filter
pBit->put_bits(1, pSeqHdr->enable_interintra_compound); // enable_interintra_compound
pBit->put_bits(1, pSeqHdr->enable_masked_compound); // enable_masked_compound
pBit->put_bits(1, pSeqHdr->enable_warped_motion); // enable_warped_motion
pBit->put_bits(1, pSeqHdr->enable_dual_filter); // enable_dual_filter
pBit->put_bits(1, pSeqHdr->enable_order_hint); // enable_order_hint
if (pSeqHdr->enable_order_hint) {
pBit->put_bits(1, pSeqHdr->enable_jnt_comp); // enable_jnt_comp
pBit->put_bits(1, pSeqHdr->enable_ref_frame_mvs); // enable_ref_frame_mvs
}
pBit->put_bits(1, pSeqHdr->seq_choose_screen_content_tools); // seq_choose_screen_content_tools
if (!pSeqHdr->seq_choose_screen_content_tools)
pBit->put_bits(1, pSeqHdr->seq_force_screen_content_tools); // seq_force_screen_content_tools
if (pSeqHdr->seq_force_screen_content_tools) {
pBit->put_bits(1, pSeqHdr->seq_choose_integer_mv); // seq_choose_integer_mv
if (!pSeqHdr->seq_choose_integer_mv)
pBit->put_bits(1, pSeqHdr->seq_force_integer_mv); // seq_force_integer_mv
}
if (pSeqHdr->enable_order_hint)
pBit->put_bits(3, pSeqHdr->order_hint_bits_minus1);
pBit->put_bits(1, pSeqHdr->enable_superres); // enable_superres
pBit->put_bits(1, pSeqHdr->enable_cdef); // enable_cdef
pBit->put_bits(1, pSeqHdr->enable_restoration); // enable_restoration
// color_config ()
pBit->put_bits(1,
pSeqHdr->color_config.bit_depth == DXGI_FORMAT_P010 ? 1 : 0); // Assume DXGI_FORMAT_NV12 otherwise
if (pSeqHdr->seq_profile != 1)
pBit->put_bits(1, 0); // mono_chrome not supported
pBit->put_bits(1, pSeqHdr->color_config.color_description_present_flag);
if (pSeqHdr->color_config.color_description_present_flag) {
pBit->put_bits(8, pSeqHdr->color_config.color_primaries);
pBit->put_bits(8, pSeqHdr->color_config.transfer_characteristics);
pBit->put_bits(8, pSeqHdr->color_config.matrix_coefficients);
}
pBit->put_bits(1, pSeqHdr->color_config.color_range); // color_range
if (pSeqHdr->seq_profile == 0)
pBit->put_bits(2, pSeqHdr->color_config.chroma_sample_position); // chroma_sample_position
pBit->put_bits(1, pSeqHdr->color_config.separate_uv_delta_q); // separate_uv_delta_q
pBit->put_bits(1, 0); // film_grain_params_present
pBit->put_trailing_bits();
}
void
d3d12_video_bitstream_builder_av1::write_temporal_delimiter_obu(std::vector<uint8_t> &headerBitstream,
std::vector<uint8_t>::iterator placingPositionStart,
size_t &writtenBytes)
{
auto startByteOffset = std::distance(headerBitstream.begin(), placingPositionStart);
if (headerBitstream.size() < (startByteOffset + c_DefaultBitstreamBufSize))
headerBitstream.resize(startByteOffset + c_DefaultBitstreamBufSize);
d3d12_video_encoder_bitstream bitstream_full_obu;
bitstream_full_obu.setup_bitstream(headerBitstream.size(), headerBitstream.data(), startByteOffset);
{
// temporal_delimiter_obu() has empty payload as per AV1 codec spec
// Write the header
constexpr uint32_t obu_extension_flag = 0;
constexpr uint32_t temporal_id = 0;
constexpr uint32_t spatial_id = 0;
write_obu_header(&bitstream_full_obu, OBU_TEMPORAL_DELIMITER, obu_extension_flag, temporal_id, spatial_id);
// Write the data size
const size_t obu_size_in_bytes = 0;
debug_printf("obu_size: %" PRIu64 " (temporal_delimiter_obu() has empty payload as per AV1 codec spec)\n",
obu_size_in_bytes);
pack_obu_header_size(&bitstream_full_obu, obu_size_in_bytes);
}
bitstream_full_obu.flush();
// Shrink headerBitstream to fit
writtenBytes = bitstream_full_obu.get_byte_count() - startByteOffset;
headerBitstream.resize(writtenBytes + startByteOffset);
}
void
d3d12_video_bitstream_builder_av1::write_sequence_header(const av1_seq_header_t *pSeqHdr,
std::vector<uint8_t> &headerBitstream,
std::vector<uint8_t>::iterator placingPositionStart,
size_t &writtenBytes)
{
auto startByteOffset = std::distance(headerBitstream.begin(), placingPositionStart);
if (headerBitstream.size() < (startByteOffset + c_DefaultBitstreamBufSize))
headerBitstream.resize(startByteOffset + c_DefaultBitstreamBufSize);
d3d12_video_encoder_bitstream bitstream_full_obu;
bitstream_full_obu.setup_bitstream(headerBitstream.size(), headerBitstream.data(), startByteOffset);
// to handle variable length we first write the content
// and later the obu header and concatenate both bitstreams
d3d12_video_encoder_bitstream bitstream_seq;
bitstream_seq.create_bitstream(c_DefaultBitstreamBufSize);
{
// Write the data
write_seq_data(&bitstream_seq, pSeqHdr);
bitstream_seq.flush();
debug_printf("sequence_header_obu() bytes: %" PRId32 "\n", bitstream_seq.get_byte_count());
// Write the header
constexpr uint32_t obu_extension_flag = 0;
constexpr uint32_t temporal_id = 0;
constexpr uint32_t spatial_id = 0;
write_obu_header(&bitstream_full_obu, OBU_SEQUENCE_HEADER, obu_extension_flag, temporal_id, spatial_id);
// Write the data size
const size_t obu_size_in_bytes = static_cast<size_t>(bitstream_seq.get_byte_count());
debug_printf("obu_size: %" PRIu64 "\n", obu_size_in_bytes);
pack_obu_header_size(&bitstream_full_obu, obu_size_in_bytes);
bitstream_full_obu.flush();
// bitstream_full_obu has external buffer allocation and
// append_bitstream deep copies bitstream_seq, so it's okay
// for RAII of bitstream_seq to be deallocated out of scope
bitstream_full_obu.append_byte_stream(&bitstream_seq);
}
bitstream_full_obu.flush();
// Shrink headerBitstream to fit
writtenBytes = bitstream_full_obu.get_byte_count() - startByteOffset;
headerBitstream.resize(writtenBytes + startByteOffset);
}
void
d3d12_video_bitstream_builder_av1::write_frame_size_with_refs(d3d12_video_encoder_bitstream *pBit,
const av1_seq_header_t *pSeqHdr,
const av1_pic_header_t *pPicHdr)
{
bool found_ref = false; // Send explicitly as default
for (int i = 0; i < 7 /*REFS_PER_FRAME*/; i++) {
pBit->put_bits(1, found_ref); // found_ref
}
if (found_ref) {
// frame_size()
write_frame_size(pBit, pSeqHdr, pPicHdr);
// render_size()
write_render_size(pBit, pPicHdr);
} else {
// superres_params()
write_superres_params(pBit, pSeqHdr, pPicHdr);
}
}
void
d3d12_video_bitstream_builder_av1::write_frame_size(d3d12_video_encoder_bitstream *pBit,
const av1_seq_header_t *pSeqHdr,
const av1_pic_header_t *pPicHdr)
{
if (pPicHdr->frame_size_override_flag) {
pBit->put_bits(d3d12_video_bitstream_builder_av1::frame_width_bits_minus_1 + 1,
pPicHdr->FrameWidth - 1); // frame_width_minus_1
pBit->put_bits(d3d12_video_bitstream_builder_av1::frame_height_bits_minus_1 + 1,
pPicHdr->FrameHeight - 1); // frame_height_minus_1
}
// superres_params()
write_superres_params(pBit, pSeqHdr, pPicHdr);
}
void
d3d12_video_bitstream_builder_av1::write_superres_params(d3d12_video_encoder_bitstream *pBit,
const av1_seq_header_t *pSeqHdr,
const av1_pic_header_t *pPicHdr)
{
if (pSeqHdr->enable_superres)
pBit->put_bits(1, pPicHdr->use_superres); // use_superres
constexpr unsigned SUPERRES_DENOM_BITS = 3; // As per AV1 codec spec
if (pPicHdr->use_superres) {
constexpr uint32_t SUPERRES_DENOM_MIN = 9; // As per AV1 codec spec
assert(pPicHdr->SuperresDenom >= SUPERRES_DENOM_MIN);
uint32_t coded_denom = pPicHdr->SuperresDenom - SUPERRES_DENOM_MIN;
pBit->put_bits(SUPERRES_DENOM_BITS, coded_denom);
}
}
void
d3d12_video_bitstream_builder_av1::write_render_size(d3d12_video_encoder_bitstream *pBit,
const av1_pic_header_t *pPicHdr)
{
uint8_t render_and_frame_size_different =
((pPicHdr->RenderWidth != pPicHdr->FrameWidth) || (pPicHdr->RenderHeight != pPicHdr->FrameHeight)) ? 1 : 0;
pBit->put_bits(1, render_and_frame_size_different); // render_and_frame_size_different
if (render_and_frame_size_different == 1) {
pBit->put_bits(16, pPicHdr->RenderWidth - 1); // render_width_minus_1
pBit->put_bits(16, pPicHdr->RenderHeight - 1); // render_height_minus_1
}
}
void
d3d12_video_bitstream_builder_av1::write_delta_q_value(d3d12_video_encoder_bitstream *pBit, int32_t delta_q_val)
{
if (delta_q_val) {
pBit->put_bits(1, 1);
pBit->put_su_bits(7, delta_q_val);
} else {
pBit->put_bits(1, 0);
}
}
inline int
get_relative_dist(int a, int b, int OrderHintBits, uint8_t enable_order_hint)
{
if (!enable_order_hint)
return 0;
int diff = a - b;
int m = 1 << (OrderHintBits - 1);
diff = (diff & (m - 1)) - (diff & m);
return diff;
}
void
d3d12_video_bitstream_builder_av1::write_pic_data(d3d12_video_encoder_bitstream *pBit,
const av1_seq_header_t *pSeqHdr,
const av1_pic_header_t *pPicHdr)
{
// uncompressed_header()
pBit->put_bits(1, pPicHdr->show_existing_frame);
if (pPicHdr->show_existing_frame) {
pBit->put_bits(3, pPicHdr->frame_to_show_map_idx); // frame_to_show_map_idx f(3)
// decoder_model_info_present_flag Default 0
// if ( decoder_model_info_present_flag && !equal_picture_interval ) {
// temporal_point_info( )
// }
// frame_id_numbers_present_flag default 0
// if ( frame_id_numbers_present_flag ) {
// display_frame_id f(idLen)
// }
} else {
const uint8_t FrameIsIntra = (pPicHdr->frame_type == D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_INTRA_ONLY_FRAME ||
pPicHdr->frame_type == D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_KEY_FRAME);
pBit->put_bits(2, pPicHdr->frame_type); // frame_type
pBit->put_bits(1, pPicHdr->show_frame); // show_frame
if (!pPicHdr->show_frame)
pBit->put_bits(1, pPicHdr->showable_frame); // showable_frame
if (pPicHdr->frame_type == D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_SWITCH_FRAME ||
(pPicHdr->frame_type == D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_KEY_FRAME && pPicHdr->show_frame)) {
assert(pPicHdr->error_resilient_mode == 1);
} else {
pBit->put_bits(1, pPicHdr->error_resilient_mode); // error_resilient_mode
}
pBit->put_bits(1, pPicHdr->disable_cdf_update); // disable_cdf_update
if (pSeqHdr->seq_force_screen_content_tools == /*SELECT_SCREEN_CONTENT_TOOLS */ 2)
pBit->put_bits(1, pPicHdr->allow_screen_content_tools); // allow_screen_content_tools
if (pPicHdr->allow_screen_content_tools && (pSeqHdr->seq_force_integer_mv == /*SELECT_INTEGER_MV */ 2))
pBit->put_bits(1, pPicHdr->force_integer_mv); // force_integer_mv
// reduced_still_picture_header default 0 and frame_type != SWITCH
if (pPicHdr->frame_type != D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_SWITCH_FRAME) {
// Expicitly coded if NOT SWITCH FRAME
pBit->put_bits(1, pPicHdr->frame_size_override_flag); // frame_size_override_flag
} else {
assert(pPicHdr->frame_size_override_flag ==
1); // As per AV1 spec for SWITCH FRAME it's not coded but defaulted to 1 instead
}
pBit->put_bits(pSeqHdr->order_hint_bits_minus1 + 1, pPicHdr->order_hint); // order_hint
if (!(FrameIsIntra || pPicHdr->error_resilient_mode))
pBit->put_bits(3, pPicHdr->primary_ref_frame); // primary_ref_frame
// decoder_model_info_present_flag Default 0
if (!(pPicHdr->frame_type == D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_SWITCH_FRAME ||
(pPicHdr->frame_type == D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_KEY_FRAME && pPicHdr->show_frame)))
pBit->put_bits(8 /* NUM_REF_FRAMES from AV1 spec */, pPicHdr->refresh_frame_flags);
constexpr uint32_t allFrames = (1 << 8 /* NUM_REF_FRAMES from AV1 spec */) - 1;
if (!FrameIsIntra || pPicHdr->refresh_frame_flags != allFrames) {
if (pPicHdr->error_resilient_mode && pSeqHdr->enable_order_hint) {
for (uint8_t i = 0; i < 8 /* NUM_REF_FRAMES from AV1 spec */; i++) {
pBit->put_bits(pSeqHdr->order_hint_bits_minus1 + 1,
pPicHdr->ref_order_hint[i]); // ref_order_hint[i] f(OrderHintBits)
}
}
}
if (FrameIsIntra) {
// frame_size()
write_frame_size(pBit, pSeqHdr, pPicHdr);
// render_size()
write_render_size(pBit, pPicHdr);
if (pPicHdr->allow_screen_content_tools && pPicHdr->UpscaledWidth == pPicHdr->FrameWidth)
pBit->put_bits(1, pPicHdr->allow_intrabc);
} else {
if (pSeqHdr->enable_order_hint)
pBit->put_bits(1, 0); // frame_refs_short_signaling default 0
for (uint8_t ref = 0; ref < ARRAY_SIZE(pPicHdr->ref_frame_idx); ref++)
pBit->put_bits(3 /* log2 of NUM_REF_FRAMES from AV1 spec */, pPicHdr->ref_frame_idx[ref]);
// frame_id_numbers_present_flag default 0
if (pPicHdr->frame_size_override_flag && !pPicHdr->error_resilient_mode) {
// frame_size_with_refs()
write_frame_size_with_refs(pBit, pSeqHdr, pPicHdr);
} else {
// frame_size()
write_frame_size(pBit, pSeqHdr, pPicHdr);
// render_size()
write_render_size(pBit, pPicHdr);
}
if (!pPicHdr->force_integer_mv)
pBit->put_bits(1, pPicHdr->allow_high_precision_mv); // allow_high_precision_mv
// read_interpolation_filter()
{
const uint8_t is_filter_switchable =
(pPicHdr->interpolation_filter == D3D12_VIDEO_ENCODER_AV1_INTERPOLATION_FILTERS_SWITCHABLE ? 1 : 0);
pBit->put_bits(1, is_filter_switchable); // is_filter_switchable
if (!is_filter_switchable) {
pBit->put_bits(2, pPicHdr->interpolation_filter); // interpolation_filter
}
}
pBit->put_bits(1, pPicHdr->is_motion_mode_switchable); // is_motion_mode_switchable
if (!(pPicHdr->error_resilient_mode || !pPicHdr->use_ref_frame_mvs))
pBit->put_bits(1, 1); // use_ref_frame_mvs
}
if (!pPicHdr->disable_cdf_update /* || reduced_still_picture_header default 0 */)
pBit->put_bits(1, pPicHdr->disable_frame_end_update_cdf); // disable_frame_end_update_cdf
// tile_info()
{
unsigned maxTileWidthSb = pPicHdr->tile_info.tile_support_caps.MaxTileWidth;
unsigned maxTileAreaSb = pPicHdr->tile_info.tile_support_caps.MaxTileArea;
unsigned minLog2TileCols = log2(pPicHdr->tile_info.tile_support_caps.MinTileCols);
unsigned maxLog2TileCols = log2(pPicHdr->tile_info.tile_support_caps.MaxTileCols);
unsigned log2TileCols = log2(pPicHdr->tile_info.tile_partition.ColCount);
unsigned minLog2TileRows = log2(pPicHdr->tile_info.tile_support_caps.MinTileRows);
unsigned maxLog2TileRows = log2(pPicHdr->tile_info.tile_support_caps.MaxTileRows);
unsigned log2TileRows = log2(pPicHdr->tile_info.tile_partition.RowCount);
pBit->put_bits(1, pPicHdr->tile_info.uniform_tile_spacing_flag); // uniform_tile_spacing_flag
if (pPicHdr->tile_info.uniform_tile_spacing_flag) {
for (unsigned i = minLog2TileCols; i < log2TileCols; i++)
pBit->put_bits(1, 1); // one increment_tile_cols_log2
if (log2TileCols < maxLog2TileCols)
pBit->put_bits(1, 0); // zero increment_tile_cols_log2
for (unsigned i = minLog2TileRows; i < log2TileRows; i++)
pBit->put_bits(1, 1); // increment_tile_rows_log2
if (log2TileRows < maxLog2TileRows)
pBit->put_bits(1, 0); // increment_tile_rows_log2
} else {
unsigned sizeSb = 0;
unsigned widestTileSb = 0;
unsigned widthSb = pPicHdr->frame_width_sb;
for (unsigned i = 0; i < pPicHdr->tile_info.tile_partition.ColCount; i++) {
sizeSb = pPicHdr->tile_info.tile_partition.ColWidths[i];
unsigned maxWidth = std::min(widthSb, maxTileWidthSb);
pBit->put_ns_bits(maxWidth, sizeSb - 1); // width_in_sbs_minus_1
widestTileSb = std::max(sizeSb, widestTileSb);
widthSb -= sizeSb;
}
unsigned maxTileHeightSb = std::max(maxTileAreaSb / widestTileSb, 1u);
unsigned heightSb = pPicHdr->frame_height_sb;
for (unsigned i = 0; i < pPicHdr->tile_info.tile_partition.RowCount; i++) {
sizeSb = pPicHdr->tile_info.tile_partition.RowHeights[i];
unsigned maxHeight = std::min(heightSb, maxTileHeightSb);
pBit->put_ns_bits(maxHeight, sizeSb - 1); // height_in_sbs_minus_1
heightSb -= sizeSb;
}
}
if (log2TileCols > 0 || log2TileRows > 0) {
pBit->put_bits(log2TileRows + log2TileCols,
pPicHdr->tile_info.tile_partition.ContextUpdateTileId); // f(TileRowsLog2 + TileColsLog2)
pBit->put_bits(2, pPicHdr->tile_info.tile_support_caps.TileSizeBytesMinus1); // tile_size_bytes_minus_1
// f(2)
}
}
// quantization_params()
{
pBit->put_bits(8, pPicHdr->quantization_params.BaseQIndex); // base_q_idx
write_delta_q_value(pBit, pPicHdr->quantization_params.YDCDeltaQ);
bool diff_uv_delta = false;
if (pPicHdr->quantization_params.UDCDeltaQ != pPicHdr->quantization_params.VDCDeltaQ ||
pPicHdr->quantization_params.UACDeltaQ != pPicHdr->quantization_params.VACDeltaQ)
diff_uv_delta = true;
if (diff_uv_delta)
assert(pSeqHdr->color_config.separate_uv_delta_q == 1);
if (pSeqHdr->color_config.separate_uv_delta_q)
pBit->put_bits(1, diff_uv_delta);
write_delta_q_value(pBit, pPicHdr->quantization_params.UDCDeltaQ);
write_delta_q_value(pBit, pPicHdr->quantization_params.UACDeltaQ);
if (diff_uv_delta) {
write_delta_q_value(pBit, pPicHdr->quantization_params.VDCDeltaQ);
write_delta_q_value(pBit, pPicHdr->quantization_params.VACDeltaQ);
}
pBit->put_bits(1, pPicHdr->quantization_params.UsingQMatrix); // using_qmatrix
if (pPicHdr->quantization_params.UsingQMatrix) {
pBit->put_bits(4, pPicHdr->quantization_params.QMY); // qm_y
pBit->put_bits(4, pPicHdr->quantization_params.QMU); // qm_u
if (pSeqHdr->color_config.separate_uv_delta_q)
pBit->put_bits(4, pPicHdr->quantization_params.QMV); // qm_v
}
}
// segmentation_params()
{
pBit->put_bits(1, pPicHdr->segmentation_enabled); // segmentation_enabled
if (pPicHdr->segmentation_enabled) {
if (pPicHdr->primary_ref_frame != 7 /*PRIMARY_REF_NONE*/) {
pBit->put_bits(1, pPicHdr->segmentation_config.UpdateMap); // segmentation_update_map f(1)
if (pPicHdr->segmentation_config.UpdateMap == 1)
pBit->put_bits(1, pPicHdr->segmentation_config.TemporalUpdate); // segmentation_temporal_update f(1)
pBit->put_bits(1, pPicHdr->segmentation_config.UpdateData); // segmentation_update_data f(1)
}
if (pPicHdr->segmentation_config.UpdateData == 1) {
const int av1_segmentation_feature_bits[8 /*SEG_LVL_MAX*/] = { 8, 6, 6, 6, 6, 3, 0, 0 };
const int av1_segmentation_feature_signed[8 /*SEG_LVL_MAX*/] = { 1, 1, 1, 1, 1, 0, 0, 0 };
for (int i = 0; i < 8 /*MAX_SEGMENTS*/; i++) {
for (int j = 0; j < 8 /*SEG_LVL_MAX*/; j++) {
bool feature_enabled =
((static_cast<UINT>(1 << j) & static_cast<UINT>(pPicHdr->segmentation_config.SegmentsData[i].EnabledFeatures)) != 0);
pBit->put_bits(1, feature_enabled ? 1 : 0); // feature_enabled f(1)
if (feature_enabled) {
int bitsToRead = av1_segmentation_feature_bits[j];
if (av1_segmentation_feature_signed[j] == 1) {
pBit->put_su_bits(
1 + bitsToRead,
pPicHdr->segmentation_config.SegmentsData[i].FeatureValue[j]); // su(1+bitsToRead)
} else {
pBit->put_bits(
bitsToRead,
pPicHdr->segmentation_config.SegmentsData[i].FeatureValue[j]); // f(bitsToRead)
}
}
}
}
}
}
}
// delta_q_params()
// combined with delta_lf_params()
{
if (pPicHdr->quantization_params.BaseQIndex)
pBit->put_bits(1, pPicHdr->delta_q_params.DeltaQPresent); // delta_q_present
if (pPicHdr->delta_q_params.DeltaQPresent) {
pBit->put_bits(2, pPicHdr->delta_q_params.DeltaQRes); // delta_q_res
// delta_lf_params()
if (!pPicHdr->allow_intrabc) {
pBit->put_bits(1, pPicHdr->delta_lf_params.DeltaLFPresent); // delta_lf_present
if (pPicHdr->delta_lf_params.DeltaLFPresent) {
pBit->put_bits(2, pPicHdr->delta_lf_params.DeltaLFRes); // delta_lf_res
pBit->put_bits(1, pPicHdr->delta_lf_params.DeltaLFMulti); // delta_lf_multi
}
}
}
}
constexpr bool CodedLossless = false; // CodedLossless default 0
constexpr bool AllLossless = false; // AllLossless default 0
// loop_filter_params()
{
if (!(CodedLossless || pPicHdr->allow_intrabc)) {
pBit->put_bits(6, pPicHdr->loop_filter_params.LoopFilterLevel[0]); // loop_filter_level[0]
pBit->put_bits(6, pPicHdr->loop_filter_params.LoopFilterLevel[1]); // loop_filter_level[1]
if (pPicHdr->loop_filter_params.LoopFilterLevel[0] || pPicHdr->loop_filter_params.LoopFilterLevel[1]) {
pBit->put_bits(6, pPicHdr->loop_filter_params.LoopFilterLevelU); // loop_filter_level[2]
pBit->put_bits(6, pPicHdr->loop_filter_params.LoopFilterLevelV); // loop_filter_level[3]
}
pBit->put_bits(3, pPicHdr->loop_filter_params.LoopFilterSharpnessLevel); // loop_filter_sharpness
pBit->put_bits(1, pPicHdr->loop_filter_params.LoopFilterDeltaEnabled); // loop_filter_delta_enabled
if (pPicHdr->loop_filter_params.LoopFilterDeltaEnabled) {
bool loop_filter_delta_update =
(pPicHdr->loop_filter_params.UpdateRefDelta || pPicHdr->loop_filter_params.UpdateModeDelta);
pBit->put_bits(1, loop_filter_delta_update); // loop_filter_delta_update
if (loop_filter_delta_update) {
constexpr uint8_t TOTAL_REFS_PER_FRAME = 8; // From AV1 spec
static_assert(ARRAY_SIZE(pPicHdr->loop_filter_params.RefDeltas) == TOTAL_REFS_PER_FRAME);
for (uint8_t i = 0; i < TOTAL_REFS_PER_FRAME; i++) {
pBit->put_bits(1, pPicHdr->loop_filter_params.UpdateRefDelta); // loop_filter_delta_update
if (pPicHdr->loop_filter_params.UpdateRefDelta) {
pBit->put_su_bits(7, pPicHdr->loop_filter_params.RefDeltas[i]); // loop_filter_ref_deltas[i]
}
}
static_assert(ARRAY_SIZE(pPicHdr->loop_filter_params.ModeDeltas) == 2); // From AV1 spec
for (uint8_t i = 0; i < 2; i++) {
pBit->put_bits(1, pPicHdr->loop_filter_params.UpdateModeDelta); // update_mode_delta
if (pPicHdr->loop_filter_params.UpdateModeDelta) {
pBit->put_su_bits(7, pPicHdr->loop_filter_params.ModeDeltas[i]); // loop_filter_mode_deltas[i]
}
}
}
}
}
}
// cdef_params()
{
if (!(!pSeqHdr->enable_cdef || CodedLossless || pPicHdr->allow_intrabc)) {
uint16_t num_planes = 3; // mono_chrome not supported
pBit->put_bits(2, pPicHdr->cdef_params.CdefDampingMinus3); // cdef_damping_minus_3
pBit->put_bits(2, pPicHdr->cdef_params.CdefBits); // cdef_bits
for (uint16_t i = 0; i < (1 << pPicHdr->cdef_params.CdefBits); ++i) {
pBit->put_bits(4, pPicHdr->cdef_params.CdefYPriStrength[i]); // cdef_y_pri_strength[i]
pBit->put_bits(2, pPicHdr->cdef_params.CdefYSecStrength[i]); // cdef_y_sec_strength[i]
if (num_planes > 1) {
pBit->put_bits(4, pPicHdr->cdef_params.CdefUVPriStrength[i]); // cdef_uv_pri_strength[i]
pBit->put_bits(2, pPicHdr->cdef_params.CdefUVSecStrength[i]); // cdef_uv_sec_strength[i]
}
}
}
}
// lr_params()
{
if (!(AllLossless || pPicHdr->allow_intrabc || !pSeqHdr->enable_restoration)) {
bool uses_lr = false;
bool uses_chroma_lr = false;
for (int i = 0; i < 3 /*MaxNumPlanes*/; i++) {
pBit->put_bits(2, pPicHdr->lr_params.lr_type[i]);
if (pPicHdr->lr_params.lr_type[i] != D3D12_VIDEO_ENCODER_AV1_RESTORATION_TYPE_DISABLED) {
uses_lr = true;
if (i > 0)
uses_chroma_lr = true;
}
}
if (uses_lr) {
pBit->put_bits(1, pPicHdr->lr_params.lr_unit_shift);
if (!pSeqHdr->use_128x128_superblock && pPicHdr->lr_params.lr_unit_shift) {
pBit->put_bits(1, pPicHdr->lr_params.lr_unit_extra_shift);
}
if (pSeqHdr->color_config.subsampling_x && pSeqHdr->color_config.subsampling_y && uses_chroma_lr) {
pBit->put_bits(1, pPicHdr->lr_params.lr_uv_shift);
}
}
}
}
// read_tx_mode()
{
const uint8_t tx_mode_select = (pPicHdr->TxMode == D3D12_VIDEO_ENCODER_AV1_TX_MODE_SELECT) ? 1 : 0;
if (!CodedLossless)
pBit->put_bits(1, tx_mode_select); // tx_mode_select
}
// frame_reference_mode()
{
if (!FrameIsIntra)
pBit->put_bits(1, pPicHdr->reference_select); // reference_select
}
// skip_mode_params()
{
uint8_t skipModeAllowed = 0;
if (!(FrameIsIntra || !pPicHdr->reference_select || !pSeqHdr->enable_order_hint)) {
int forwardIdx = -1;
int backwardIdx = -1;
int forwardHint = 0;
int backwardHint = 0;
for (int i = 0; i < 7 /*REFS_PER_FRAME*/; i++) {
uint32_t refHint = pPicHdr->ref_order_hint[pPicHdr->ref_frame_idx[i]];
if (get_relative_dist(refHint,
pPicHdr->order_hint,
pSeqHdr->order_hint_bits_minus1 + 1,
pSeqHdr->enable_order_hint) < 0) {
if (forwardIdx < 0 || get_relative_dist(refHint,
forwardHint,
pSeqHdr->order_hint_bits_minus1 + 1,
pSeqHdr->enable_order_hint) > 0) {
forwardIdx = i;
forwardHint = refHint;
}
} else if (get_relative_dist(refHint,
pPicHdr->order_hint,
pSeqHdr->order_hint_bits_minus1 + 1,
pSeqHdr->enable_order_hint) > 0) {
if (backwardIdx < 0 || get_relative_dist(refHint,
backwardHint,
pSeqHdr->order_hint_bits_minus1 + 1,
pSeqHdr->enable_order_hint) < 0) {
backwardIdx = i;
backwardHint = refHint;
}
}
}
if (forwardIdx < 0) {
skipModeAllowed = 0;
} else if (backwardIdx >= 0) {
skipModeAllowed = 1;
} else {
int secondForwardIdx = -1;
int secondForwardHint = 0;
for (int i = 0; i < 7 /*REFS_PER_FRAME*/; i++) {
uint32_t refHint = pPicHdr->ref_order_hint[pPicHdr->ref_frame_idx[i]];
if (get_relative_dist(refHint,
forwardHint,
pSeqHdr->order_hint_bits_minus1 + 1,
pSeqHdr->enable_order_hint) < 0) {
if (secondForwardIdx < 0 || get_relative_dist(refHint,
secondForwardHint,
pSeqHdr->order_hint_bits_minus1 + 1,
pSeqHdr->enable_order_hint) > 0) {
secondForwardIdx = i;
secondForwardHint = refHint;
}
}
}
if (secondForwardIdx < 0) {
skipModeAllowed = 0;
} else {
skipModeAllowed = 1;
}
}
}
if (skipModeAllowed)
pBit->put_bits(1, pPicHdr->skip_mode_present); // skip_mode_present
if (!(FrameIsIntra || pPicHdr->error_resilient_mode || !pSeqHdr->enable_warped_motion)) {
pBit->put_bits(1, pPicHdr->allow_warped_motion); // allow_warped_motion
}
}
pBit->put_bits(1, pPicHdr->reduced_tx_set); // reduced_tx_set
// global_motion_params()
{
if (!FrameIsIntra) {
for (uint8_t i = 0; i < 7; i++) {
pBit->put_bits(1, 0); // is_global[7]
// Unimplemented: Enable global_motion_params with ref_global_motion_info
assert(pPicHdr->ref_global_motion_info[i].TransformationType ==
D3D12_VIDEO_ENCODER_AV1_REFERENCE_WARPED_MOTION_TRANSFORMATION_IDENTITY);
}
}
}
// film_grain_params()
// constexpr uint8_t film_grain_params_present = 0; // film_grain_params_present default 0
// {
// if (!(!film_grain_params_present || (!pPicHdr->show_frame && !pPicHdr->showable_frame))
// ... this will be unreachable as film_grain_params_present is zero.
// }
}
}
void
d3d12_video_bitstream_builder_av1::write_frame_header(const av1_seq_header_t *pSeqHdr,
const av1_pic_header_t *pPicHdr,
av1_obutype_t frame_pack_type,
size_t extra_obu_size_bytes,
std::vector<uint8_t> &headerBitstream,
std::vector<uint8_t>::iterator placingPositionStart,
size_t &writtenBytes)
{
assert((frame_pack_type == OBU_FRAME) || (frame_pack_type == OBU_FRAME_HEADER));
auto startByteOffset = std::distance(headerBitstream.begin(), placingPositionStart);
if (headerBitstream.size() < (startByteOffset + c_DefaultBitstreamBufSize))
headerBitstream.resize(startByteOffset + c_DefaultBitstreamBufSize);
d3d12_video_encoder_bitstream bitstream_full_obu;
bitstream_full_obu.setup_bitstream(headerBitstream.size(), headerBitstream.data(), startByteOffset);
// to handle variable length we first write the content
// and later the obu header and concatenate both bitstreams
d3d12_video_encoder_bitstream bitstream_pic;
bitstream_pic.create_bitstream(c_DefaultBitstreamBufSize);
{
// Write frame_header_obu()
write_pic_data(&bitstream_pic, pSeqHdr, pPicHdr);
debug_printf("frame_header_obu() bytes (without OBU_FRAME nor OBU_FRAME_HEADER alignment padding): %" PRId32 "\n",
bitstream_pic.get_byte_count()); // May be bit unaligned at this point (see padding below)
debug_printf("extra_obu_size_bytes (ie. tile_group_obu_size if writing OBU_FRAME ): %" PRIu64 "\n",
extra_obu_size_bytes);
// Write the obu_header
constexpr uint32_t obu_extension_flag = 0;
constexpr uint32_t temporal_id = 0;
constexpr uint32_t spatial_id = 0;
write_obu_header(&bitstream_full_obu, frame_pack_type, obu_extension_flag, temporal_id, spatial_id);
if (frame_pack_type == OBU_FRAME) {
// Required byte_alignment() in frame_obu() after frame_header_obu()
bitstream_pic.put_aligning_bits();
debug_printf("Adding byte_alignment() after frame_header_obu() for OBU_FRAME\n");
} else if (frame_pack_type == OBU_FRAME_HEADER) {
// whole open_bitstream_unit() for OBU_FRAME_HEADER
// required in open_bitstream_unit () for OBU_FRAME_HEADER
bitstream_pic.put_trailing_bits();
debug_printf("Adding trailing_bits() after frame_header_obu() for OBU_FRAME\n");
assert(extra_obu_size_bytes == 0);
}
bitstream_pic.flush();
// Write the obu_size element
const size_t obu_size_in_bytes = bitstream_pic.get_byte_count() + extra_obu_size_bytes;
debug_printf("obu_size: %" PRIu64 "\n", obu_size_in_bytes);
pack_obu_header_size(&bitstream_full_obu, obu_size_in_bytes);
bitstream_full_obu.flush();
// bitstream_full_obu has external buffer allocation and
// append_bitstream deep copies bitstream_pic, so it's okay
// for RAII of bitstream_pic to be deallocated out of scope
bitstream_full_obu.append_byte_stream(&bitstream_pic);
}
bitstream_full_obu.flush();
// Shrink headerBitstream to fit
writtenBytes = bitstream_full_obu.get_byte_count() - startByteOffset;
headerBitstream.resize(writtenBytes + startByteOffset);
}
void
d3d12_video_bitstream_builder_av1::calculate_tile_group_obu_size(
const D3D12_VIDEO_ENCODER_OUTPUT_METADATA *pParsedMetadata,
const D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA *pFrameSubregionMetadata,
size_t TileSizeBytes, // Pass already +1'd from TileSizeBytesMinus1
const D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_TILES &TilesPartition,
const av1_tile_group_t &tileGroup,
size_t &tile_group_obu_size,
size_t &decode_tile_elements_size)
{
size_t tile_group_obu_size_bits = 0;
uint8_t NumTiles = TilesPartition.ColCount * TilesPartition.RowCount;
if (NumTiles > 1)
tile_group_obu_size_bits++; // tile_start_and_end_present_flag f(1)
bool tile_start_and_end_present_flag = !(tileGroup.tg_start == 0 && (tileGroup.tg_end == (NumTiles - 1)));
if (!(NumTiles == 1 || !tile_start_and_end_present_flag)) {
uint8_t tileBits = log2(TilesPartition.ColCount) + log2(TilesPartition.RowCount);
tile_group_obu_size_bits += tileBits; // tg_start f(tileBits)
tile_group_obu_size_bits += tileBits; // tg_end f(tileBits)
}
while (tile_group_obu_size_bits & 7) // byte_alignment()
tile_group_obu_size_bits++;
decode_tile_elements_size = 0;
for (UINT64 TileIdx = tileGroup.tg_start; TileIdx <= tileGroup.tg_end; TileIdx++) {
// tile_size_minus_1 not coded for last tile
if ((TileIdx != tileGroup.tg_end))
tile_group_obu_size_bits += (TileSizeBytes * 8); // tile_size_minus_1 le(TileSizeBytes)
size_t tile_effective_bytes_size =
static_cast<size_t>(pFrameSubregionMetadata[TileIdx].bSize - pFrameSubregionMetadata[TileIdx].bStartOffset);
decode_tile_elements_size += tile_effective_bytes_size;
tile_group_obu_size_bits += (tile_effective_bytes_size * 8);
}
assert((tile_group_obu_size_bits % 8) == 0);
tile_group_obu_size = (tile_group_obu_size_bits / 8);
}
void
d3d12_video_bitstream_builder_av1::write_obu_tile_group_header(size_t tile_group_obu_size,
std::vector<uint8_t> &headerBitstream,
std::vector<uint8_t>::iterator placingPositionStart,
size_t &writtenBytes)
{
auto startByteOffset = std::distance(headerBitstream.begin(), placingPositionStart);
if (headerBitstream.size() < (startByteOffset + c_DefaultBitstreamBufSize))
headerBitstream.resize(startByteOffset + c_DefaultBitstreamBufSize);
d3d12_video_encoder_bitstream bitstream_full_obu;
bitstream_full_obu.setup_bitstream(headerBitstream.size(), headerBitstream.data(), startByteOffset);
// Write the obu_header
constexpr uint32_t obu_extension_flag = 0;
constexpr uint32_t temporal_id = 0;
constexpr uint32_t spatial_id = 0;
write_obu_header(&bitstream_full_obu, OBU_TILE_GROUP, obu_extension_flag, temporal_id, spatial_id);
// tile_group_obu() will be copied by get_feedback from EncodeFrame output
// we have to calculate its size anyways using the metadata for the obu_header.
// so we just add below the argument tile_group_obu_size informing about the
// tile_group_obu() byte size
// For OBU_TILE_GROUP there is no padding/alignment requirement so they can be concatenated directly by get_feedback
// Write the obu_size element
pack_obu_header_size(&bitstream_full_obu, tile_group_obu_size);
debug_printf("obu_size: %" PRIu64 "\n", tile_group_obu_size);
bitstream_full_obu.flush();
// Shrink headerBitstream to fit
writtenBytes = bitstream_full_obu.get_byte_count() - startByteOffset;
headerBitstream.resize(writtenBytes + startByteOffset);
}

View file

@ -0,0 +1,252 @@
/*
* 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_AV1_H
#define D3D12_VIDEO_ENC_BITSTREAM_BUILDER_AV1_H
#include "d3d12_video_encoder_bitstream_builder.h"
#include "d3d12_video_encoder_bitstream.h"
typedef struct av1_tile_group_t
{
uint8_t tg_start;
uint8_t tg_end;
} av1_tile_group_t;
typedef enum av1_obutype_t
{
OBU_SEQUENCE_HEADER = 1,
OBU_TEMPORAL_DELIMITER = 2,
OBU_FRAME_HEADER = 3,
OBU_TILE_GROUP = 4,
OBU_METADATA = 5,
OBU_FRAME = 6,
OBU_REDUNDANT_FRAME_HEADER = 7,
OBU_PADDING = 15,
} av1_obutype_t;
typedef struct av1_pic_tile_info_t
{
uint32_t uniform_tile_spacing_flag;
D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_TILES tile_partition;
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE tile_mode;
D3D12_VIDEO_ENCODER_AV1_FRAME_SUBREGION_LAYOUT_CONFIG_SUPPORT tile_support_caps;
} av1_pic_tile_info_t;
typedef struct av1_pic_lr_params_t
{
D3D12_VIDEO_ENCODER_AV1_RESTORATION_TYPE lr_type[3];
uint32_t lr_unit_shift;
uint32_t lr_uv_shift;
uint32_t lr_unit_extra_shift;
} av1_pic_lr_params_t;
typedef struct av1_seq_color_config_t
{
DXGI_FORMAT bit_depth;
// uint32_t mono_chrome; // coded in bitstream by default as 0
uint32_t color_primaries;
uint32_t transfer_characteristics;
uint32_t matrix_coefficients;
uint32_t color_description_present_flag;
uint32_t color_range;
uint32_t chroma_sample_position;
uint32_t subsampling_x;
uint32_t subsampling_y;
uint32_t separate_uv_delta_q;
} av1_seq_color_config_t;
typedef struct av1_pic_header_t
{
uint32_t show_existing_frame;
uint32_t frame_to_show_map_idx;
// uint32_t display_frame_id; // frame_id_numbers_present_flag coded in bitstream by default as 0
D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE frame_type;
uint32_t show_frame;
uint32_t showable_frame;
uint32_t error_resilient_mode;
uint32_t disable_cdf_update;
uint32_t allow_screen_content_tools;
uint32_t force_integer_mv;
uint32_t frame_size_override_flag;
uint32_t order_hint;
uint32_t ref_order_hint[8];
uint32_t primary_ref_frame;
uint8_t refresh_frame_flags;
uint32_t FrameWidth;
uint32_t FrameHeight;
uint32_t frame_width_sb;
uint32_t frame_height_sb;
uint32_t use_superres;
uint32_t SuperresDenom;
uint32_t UpscaledWidth;
uint32_t RenderWidth;
uint32_t RenderHeight;
uint32_t allow_intrabc;
int32_t ref_frame_idx[7];
D3D12_VIDEO_ENCODER_AV1_REFERENCE_PICTURE_WARPED_MOTION_INFO ref_global_motion_info[7];
uint32_t allow_high_precision_mv;
D3D12_VIDEO_ENCODER_AV1_INTERPOLATION_FILTERS interpolation_filter;
uint32_t is_motion_mode_switchable;
uint32_t use_ref_frame_mvs;
uint32_t disable_frame_end_update_cdf;
av1_pic_tile_info_t tile_info;
D3D12_VIDEO_ENCODER_CODEC_AV1_QUANTIZATION_CONFIG quantization_params;
D3D12_VIDEO_ENCODER_CODEC_AV1_LOOP_FILTER_DELTA_CONFIG delta_lf_params;
D3D12_VIDEO_ENCODER_CODEC_AV1_QUANTIZATION_DELTA_CONFIG delta_q_params;
// uint32_t CodedLossless; // coded in bitstream by default as 0
uint32_t AllLossless; // coded in bitstream by default as 0
D3D12_VIDEO_ENCODER_CODEC_AV1_LOOP_FILTER_CONFIG loop_filter_params;
D3D12_VIDEO_ENCODER_AV1_CDEF_CONFIG cdef_params;
av1_pic_lr_params_t lr_params;
D3D12_VIDEO_ENCODER_AV1_TX_MODE TxMode;
uint32_t reference_select;
uint32_t skip_mode_present;
uint32_t allow_warped_motion;
uint32_t reduced_tx_set;
uint32_t segmentation_enabled;
D3D12_VIDEO_ENCODER_AV1_SEGMENTATION_CONFIG segmentation_config;
// uint32_t frame_refs_short_signaling; // coded in bitstream by default as 0
} av1_pic_header_t;
typedef struct av1_seq_header_t
{
uint32_t seq_profile;
// uint32_t still_picture; // coded in bitstream by default as 0
// uint32_t reduced_still_picture_header; // coded in bitstream by default as 0
// uint32_t timing_info_present_flag; // coded in bitstream by default as 0
// uint32_t decoder_model_info_present_flag; // coded in bitstream by default as 0
uint32_t operating_points_cnt_minus_1;
uint32_t operating_point_idc[32];
uint32_t seq_level_idx[32];
uint32_t seq_tier[32];
uint32_t decoder_model_present_for_this_op[32];
// uint32_t initial_display_delay_present_flag; // coded in bitstream by default as 0
// uint32_t initial_display_delay_minus_1[32];
// uint32_t initial_display_delay_present_for_this_op[32]
uint32_t max_frame_width;
uint32_t max_frame_height;
// uint32_t frame_id_numbers_present_flag; // coded in bitstream by default as 0
uint32_t use_128x128_superblock;
uint32_t enable_filter_intra;
uint32_t enable_intra_edge_filter;
uint32_t enable_interintra_compound;
uint32_t enable_masked_compound;
uint32_t enable_warped_motion;
uint32_t enable_dual_filter;
uint32_t enable_order_hint;
uint32_t enable_jnt_comp;
uint32_t enable_ref_frame_mvs;
uint32_t seq_choose_screen_content_tools;
uint32_t seq_force_screen_content_tools;
uint32_t seq_choose_integer_mv;
uint32_t seq_force_integer_mv;
uint32_t order_hint_bits_minus1;
uint32_t enable_superres;
uint32_t enable_cdef;
uint32_t enable_restoration;
av1_seq_color_config_t color_config;
// uint32_t film_grain_params_present; // coded in bitstream by default as 0
} av1_seq_header_t;
class d3d12_video_bitstream_builder_av1 : public d3d12_video_bitstream_builder_interface
{
public:
d3d12_video_bitstream_builder_av1()
{ }
~d3d12_video_bitstream_builder_av1()
{ }
void write_temporal_delimiter_obu(std::vector<uint8_t> &headerBitstream,
std::vector<uint8_t>::iterator placingPositionStart,
size_t &writtenBytes);
void write_sequence_header(const av1_seq_header_t *pSeqHdr,
std::vector<uint8_t> &headerBitstream,
std::vector<uint8_t>::iterator placingPositionStart,
size_t &writtenBytes);
void write_frame_header(const av1_seq_header_t *pSeqHdr,
const av1_pic_header_t *pPicHdr,
av1_obutype_t frame_pack_type,
size_t extra_obu_size_bytes,
std::vector<uint8_t> &headerBitstream,
std::vector<uint8_t>::iterator placingPositionStart,
size_t &writtenBytes);
void calculate_tile_group_obu_size(
const D3D12_VIDEO_ENCODER_OUTPUT_METADATA *pParsedMetadata,
const D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA *pFrameSubregionMetadata,
size_t TileSizeBytes, // Pass already +1'd from TileSizeBytesMinus1
const D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_TILES &TilesPartition,
const av1_tile_group_t &tileGroup,
size_t &tile_group_obu_total_size,
size_t &decode_tile_elements_size);
void write_obu_tile_group_header(size_t tile_group_obu_size,
std::vector<uint8_t> &headerBitstream,
std::vector<uint8_t>::iterator placingPositionStart,
size_t &writtenBytes);
private:
// static void write_delta_q_value(d3d12_video_encoder_bitstream& bitstream,
// int32_t delta_q_val);
const size_t c_DefaultBitstreamBufSize = 1024;
void write_obu_header(d3d12_video_encoder_bitstream *pBit,
av1_obutype_t obu_type,
uint32_t obu_extension_flag,
uint32_t temporal_id,
uint32_t spatial_id);
void pack_obu_header_size(d3d12_video_encoder_bitstream *pBit, uint64_t val);
void write_seq_data(d3d12_video_encoder_bitstream *pBit, const av1_seq_header_t *pSeqHdr);
void write_pic_data(d3d12_video_encoder_bitstream *pBit,
const av1_seq_header_t *pSeqHdr,
const av1_pic_header_t *pPicHdr);
void write_delta_q_value(d3d12_video_encoder_bitstream *pBit, int32_t delta_q_val);
void write_frame_size(d3d12_video_encoder_bitstream *pBit,
const av1_seq_header_t *pSeqHdr,
const av1_pic_header_t *pPicHdr);
void write_frame_size_with_refs(d3d12_video_encoder_bitstream *pBit,
const av1_seq_header_t *pSeqHdr,
const av1_pic_header_t *pPicHdr);
void write_render_size(d3d12_video_encoder_bitstream *pBit, const av1_pic_header_t *pPicHdr);
void write_superres_params(d3d12_video_encoder_bitstream *pBit,
const av1_seq_header_t *pSeqHdr,
const av1_pic_header_t *pPicHdr);
// Constants
static const uint32_t frame_width_bits_minus_1 = 15;
static const uint32_t frame_height_bits_minus_1 = 15;
};
#endif // D3D12_VIDEO_ENC_BITSTREAM_BUILDER_AV1_H

View file

@ -0,0 +1,431 @@
/*
* 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_av1.h"
#include "d3d12_video_encoder_references_manager_av1.h"
#include <algorithm>
#include <string>
#include "d3d12_screen.h"
using namespace std;
d3d12_video_encoder_references_manager_av1::d3d12_video_encoder_references_manager_av1(
bool gopHasInterFrames, d3d12_video_dpb_storage_manager_interface &rDpbStorageManager)
: m_CurrentFrameReferencesData({}),
m_PhysicalAllocationsStorage(rDpbStorageManager),
m_gopHasInterFrames(gopHasInterFrames),
m_isCurrentFrameUsedAsReference(false),
m_CurrentFramePicParams({})
{
assert((NUM_REF_FRAMES + 1 /*extra for cur frame output recon pic*/) ==
m_PhysicalAllocationsStorage.get_number_of_tracked_allocations());
debug_printf("[D3D12 Video Encoder Picture Manager AV1] Completed construction of "
"d3d12_video_encoder_references_manager_AV1 instance.\n");
}
void
d3d12_video_encoder_references_manager_av1::clear_dpb()
{
// Reset m_CurrentFrameReferencesData tracking
m_CurrentFrameReferencesData.pVirtualDPBEntries.clear();
m_CurrentFrameReferencesData.pVirtualDPBEntries.resize(NUM_REF_FRAMES);
m_CurrentFrameReferencesData.ReconstructedPicTexture = { nullptr, 0 };
// Initialize DPB slots as unused
for (size_t i = 0; i < NUM_REF_FRAMES; i++)
m_CurrentFrameReferencesData.pVirtualDPBEntries[i].ReconstructedPictureResourceIndex =
UNUSED_VIRTUAL_DPB_SLOT_PHYSICAL_INDEX;
// Reset physical DPB underlying storage
ASSERTED uint32_t numPicsBeforeClearInDPB = m_PhysicalAllocationsStorage.get_number_of_pics_in_dpb();
ASSERTED uint32_t cFreedResources = m_PhysicalAllocationsStorage.clear_decode_picture_buffer();
assert(numPicsBeforeClearInDPB == cFreedResources);
}
D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE
d3d12_video_encoder_references_manager_av1::get_current_frame_recon_pic_output_allocation()
{
return m_CurrentFrameReferencesData.ReconstructedPicTexture;
}
bool
d3d12_video_encoder_references_manager_av1::is_current_frame_used_as_reference()
{
return m_isCurrentFrameUsedAsReference;
}
void
d3d12_video_encoder_references_manager_av1::begin_frame(D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA curFrameData,
bool bUsedAsReference,
struct pipe_picture_desc *picture)
{
m_CurrentFramePicParams = *curFrameData.pAV1PicData;
m_isCurrentFrameUsedAsReference = bUsedAsReference;
if (m_CurrentFramePicParams.FrameType == D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_KEY_FRAME)
clear_dpb();
// Prepare current frame recon pic allocation
m_CurrentFrameReferencesData.ReconstructedPicTexture = { nullptr, 0 };
if (is_current_frame_used_as_reference() && m_gopHasInterFrames) {
auto reconPic = m_PhysicalAllocationsStorage.get_new_tracked_picture_allocation();
m_CurrentFrameReferencesData.ReconstructedPicTexture.pReconstructedPicture = reconPic.pReconstructedPicture;
m_CurrentFrameReferencesData.ReconstructedPicTexture.ReconstructedPictureSubresource =
reconPic.ReconstructedPictureSubresource;
}
#ifdef DEBUG
assert(m_PhysicalAllocationsStorage.get_number_of_tracked_allocations() <=
(NUM_REF_FRAMES + 1)); // pool is not extended beyond maximum expected usage
if (m_CurrentFramePicParams.FrameType == D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_KEY_FRAME) {
// After clearing the DPB, outstanding used allocations should be 1u only for the first allocation for the
// reconstructed picture of the initial KEY_FRAME
assert(m_PhysicalAllocationsStorage.get_number_of_in_use_allocations() ==
((is_current_frame_used_as_reference() && m_gopHasInterFrames) ? 1u : 0u));
}
#endif
}
void
d3d12_video_encoder_references_manager_av1::end_frame()
{
refresh_dpb_slots_with_current_frame_reconpic();
}
D3D12_VIDEO_ENCODE_REFERENCE_FRAMES
d3d12_video_encoder_references_manager_av1::get_current_reference_frames()
{
D3D12_VIDEO_ENCODE_REFERENCE_FRAMES retVal = { 0,
// ppTexture2Ds
nullptr,
// pSubresources
nullptr };
if ((m_CurrentFramePicParams.FrameType != D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_KEY_FRAME) && m_gopHasInterFrames) {
auto curRef = m_PhysicalAllocationsStorage.get_current_reference_frames();
retVal.NumTexture2Ds = curRef.NumTexture2Ds;
retVal.ppTexture2Ds = curRef.ppTexture2Ds;
retVal.pSubresources = curRef.pSubresources;
}
return retVal;
}
void
d3d12_video_encoder_references_manager_av1::print_ref_frame_idx()
{
std::string refListContentsString;
for (uint32_t idx = 0; idx < REFS_PER_FRAME; idx++) {
uint32_t reference = 0;
reference = m_CurrentFramePicParams.ReferenceIndices[idx];
refListContentsString += "{ ref_frame_idx[" + std::to_string(idx) + "] - ";
refListContentsString += " DPBidx: ";
refListContentsString += std::to_string(reference);
if (m_CurrentFrameReferencesData.pVirtualDPBEntries[reference].ReconstructedPictureResourceIndex !=
UNUSED_VIRTUAL_DPB_SLOT_PHYSICAL_INDEX) {
refListContentsString += " - OrderHint: ";
refListContentsString += std::to_string(m_CurrentFrameReferencesData.pVirtualDPBEntries[reference].OrderHint);
refListContentsString += " - PictureIndex: ";
refListContentsString +=
std::to_string(m_CurrentFrameReferencesData.pVirtualDPBEntries[reference].PictureIndex);
} else {
refListContentsString += " - Unused Virtual DPB slot ";
}
refListContentsString += " }\n";
}
debug_printf("[D3D12 Video Encoder Picture Manager AV1] ref_frame_idx[REFS_PER_FRAME] for frame with OrderHint %d "
"(PictureIndex %d) is: \n%s \n",
m_CurrentFramePicParams.OrderHint,
m_CurrentFramePicParams.PictureIndex,
refListContentsString.c_str());
debug_printf("[D3D12 Video Encoder Picture Manager AV1] Requested PrimaryRefFrame: %d\n",
m_CurrentFramePicParams.PrimaryRefFrame);
debug_printf("[D3D12 Video Encoder Picture Manager AV1] Requested RefreshFrameFlags: %d\n",
m_CurrentFramePicParams.RefreshFrameFlags);
}
void
d3d12_video_encoder_references_manager_av1::print_virtual_dpb_entries()
{
std::string dpbContents;
for (uint32_t dpbResIdx = 0; dpbResIdx < m_CurrentFrameReferencesData.pVirtualDPBEntries.size(); dpbResIdx++) {
auto &dpbDesc = m_CurrentFrameReferencesData.pVirtualDPBEntries[dpbResIdx];
if (dpbDesc.ReconstructedPictureResourceIndex != UNUSED_VIRTUAL_DPB_SLOT_PHYSICAL_INDEX) {
d3d12_video_reconstructed_picture dpbEntry =
m_PhysicalAllocationsStorage.get_reference_frame(dpbDesc.ReconstructedPictureResourceIndex);
dpbContents += "{ DPBidx: ";
dpbContents += std::to_string(dpbResIdx);
dpbContents += " - OrderHint: ";
dpbContents += std::to_string(dpbDesc.OrderHint);
dpbContents += " - PictureIndex: ";
dpbContents += std::to_string(dpbDesc.PictureIndex);
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 += " - RefCount (from any ref_frame_idx[0..6]): ";
dpbContents += std::to_string(get_dpb_virtual_slot_refcount_from_ref_frame_idx(dpbResIdx));
dpbContents += " }\n";
} else {
dpbContents += "{ DPBidx: ";
dpbContents += std::to_string(dpbResIdx);
dpbContents += " < Unused Virtual DPB slot > }\n";
}
}
debug_printf(
"[D3D12 Video Encoder Picture Manager AV1] DPB Current output reconstructed picture %p subresource %d\n",
m_CurrentFrameReferencesData.ReconstructedPicTexture.pReconstructedPicture,
m_CurrentFrameReferencesData.ReconstructedPicTexture.ReconstructedPictureSubresource);
debug_printf("[D3D12 Video Encoder Picture Manager AV1] NumTexture2Ds is %d frames - "
"Number of DPB virtual entries is %" PRIu64 " entries for frame with OrderHint "
"%d (PictureIndex %d) are: \n%s \n",
m_PhysicalAllocationsStorage.get_number_of_pics_in_dpb(),
m_CurrentFrameReferencesData.pVirtualDPBEntries.size(),
m_CurrentFramePicParams.OrderHint,
m_CurrentFramePicParams.PictureIndex,
dpbContents.c_str());
}
void
d3d12_video_encoder_references_manager_av1::print_physical_resource_references()
{
debug_printf("[D3D12 Video Encoder Picture Manager AV1] %d physical resources IN USE out of a total of %d physical "
"resources ALLOCATED "
"resources at end_frame for frame with OrderHint %d (PictureIndex %d)\n",
m_PhysicalAllocationsStorage.get_number_of_in_use_allocations(),
m_PhysicalAllocationsStorage.get_number_of_tracked_allocations(),
m_CurrentFramePicParams.OrderHint,
m_CurrentFramePicParams.PictureIndex);
D3D12_VIDEO_ENCODE_REFERENCE_FRAMES curRefs = get_current_reference_frames();
std::string descContents;
for (uint32_t index = 0; index < curRefs.NumTexture2Ds; index++) {
assert(curRefs.ppTexture2Ds[index]); // These must be contiguous when sending down to D3D12 API
descContents += "{ REFERENCE_FRAMES Index: ";
descContents += std::to_string(index);
descContents += " - ppTextures ptr: ";
char strBuf[256];
memset(&strBuf, '\0', 256);
sprintf(strBuf, "%p", curRefs.ppTexture2Ds[index]);
descContents += std::string(strBuf);
descContents += " - ppSubresources index: ";
if (curRefs.pSubresources != nullptr)
descContents += std::to_string(curRefs.pSubresources[index]);
else
descContents += "(nil)";
descContents += " - RefCount (from any virtual dpb slot [0..REFS_PER_FRAME]): ";
descContents += std::to_string(get_dpb_physical_slot_refcount_from_virtual_dpb(index));
descContents += " }\n";
}
debug_printf("[D3D12 Video Encoder Picture Manager AV1] D3D12_VIDEO_ENCODE_REFERENCE_FRAMES has %d physical "
"resources in ppTexture2Ds for OrderHint "
"%d (PictureIndex %d) are: \n%s \n",
curRefs.NumTexture2Ds,
m_CurrentFramePicParams.OrderHint,
m_CurrentFramePicParams.PictureIndex,
descContents.c_str());
}
//
// Returns the number of Reference<XXX> (ie. LAST, LAST2, ..., ALTREF, etc)
// pointing to dpbSlotIndex
//
uint32_t
d3d12_video_encoder_references_manager_av1::get_dpb_virtual_slot_refcount_from_ref_frame_idx(uint32_t dpbSlotIndex)
{
uint32_t refCount = 0;
for (uint8_t i = 0; i < ARRAY_SIZE(m_CurrentFramePicParams.ReferenceIndices); i++) {
if (m_CurrentFramePicParams.ReferenceIndices[i] == dpbSlotIndex) {
refCount++;
}
}
return refCount;
}
//
// Returns the number of entries in the virtual DPB descriptors
// that point to physicalSlotIndex
//
uint32_t
d3d12_video_encoder_references_manager_av1::get_dpb_physical_slot_refcount_from_virtual_dpb(uint32_t physicalSlotIndex)
{
uint32_t refCount = 0;
for (unsigned dpbSlotIndex = 0; dpbSlotIndex < m_CurrentFrameReferencesData.pVirtualDPBEntries.size();
dpbSlotIndex++) {
if (m_CurrentFrameReferencesData.pVirtualDPBEntries[dpbSlotIndex].ReconstructedPictureResourceIndex ==
physicalSlotIndex)
refCount++;
}
return refCount;
}
void
d3d12_video_encoder_references_manager_av1::get_current_frame_picture_control_data(
D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA &codecAllocation)
{
assert(m_CurrentFrameReferencesData.pVirtualDPBEntries.size() == NUM_REF_FRAMES);
assert(codecAllocation.DataSize == sizeof(D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_CODEC_DATA));
// Some apps don't clean this up for INTRA/KEY frames
if ((m_CurrentFramePicParams.FrameType != D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_INTER_FRAME)
&& (m_CurrentFramePicParams.FrameType != D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_SWITCH_FRAME))
{
for (uint8_t i = 0; i < ARRAY_SIZE(m_CurrentFramePicParams.ReferenceIndices); i++) {
m_CurrentFramePicParams.ReferenceIndices[i] = 0;
}
}
for (uint8_t i = 0; i < NUM_REF_FRAMES; i++)
m_CurrentFramePicParams.ReferenceFramesReconPictureDescriptors[i] =
m_CurrentFrameReferencesData.pVirtualDPBEntries[i];
#ifdef DEBUG // Otherwise may iterate over structures and do no-op debug_printf
print_ref_frame_idx();
print_virtual_dpb_entries();
print_physical_resource_references();
#endif
*codecAllocation.pAV1PicData = m_CurrentFramePicParams;
}
void
d3d12_video_encoder_references_manager_av1::refresh_dpb_slots_with_current_frame_reconpic()
{
UINT refresh_frame_flags = m_CurrentFramePicParams.RefreshFrameFlags;
debug_printf("[D3D12 Video Encoder Picture Manager AV1] refresh_frame_flags 0x%x for frame with OrderHint %d "
"(PictureIndex %d)\n",
refresh_frame_flags,
m_CurrentFramePicParams.OrderHint,
m_CurrentFramePicParams.PictureIndex);
//
// Put current frame in all slots of DPB indicated by refresh_frame_flags
//
if (is_current_frame_used_as_reference() && m_gopHasInterFrames && (refresh_frame_flags != 0)) {
//
// First do a eviction pass and update virtual DPB physical indices in case the physical array shifted with an
// eviction (erasing an ppTexture2Ds entry)
//
for (unsigned dpbSlotIdx = 0; dpbSlotIdx < NUM_REF_FRAMES; dpbSlotIdx++) {
if (((refresh_frame_flags >> dpbSlotIdx) & 0x1) != 0) {
//
// Check if the slot this reconpic will take in the virtual DPB will leave an unreferenced
// physical allocation, that may need to be evicted (if no other virtual dpb slot references it)
//
if (m_CurrentFrameReferencesData.pVirtualDPBEntries[dpbSlotIdx].ReconstructedPictureResourceIndex !=
UNUSED_VIRTUAL_DPB_SLOT_PHYSICAL_INDEX) {
// If this is a virtual dpb valid entry, there has to be a valid underlying physical allocation
assert(m_CurrentFrameReferencesData.pVirtualDPBEntries[dpbSlotIdx].ReconstructedPictureResourceIndex <
m_PhysicalAllocationsStorage.get_number_of_pics_in_dpb());
// Get the number of entries in the virtual DPB descriptors that point to
// ReconstructedPictureResourceIndex of the current virtual dpb slot (counting the current dpbSlotIdx we
// didn't clear yet)
uint32_t numRefs = get_dpb_physical_slot_refcount_from_virtual_dpb(
m_CurrentFrameReferencesData.pVirtualDPBEntries[dpbSlotIdx].ReconstructedPictureResourceIndex);
if (numRefs == 1) {
// When refreshing this dpbSlotIdx, we will leave an unreferenced physical allocation
// so we can just remove it (and release the ID3D12Resource allocation back to the unused pool)
bool wasTracked = false;
m_PhysicalAllocationsStorage.remove_reference_frame(
m_CurrentFrameReferencesData.pVirtualDPBEntries[dpbSlotIdx].ReconstructedPictureResourceIndex,
&wasTracked);
assert(wasTracked);
// Indices in the virtual dpb higher or equal than
// m_CurrentFrameReferencesData.pVirtualDPBEntries[dpbSlotIdx].ReconstructedPictureResourceIndex
// must be shifted back one place as we erased the entry in the physical allocations array (ppTexture2Ds)
for (auto &dpbVirtualEntry : m_CurrentFrameReferencesData.pVirtualDPBEntries) {
if (
// Check for slot being used
(dpbVirtualEntry.ReconstructedPictureResourceIndex != UNUSED_VIRTUAL_DPB_SLOT_PHYSICAL_INDEX) &&
// Check for slot to be affected by removing the entry in ppTexture2Ds above
(dpbVirtualEntry.ReconstructedPictureResourceIndex >
m_CurrentFrameReferencesData.pVirtualDPBEntries[dpbSlotIdx].ReconstructedPictureResourceIndex)) {
// Decrease the index to compensate for the removed ppTexture2Ds entry
dpbVirtualEntry.ReconstructedPictureResourceIndex--;
}
}
}
// Clear this virtual dpb entry (so next iterations will decrease refcount)
m_CurrentFrameReferencesData.pVirtualDPBEntries[dpbSlotIdx].ReconstructedPictureResourceIndex =
UNUSED_VIRTUAL_DPB_SLOT_PHYSICAL_INDEX;
}
}
}
//
// Find a new free physical index for the current recon pic; always put new physical entry at the end to avoid
// having to shift existing indices in virtual DPB
//
UINT allocationSlotIdx = m_PhysicalAllocationsStorage.get_number_of_pics_in_dpb();
assert(static_cast<uint32_t>(allocationSlotIdx) < NUM_REF_FRAMES);
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;
m_PhysicalAllocationsStorage.insert_reference_frame(refFrameDesc, allocationSlotIdx);
//
// Update virtual DPB entries with the current frame recon picture
//
for (unsigned dpbSlotIdx = 0; dpbSlotIdx < NUM_REF_FRAMES; dpbSlotIdx++) {
if (((refresh_frame_flags >> dpbSlotIdx) & 0x1) != 0) {
m_CurrentFrameReferencesData.pVirtualDPBEntries[dpbSlotIdx] = { allocationSlotIdx,
0, // NO temporal scalability support
0, // NO spatial scalability support
m_CurrentFramePicParams.FrameType,
{}, // No global_motion support
m_CurrentFramePicParams.OrderHint,
m_CurrentFramePicParams.PictureIndex };
}
}
}
// 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_PhysicalAllocationsStorage.get_number_of_tracked_allocations() <= (NUM_REF_FRAMES + 1));
}

View file

@ -0,0 +1,79 @@
/*
* 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_AV1_H
#define D3D12_VIDEO_ENCODE_FIFO_REFERENCES_MANAGER_AV1_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_av1 : public d3d12_video_encoder_references_manager_interface
{
public:
void end_frame();
void begin_frame(D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA curFrameData,
bool bUsedAsReference,
struct pipe_picture_desc *picture);
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_av1(bool gopHasInterCodedFrames,
d3d12_video_dpb_storage_manager_interface &rDpbStorageManager);
~d3d12_video_encoder_references_manager_av1()
{ }
private:
// Class helpers
void clear_dpb();
void refresh_dpb_slots_with_current_frame_reconpic();
uint32_t get_dpb_virtual_slot_refcount_from_ref_frame_idx(uint32_t dpbSlotIndex);
uint32_t get_dpb_physical_slot_refcount_from_virtual_dpb(uint32_t ReconstructedPictureResourceIndex);
void print_virtual_dpb_entries();
void print_physical_resource_references();
void print_ref_frame_idx();
// Class members
struct current_frame_references_data
{
std::vector<D3D12_VIDEO_ENCODER_AV1_REFERENCE_PICTURE_DESCRIPTOR> pVirtualDPBEntries;
D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE ReconstructedPicTexture;
} m_CurrentFrameReferencesData = {};
d3d12_video_dpb_storage_manager_interface &m_PhysicalAllocationsStorage;
bool m_gopHasInterFrames = false;
bool m_isCurrentFrameUsedAsReference = false;
D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_CODEC_DATA m_CurrentFramePicParams = {};
// Constants
const uint32_t NUM_REF_FRAMES = 8;
const uint32_t REFS_PER_FRAME = 7;
};
#endif

View file

@ -42,10 +42,14 @@ struct d3d12_encode_codec_support {
union pipe_h265_enc_cap_block_sizes hevc_block_sizes;
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC d3d12_caps;
} hevc_support;
// Can add more codecs for each codec specific caps here, for example:
// struct {
// D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_H264;
// } h264_support;
struct {
union pipe_av1_enc_cap_features features;
union pipe_av1_enc_cap_features_ext1 features_ext1;
union pipe_av1_enc_cap_features_ext2 features_ext2;
#if D3D12_PREVIEW_SDK_VERSION >= 711
D3D12_VIDEO_ENCODER_AV1_CODEC_CONFIGURATION_SUPPORT d3d12_caps;
#endif
} av1_support;
};
};
@ -290,16 +294,88 @@ d3d12_video_encode_supported_references_per_frame_structures(const D3D12_VIDEO_E
supportedMaxRefFrames = (maxRefForL0 & 0xffff) | ((maxRefForL1 & 0xffff) << 16);
}
}
#if D3D12_PREVIEW_SDK_VERSION >= 711
else if(codec == D3D12_VIDEO_ENCODER_CODEC_AV1){
D3D12_VIDEO_ENCODER_CODEC_AV1_PICTURE_CONTROL_SUPPORT av1PictureControl = {};
capPictureControlData.Profile = profile;
capPictureControlData.PictureSupport.pAV1Support = &av1PictureControl;
capPictureControlData.PictureSupport.DataSize = sizeof(av1PictureControl);
HRESULT hr = pD3D12VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_ENCODER_CODEC_PICTURE_CONTROL_SUPPORT,
&capPictureControlData,
sizeof(capPictureControlData));
if (FAILED(hr)) {
debug_printf("CheckFeatureSupport failed with HR %x\n", hr);
}
if (capPictureControlData.IsSupported) {
/* This attribute determines the maximum number of reference
* frames supported for encoding.
*/
supportedMaxRefFrames = capPictureControlData.PictureSupport.pAV1Support->MaxUniqueReferencesPerFrame;
supportedMaxRefFrames = (supportedMaxRefFrames & 0xffff) | ((supportedMaxRefFrames & 0xffff) << 16);
}
}
#endif
return supportedMaxRefFrames;
}
#if D3D12_PREVIEW_SDK_VERSION >= 711
static void
d3d12_video_encode_supported_tile_structures(const D3D12_VIDEO_ENCODER_CODEC &codec,
const D3D12_VIDEO_ENCODER_PROFILE_DESC &profile,
const D3D12_VIDEO_ENCODER_LEVEL_SETTING &level,
ID3D12VideoDevice3 *pD3D12VideoDevice,
D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC maxRes,
uint32_t& supportedSliceStructures, // out
uint32_t& tile_size_bytes_minus1 // out
)
{
// Assume no support and then add as queries succeed
supportedSliceStructures = PIPE_VIDEO_CAP_SLICE_STRUCTURE_NONE;
// Only codecs supporting tiles should use this method. For slices related info, use d3d12_video_encode_supported_slice_structures
assert (codec == D3D12_VIDEO_ENCODER_CODEC_AV1);
D3D12_FEATURE_DATA_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_CONFIG capDataTilesSupport = { };
capDataTilesSupport.NodeIndex = 0;
capDataTilesSupport.Codec = codec;
capDataTilesSupport.Profile = profile;
capDataTilesSupport.Level = level;
capDataTilesSupport.FrameResolution = maxRes; // Query for worst case resolution
D3D12_VIDEO_ENCODER_AV1_FRAME_SUBREGION_LAYOUT_CONFIG_SUPPORT av1TileSupport = { };
capDataTilesSupport.CodecSupport.DataSize = sizeof(av1TileSupport);
capDataTilesSupport.CodecSupport.pAV1Support = &av1TileSupport;
av1TileSupport.Use128SuperBlocks = false; // return units in 64x64 default size
capDataTilesSupport.SubregionMode =
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_GRID_PARTITION;
HRESULT hr = pD3D12VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_CONFIG,
&capDataTilesSupport,
sizeof(capDataTilesSupport));
if(SUCCEEDED(hr) && capDataTilesSupport.IsSupported)
supportedSliceStructures |= (PIPE_VIDEO_CAP_SLICE_STRUCTURE_POWER_OF_TWO_ROWS | PIPE_VIDEO_CAP_SLICE_STRUCTURE_EQUAL_ROWS | PIPE_VIDEO_CAP_SLICE_STRUCTURE_EQUAL_MULTI_ROWS);
capDataTilesSupport.SubregionMode =
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_CONFIGURABLE_GRID_PARTITION;
hr = pD3D12VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_CONFIG,
&capDataTilesSupport,
sizeof(capDataTilesSupport));
if(SUCCEEDED(hr) && capDataTilesSupport.IsSupported)
supportedSliceStructures |= (PIPE_VIDEO_CAP_SLICE_STRUCTURE_ARBITRARY_MACROBLOCKS | PIPE_VIDEO_CAP_SLICE_STRUCTURE_ARBITRARY_ROWS);
tile_size_bytes_minus1 = av1TileSupport.TileSizeBytesMinus1;
}
#endif
static uint32_t
d3d12_video_encode_supported_slice_structures(const D3D12_VIDEO_ENCODER_CODEC &codec,
D3D12_VIDEO_ENCODER_PROFILE_DESC profile,
D3D12_VIDEO_ENCODER_LEVEL_SETTING level,
ID3D12VideoDevice3 *pD3D12VideoDevice)
{
// Only codecs supporting slices should use this method. For tile related info, use d3d12_video_encode_supported_tile_structures
assert ((codec == D3D12_VIDEO_ENCODER_CODEC_H264) || (codec == D3D12_VIDEO_ENCODER_CODEC_HEVC));
uint32_t supportedSliceStructuresBitMask = PIPE_VIDEO_CAP_SLICE_STRUCTURE_NONE;
D3D12_FEATURE_DATA_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE capDataSubregionLayout = {};
@ -328,15 +404,15 @@ d3d12_video_encode_supported_slice_structures(const D3D12_VIDEO_ENCODER_CODEC &c
capDataSubregionLayout.SubregionMode =
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_PARTITIONING_SUBREGIONS_PER_FRAME;
HRESULT hr = pD3D12VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE,
&capDataSubregionLayout,
sizeof(capDataSubregionLayout));
&capDataSubregionLayout,
sizeof(capDataSubregionLayout));
if (FAILED(hr)) {
debug_printf("CheckFeatureSupport failed with HR %x\n", hr);
} else if (capDataSubregionLayout.IsSupported) {
/* This would be setting N subregions per frame in this D3D12 mode where N = (height/blocksize) / K */
supportedSliceStructuresBitMask |= PIPE_VIDEO_CAP_SLICE_STRUCTURE_EQUAL_MULTI_ROWS;
/* Assuming height/blocksize >= max_supported_slices, which is reported
in PIPE_VIDEO_CAP_ENC_MAX_SLICES_PER_FRAME and should be checked by the client*/
in PIPE_VIDEO_CAP_ENC_MAX_SLICES_PER_FRAME and should be checked by the client*/
/* This would be setting N subregions per frame in this D3D12 mode where N = (height/blocksize) */
supportedSliceStructuresBitMask |= PIPE_VIDEO_CAP_SLICE_STRUCTURE_EQUAL_ROWS;
/* This is ok, would be setting K rows per subregions in this D3D12 mode (and rounding the last one) */
@ -346,15 +422,15 @@ d3d12_video_encode_supported_slice_structures(const D3D12_VIDEO_ENCODER_CODEC &c
capDataSubregionLayout.SubregionMode =
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_PARTITIONING_ROWS_PER_SUBREGION;
hr = pD3D12VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE,
&capDataSubregionLayout,
sizeof(capDataSubregionLayout));
&capDataSubregionLayout,
sizeof(capDataSubregionLayout));
if (FAILED(hr)) {
debug_printf("CheckFeatureSupport failed with HR %x\n", hr);
} else if (capDataSubregionLayout.IsSupported) {
/* This would be setting K rows per subregions in this D3D12 mode */
supportedSliceStructuresBitMask |= PIPE_VIDEO_CAP_SLICE_STRUCTURE_EQUAL_MULTI_ROWS;
/* Assuming height/blocksize >= max_supported_slices, which is reported
in PIPE_VIDEO_CAP_ENC_MAX_SLICES_PER_FRAME and should be checked by the client*/
in PIPE_VIDEO_CAP_ENC_MAX_SLICES_PER_FRAME and should be checked by the client*/
/* This would be setting 1 row per subregion in this D3D12 mode */
supportedSliceStructuresBitMask |= PIPE_VIDEO_CAP_SLICE_STRUCTURE_EQUAL_ROWS;
/* This is ok, would be setting K rows per subregions in this D3D12 mode (and rounding the last one) */
@ -366,43 +442,49 @@ d3d12_video_encode_supported_slice_structures(const D3D12_VIDEO_ENCODER_CODEC &c
/*capDataSubregionLayout.SubregionMode = D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_BYTES_PER_SUBREGION;
hr = pD3D12VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE,
&capDataSubregionLayout,
sizeof(capDataSubregionLayout));
&capDataSubregionLayout,
sizeof(capDataSubregionLayout));
if (FAILED(hr)) {
debug_printf("CheckFeatureSupport failed with HR %x\n", hr);
} else if (capDataSubregionLayout.IsSupported) {
supportedSliceStructuresBitMask |= PIPE_VIDEO_CAP_SLICE_STRUCTURE_MAX_SLICE_SIZE;
}*/
}*/
return supportedSliceStructuresBitMask;
}
static bool
d3d12_video_encode_max_supported_slices(const D3D12_VIDEO_ENCODER_CODEC &argTargetCodec,
D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC maxResolution,
DXGI_FORMAT encodeFormat,
uint32_t &outMaxSlices,
ID3D12VideoDevice3 *pD3D12VideoDevice,
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT codecSupport)
d3d12_video_encode_support_caps(const D3D12_VIDEO_ENCODER_CODEC &argTargetCodec,
D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC maxResolution,
DXGI_FORMAT encodeFormat,
ID3D12VideoDevice3 *pD3D12VideoDevice,
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT codecSupport,
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE requestedSliceMode,
D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT1 &capEncoderSupportData1,
D3D12_FEATURE_DATA_VIDEO_ENCODER_RESOLUTION_SUPPORT_LIMITS &resolutionDepCaps)
{
D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT capEncoderSupportData = {};
capEncoderSupportData.NodeIndex = 0;
capEncoderSupportData.Codec = argTargetCodec;
capEncoderSupportData.InputFormat = encodeFormat;
capEncoderSupportData.RateControl = {};
capEncoderSupportData.RateControl.Mode = D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CQP;
capEncoderSupportData.RateControl.TargetFrameRate.Numerator = 60;
capEncoderSupportData.RateControl.TargetFrameRate.Denominator = 1;
capEncoderSupportData1 = {};
capEncoderSupportData1.NodeIndex = 0;
capEncoderSupportData1.Codec = argTargetCodec;
capEncoderSupportData1.InputFormat = encodeFormat;
capEncoderSupportData1.RateControl = {};
capEncoderSupportData1.RateControl.Mode = D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CQP;
capEncoderSupportData1.RateControl.TargetFrameRate.Numerator = 60;
capEncoderSupportData1.RateControl.TargetFrameRate.Denominator = 1;
D3D12_VIDEO_ENCODER_RATE_CONTROL_CQP rcCqp = { 25, 25, 25 };
capEncoderSupportData.RateControl.ConfigParams.pConfiguration_CQP = &rcCqp;
capEncoderSupportData.RateControl.ConfigParams.DataSize = sizeof(rcCqp);
capEncoderSupportData.IntraRefresh = D3D12_VIDEO_ENCODER_INTRA_REFRESH_MODE_NONE;
capEncoderSupportData.ResolutionsListCount = 1;
capEncoderSupportData.pResolutionList = &maxResolution;
capEncoderSupportData.MaxReferenceFramesInDPB = 1;
capEncoderSupportData.SubregionFrameEncoding =
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_PARTITIONING_SUBREGIONS_PER_FRAME;
capEncoderSupportData1.RateControl.ConfigParams.pConfiguration_CQP = &rcCqp;
capEncoderSupportData1.RateControl.ConfigParams.DataSize = sizeof(rcCqp);
capEncoderSupportData1.IntraRefresh = D3D12_VIDEO_ENCODER_INTRA_REFRESH_MODE_NONE;
capEncoderSupportData1.ResolutionsListCount = 1;
capEncoderSupportData1.pResolutionList = &maxResolution;
capEncoderSupportData1.MaxReferenceFramesInDPB = 1;
D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_SLICES sliceData = { };
#if D3D12_PREVIEW_SDK_VERSION >= 711
// Assume single tile for feature query support / entrypoint enumeration purposes
D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_TILES tileData = { };
tileData.ColCount = 1;
tileData.RowCount = 1;
#endif
/*
All codec structures must be declared outside the switch statement to be
present in memory (stack scope) when calling CheckFeatureSupport below
@ -415,18 +497,29 @@ d3d12_video_encode_max_supported_slices(const D3D12_VIDEO_ENCODER_CODEC &argTarg
D3D12_VIDEO_ENCODER_LEVEL_TIER_CONSTRAINTS_HEVC hevcLvl = { };
D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE_HEVC hevcGop = { 1, 0, 0 };
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC hevcConfig = {};
#if D3D12_PREVIEW_SDK_VERSION >= 711
D3D12_VIDEO_ENCODER_AV1_PROFILE av1prof = { };
D3D12_VIDEO_ENCODER_AV1_LEVEL_TIER_CONSTRAINTS av1Lvl = { };
D3D12_VIDEO_ENCODER_AV1_SEQUENCE_STRUCTURE av1Gop = { 1, 0 };
D3D12_VIDEO_ENCODER_AV1_CODEC_CONFIGURATION av1Config = {};
#endif
switch (argTargetCodec) {
case D3D12_VIDEO_ENCODER_CODEC_H264:
{
// assert(codecSupport.pH264Support); // Fill this in caller if ever used
capEncoderSupportData.SuggestedProfile.pH264Profile = &h264prof;
capEncoderSupportData.SuggestedProfile.DataSize = sizeof(h264prof);
capEncoderSupportData.SuggestedLevel.pH264LevelSetting = &h264lvl;
capEncoderSupportData.SuggestedLevel.DataSize = sizeof(h264lvl);
capEncoderSupportData.CodecGopSequence.pH264GroupOfPictures = &h264Gop;
capEncoderSupportData.CodecGopSequence.DataSize = sizeof(h264Gop);
capEncoderSupportData.CodecConfiguration.DataSize = sizeof(h264Config);
capEncoderSupportData.CodecConfiguration.pH264Config = &h264Config;
capEncoderSupportData1.SuggestedProfile.pH264Profile = &h264prof;
capEncoderSupportData1.SuggestedProfile.DataSize = sizeof(h264prof);
capEncoderSupportData1.SuggestedLevel.pH264LevelSetting = &h264lvl;
capEncoderSupportData1.SuggestedLevel.DataSize = sizeof(h264lvl);
capEncoderSupportData1.CodecGopSequence.pH264GroupOfPictures = &h264Gop;
capEncoderSupportData1.CodecGopSequence.DataSize = sizeof(h264Gop);
capEncoderSupportData1.CodecConfiguration.DataSize = sizeof(h264Config);
capEncoderSupportData1.CodecConfiguration.pH264Config = &h264Config;
capEncoderSupportData1.SubregionFrameEncoding = requestedSliceMode;
#if D3D12_PREVIEW_SDK_VERSION >= 711
capEncoderSupportData1.SubregionFrameEncodingData.DataSize = sizeof(sliceData);
capEncoderSupportData1.SubregionFrameEncodingData.pSlicesPartition_H264 = &sliceData;
#endif
} break;
case D3D12_VIDEO_ENCODER_CODEC_HEVC:
@ -446,16 +539,56 @@ d3d12_video_encode_max_supported_slices(const D3D12_VIDEO_ENCODER_CODEC &argTarg
if ((codecSupport.pHEVCSupport->SupportFlags & D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_ASYMETRIC_MOTION_PARTITION_REQUIRED) != 0)
hevcConfig.ConfigurationFlags |= D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_USE_ASYMETRIC_MOTION_PARTITION;
capEncoderSupportData.SuggestedProfile.pHEVCProfile = &hevcprof;
capEncoderSupportData.SuggestedProfile.DataSize = sizeof(hevcprof);
capEncoderSupportData.SuggestedLevel.pHEVCLevelSetting = &hevcLvl;
capEncoderSupportData.SuggestedLevel.DataSize = sizeof(hevcLvl);
capEncoderSupportData.CodecGopSequence.pHEVCGroupOfPictures = &hevcGop;
capEncoderSupportData.CodecGopSequence.DataSize = sizeof(hevcGop);
capEncoderSupportData.CodecConfiguration.DataSize = sizeof(hevcConfig);
capEncoderSupportData.CodecConfiguration.pHEVCConfig = &hevcConfig;
capEncoderSupportData1.SuggestedProfile.pHEVCProfile = &hevcprof;
capEncoderSupportData1.SuggestedProfile.DataSize = sizeof(hevcprof);
capEncoderSupportData1.SuggestedLevel.pHEVCLevelSetting = &hevcLvl;
capEncoderSupportData1.SuggestedLevel.DataSize = sizeof(hevcLvl);
capEncoderSupportData1.CodecGopSequence.pHEVCGroupOfPictures = &hevcGop;
capEncoderSupportData1.CodecGopSequence.DataSize = sizeof(hevcGop);
capEncoderSupportData1.CodecConfiguration.DataSize = sizeof(hevcConfig);
capEncoderSupportData1.CodecConfiguration.pHEVCConfig = &hevcConfig;
capEncoderSupportData1.SubregionFrameEncoding = requestedSliceMode;
#if D3D12_PREVIEW_SDK_VERSION >= 711
capEncoderSupportData1.SubregionFrameEncodingData.DataSize = sizeof(sliceData);
capEncoderSupportData1.SubregionFrameEncodingData.pSlicesPartition_HEVC = &sliceData;
#endif
} break;
#if D3D12_PREVIEW_SDK_VERSION >= 711
case D3D12_VIDEO_ENCODER_CODEC_AV1:
{
capEncoderSupportData1.SuggestedProfile.pAV1Profile = &av1prof;
capEncoderSupportData1.SuggestedProfile.DataSize = sizeof(av1prof);
capEncoderSupportData1.SuggestedLevel.pAV1LevelSetting = &av1Lvl;
capEncoderSupportData1.SuggestedLevel.DataSize = sizeof(av1Lvl);
capEncoderSupportData1.CodecGopSequence.pAV1SequenceStructure = &av1Gop;
capEncoderSupportData1.CodecGopSequence.DataSize = sizeof(av1Gop);
D3D12_FEATURE_DATA_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT capCodecConfigData = { };
capCodecConfigData.NodeIndex = 0;
capCodecConfigData.Codec = D3D12_VIDEO_ENCODER_CODEC_AV1;
capCodecConfigData.Profile.pAV1Profile = &av1prof;
capCodecConfigData.Profile.DataSize = sizeof(av1prof);
D3D12_VIDEO_ENCODER_AV1_CODEC_CONFIGURATION_SUPPORT av1CodecSupport = { };
capCodecConfigData.CodecSupportLimits.pAV1Support = &av1CodecSupport;
capCodecConfigData.CodecSupportLimits.DataSize = sizeof(av1CodecSupport);
HRESULT hr = pD3D12VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT, &capCodecConfigData, sizeof(capCodecConfigData));
if (FAILED(hr)) {
debug_printf("CheckFeatureSupport D3D12_FEATURE_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT failed with HR %x\n", hr);
return false;
} else if (!capCodecConfigData.IsSupported) {
debug_printf("CheckFeatureSupport D3D12_FEATURE_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT IsSupported is false\n");
return false;
}
av1Config.OrderHintBitsMinus1 = 7;
av1Config.FeatureFlags = av1CodecSupport.RequiredFeatureFlags;
capEncoderSupportData1.CodecConfiguration.DataSize = sizeof(av1Config);
capEncoderSupportData1.CodecConfiguration.pAV1Config = &av1Config;
capEncoderSupportData1.SubregionFrameEncoding = requestedSliceMode;
capEncoderSupportData1.SubregionFrameEncodingData.DataSize = sizeof(tileData);
capEncoderSupportData1.SubregionFrameEncodingData.pTilesPartition_AV1 = &tileData;
} break;
#endif
default:
{
unreachable("Unsupported D3D12_VIDEO_ENCODER_CODEC");
@ -463,25 +596,57 @@ d3d12_video_encode_max_supported_slices(const D3D12_VIDEO_ENCODER_CODEC &argTarg
}
// prepare inout storage for the resolution dependent result.
D3D12_FEATURE_DATA_VIDEO_ENCODER_RESOLUTION_SUPPORT_LIMITS resolutionDepCaps = {};
capEncoderSupportData.pResolutionDependentSupport = &resolutionDepCaps;
resolutionDepCaps = {};
capEncoderSupportData1.pResolutionDependentSupport = &resolutionDepCaps;
HRESULT hr = pD3D12VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_ENCODER_SUPPORT,
&capEncoderSupportData,
sizeof(capEncoderSupportData));
HRESULT hr = pD3D12VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_ENCODER_SUPPORT1,
&capEncoderSupportData1,
sizeof(capEncoderSupportData1));
if (FAILED(hr)) {
debug_printf("CheckFeatureSupport failed with HR %x\n", hr);
return false;
} else {
bool configSupported =
(((capEncoderSupportData.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_GENERAL_SUPPORT_OK) != 0) &&
(capEncoderSupportData.ValidationFlags == D3D12_VIDEO_ENCODER_VALIDATION_FLAG_NONE));
debug_printf("CheckFeatureSupport D3D12_FEATURE_VIDEO_ENCODER_SUPPORT1 failed with HR %x\n", hr);
debug_printf("Falling back to check previous query version D3D12_FEATURE_VIDEO_ENCODER_SUPPORT...\n");
outMaxSlices = resolutionDepCaps.MaxSubregionsNumber;
return configSupported;
// D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT1 extends D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT
// in a binary compatible way, so just cast it and try with the older query D3D12_FEATURE_VIDEO_ENCODER_SUPPORT
D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT * casted_down_cap_data = reinterpret_cast<D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT*>(&capEncoderSupportData1);
hr = pD3D12VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_ENCODER_SUPPORT,
casted_down_cap_data,
sizeof(D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT));
if (FAILED(hr)) {
debug_printf("CheckFeatureSupport D3D12_FEATURE_VIDEO_ENCODER_SUPPORT failed with HR %x\n", hr);
return false;
}
}
bool configSupported =
(((capEncoderSupportData1.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_GENERAL_SUPPORT_OK) != 0) &&
(capEncoderSupportData1.ValidationFlags == D3D12_VIDEO_ENCODER_VALIDATION_FLAG_NONE));
return configSupported;
}
#if D3D12_PREVIEW_SDK_VERSION >= 711
bool
static d3d12_video_encode_get_av1_codec_support ( const D3D12_VIDEO_ENCODER_CODEC &argCodec,
const D3D12_VIDEO_ENCODER_PROFILE_DESC &argTargetProfile,
ID3D12VideoDevice3 *pD3D12VideoDevice,
D3D12_VIDEO_ENCODER_AV1_CODEC_CONFIGURATION_SUPPORT &av1Support)
{
D3D12_FEATURE_DATA_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT capCodecConfigData = { };
capCodecConfigData.NodeIndex = 0;
capCodecConfigData.Codec = D3D12_VIDEO_ENCODER_CODEC_AV1;
capCodecConfigData.Profile = argTargetProfile;
capCodecConfigData.CodecSupportLimits.pAV1Support = &av1Support;
capCodecConfigData.CodecSupportLimits.DataSize = sizeof(D3D12_VIDEO_ENCODER_AV1_CODEC_CONFIGURATION_SUPPORT);
if(SUCCEEDED(pD3D12VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT, &capCodecConfigData, sizeof(capCodecConfigData)))
&& capCodecConfigData.IsSupported) {
return true;
}
memset(&av1Support, 0, sizeof(D3D12_VIDEO_ENCODER_AV1_CODEC_CONFIGURATION_SUPPORT));
return false;
}
#endif
bool
static d3d12_video_encode_get_hevc_codec_support ( const D3D12_VIDEO_ENCODER_CODEC &argCodec,
@ -567,7 +732,8 @@ d3d12_has_video_encode_support(struct pipe_screen *pscreen,
uint32_t &maxSlices,
uint32_t &supportedSliceStructures,
uint32_t &maxReferencesPerFrame,
struct d3d12_encode_codec_support& codecSupport)
struct d3d12_encode_codec_support& codecSupport,
uint32_t &isRCMaxFrameSizeSupported)
{
ComPtr<ID3D12VideoDevice3> spD3D12VideoDevice;
struct d3d12_screen *pD3D12Screen = (struct d3d12_screen *) pscreen;
@ -628,15 +794,24 @@ d3d12_has_video_encode_support(struct pipe_screen *pscreen,
profile,
level,
spD3D12VideoDevice.Get());
D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT1 capEncoderSupportData1;
D3D12_FEATURE_DATA_VIDEO_ENCODER_RESOLUTION_SUPPORT_LIMITS resolutionDepCaps;
supportsProfile = supportsProfile && d3d12_video_encode_support_caps(codecDesc,
maxRes,
encodeFormat,
spD3D12VideoDevice.Get(),
d3d12_codec_support,
(supportedSliceStructures == PIPE_VIDEO_CAP_SLICE_STRUCTURE_NONE) ?
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME
: D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_PARTITIONING_SUBREGIONS_PER_FRAME,
capEncoderSupportData1,
resolutionDepCaps);
if (supportedSliceStructures == PIPE_VIDEO_CAP_SLICE_STRUCTURE_NONE)
maxSlices = 0;
else
supportsProfile = supportsProfile && d3d12_video_encode_max_supported_slices(codecDesc,
maxRes,
encodeFormat,
maxSlices,
spD3D12VideoDevice.Get(),
d3d12_codec_support);
maxSlices = resolutionDepCaps.MaxSubregionsNumber;
isRCMaxFrameSizeSupported = ((capEncoderSupportData1.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_MAX_FRAME_SIZE_AVAILABLE) != 0) ? 1 : 0;
maxReferencesPerFrame =
d3d12_video_encode_supported_references_per_frame_structures(codecDesc,
profile,
@ -782,18 +957,275 @@ d3d12_has_video_encode_support(struct pipe_screen *pscreen,
supportsProfile = supportsProfile &&
d3d12_video_encode_supported_resolution_range(codecDesc, minRes, maxRes, spD3D12VideoDevice.Get());
D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT1 capEncoderSupportData1;
D3D12_FEATURE_DATA_VIDEO_ENCODER_RESOLUTION_SUPPORT_LIMITS resolutionDepCaps;
supportsProfile = supportsProfile && d3d12_video_encode_support_caps(codecDesc,
maxRes,
encodeFormat,
spD3D12VideoDevice.Get(),
d3d12_codec_support,
(supportedSliceStructures == PIPE_VIDEO_CAP_SLICE_STRUCTURE_NONE) ?
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME
: D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_PARTITIONING_SUBREGIONS_PER_FRAME,
capEncoderSupportData1,
resolutionDepCaps);
if (supportedSliceStructures == PIPE_VIDEO_CAP_SLICE_STRUCTURE_NONE)
maxSlices = 0;
else
supportsProfile = supportsProfile && d3d12_video_encode_max_supported_slices(codecDesc,
maxRes,
encodeFormat,
maxSlices,
spD3D12VideoDevice.Get(),
d3d12_codec_support);
maxSlices = resolutionDepCaps.MaxSubregionsNumber;
isRCMaxFrameSizeSupported = ((capEncoderSupportData1.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_MAX_FRAME_SIZE_AVAILABLE) != 0) ? 1 : 0;
}
}
} break;
#if D3D12_PREVIEW_SDK_VERSION >= 711
case PIPE_VIDEO_PROFILE_AV1_MAIN:
{
D3D12_VIDEO_ENCODER_PROFILE_DESC profDesc = {};
D3D12_VIDEO_ENCODER_AV1_PROFILE profAV1 =
d3d12_video_encoder_convert_profile_to_d3d12_enc_profile_av1(profile);
profDesc.DataSize = sizeof(profAV1);
profDesc.pAV1Profile = &profAV1;
D3D12_VIDEO_ENCODER_CODEC codecDesc = d3d12_video_encoder_convert_codec_to_d3d12_enc_codec(profile);
D3D12_VIDEO_ENCODER_AV1_LEVEL_TIER_CONSTRAINTS minLvlSettingAV1 = { };
D3D12_VIDEO_ENCODER_AV1_LEVEL_TIER_CONSTRAINTS maxLvlSettingAV1 = { };
D3D12_VIDEO_ENCODER_LEVEL_SETTING minLvl = {};
D3D12_VIDEO_ENCODER_LEVEL_SETTING maxLvl = {};
minLvl.pAV1LevelSetting = &minLvlSettingAV1;
minLvl.DataSize = sizeof(minLvlSettingAV1);
maxLvl.pAV1LevelSetting = &maxLvlSettingAV1;
maxLvl.DataSize = sizeof(maxLvlSettingAV1);
if (d3d12_video_encode_max_supported_level_for_profile(codecDesc,
profDesc,
minLvl,
maxLvl,
spD3D12VideoDevice.Get())) {
d3d12_video_encoder_convert_d3d12_to_spec_level_av1(maxLvlSettingAV1.Level, maxLvlSpec);
D3D12_VIDEO_ENCODER_PROFILE_DESC d3d12_profile;
d3d12_profile.pAV1Profile = &profAV1;
d3d12_profile.DataSize = sizeof(profAV1);
maxReferencesPerFrame =
d3d12_video_encode_supported_references_per_frame_structures(codecDesc,
d3d12_profile,
spD3D12VideoDevice.Get());
supportsProfile = d3d12_video_encode_get_av1_codec_support(codecDesc,
profDesc,
spD3D12VideoDevice.Get(),
codecSupport.av1_support.d3d12_caps);
if (supportsProfile) {
d3d12_codec_support.DataSize = sizeof(codecSupport.av1_support.d3d12_caps);
d3d12_codec_support.pAV1Support = &codecSupport.av1_support.d3d12_caps;
if ((codecSupport.av1_support.d3d12_caps.SupportedFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_128x128_SUPERBLOCK) != 0)
codecSupport.av1_support.features.bits.support_128x128_superblock = PIPE_ENC_FEATURE_SUPPORTED;
if ((codecSupport.av1_support.d3d12_caps.RequiredFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_128x128_SUPERBLOCK) != 0)
codecSupport.av1_support.features.bits.support_128x128_superblock = (PIPE_ENC_FEATURE_SUPPORTED | PIPE_ENC_FEATURE_REQUIRED);
if ((codecSupport.av1_support.d3d12_caps.SupportedFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_FILTER_INTRA) != 0)
codecSupport.av1_support.features.bits.support_filter_intra = PIPE_ENC_FEATURE_SUPPORTED;
if ((codecSupport.av1_support.d3d12_caps.RequiredFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_FILTER_INTRA) != 0)
codecSupport.av1_support.features.bits.support_filter_intra = (PIPE_ENC_FEATURE_SUPPORTED | PIPE_ENC_FEATURE_REQUIRED);
if ((codecSupport.av1_support.d3d12_caps.SupportedFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTRA_EDGE_FILTER) != 0)
codecSupport.av1_support.features.bits.support_intra_edge_filter = PIPE_ENC_FEATURE_SUPPORTED;
if ((codecSupport.av1_support.d3d12_caps.RequiredFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTRA_EDGE_FILTER) != 0)
codecSupport.av1_support.features.bits.support_intra_edge_filter = (PIPE_ENC_FEATURE_SUPPORTED | PIPE_ENC_FEATURE_REQUIRED);
if ((codecSupport.av1_support.d3d12_caps.SupportedFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTERINTRA_COMPOUND) != 0)
codecSupport.av1_support.features.bits.support_interintra_compound = PIPE_ENC_FEATURE_SUPPORTED;
if ((codecSupport.av1_support.d3d12_caps.RequiredFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTERINTRA_COMPOUND) != 0)
codecSupport.av1_support.features.bits.support_interintra_compound = (PIPE_ENC_FEATURE_SUPPORTED | PIPE_ENC_FEATURE_REQUIRED);
if ((codecSupport.av1_support.d3d12_caps.SupportedFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_MASKED_COMPOUND) != 0)
codecSupport.av1_support.features.bits.support_masked_compound = PIPE_ENC_FEATURE_SUPPORTED;
if ((codecSupport.av1_support.d3d12_caps.RequiredFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_MASKED_COMPOUND) != 0)
codecSupport.av1_support.features.bits.support_masked_compound = (PIPE_ENC_FEATURE_SUPPORTED | PIPE_ENC_FEATURE_REQUIRED);
if ((codecSupport.av1_support.d3d12_caps.SupportedFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_WARPED_MOTION) != 0)
codecSupport.av1_support.features.bits.support_warped_motion = PIPE_ENC_FEATURE_SUPPORTED;
if ((codecSupport.av1_support.d3d12_caps.RequiredFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_WARPED_MOTION) != 0)
codecSupport.av1_support.features.bits.support_warped_motion = (PIPE_ENC_FEATURE_SUPPORTED | PIPE_ENC_FEATURE_REQUIRED);
if ((codecSupport.av1_support.d3d12_caps.SupportedFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_PALETTE_ENCODING) != 0)
codecSupport.av1_support.features.bits.support_palette_mode = PIPE_ENC_FEATURE_SUPPORTED;
if ((codecSupport.av1_support.d3d12_caps.RequiredFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_PALETTE_ENCODING) != 0)
codecSupport.av1_support.features.bits.support_palette_mode = (PIPE_ENC_FEATURE_SUPPORTED | PIPE_ENC_FEATURE_REQUIRED);
if ((codecSupport.av1_support.d3d12_caps.SupportedFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_DUAL_FILTER) != 0)
codecSupport.av1_support.features.bits.support_dual_filter = PIPE_ENC_FEATURE_SUPPORTED;
if ((codecSupport.av1_support.d3d12_caps.RequiredFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_DUAL_FILTER) != 0)
codecSupport.av1_support.features.bits.support_dual_filter = (PIPE_ENC_FEATURE_SUPPORTED | PIPE_ENC_FEATURE_REQUIRED);
if ((codecSupport.av1_support.d3d12_caps.SupportedFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_JNT_COMP) != 0)
codecSupport.av1_support.features.bits.support_jnt_comp = PIPE_ENC_FEATURE_SUPPORTED;
if ((codecSupport.av1_support.d3d12_caps.RequiredFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_JNT_COMP) != 0)
codecSupport.av1_support.features.bits.support_jnt_comp = (PIPE_ENC_FEATURE_SUPPORTED | PIPE_ENC_FEATURE_REQUIRED);
if ((codecSupport.av1_support.d3d12_caps.SupportedFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_FRAME_REFERENCE_MOTION_VECTORS) != 0)
codecSupport.av1_support.features.bits.support_ref_frame_mvs = PIPE_ENC_FEATURE_SUPPORTED;
if ((codecSupport.av1_support.d3d12_caps.RequiredFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_FRAME_REFERENCE_MOTION_VECTORS) != 0)
codecSupport.av1_support.features.bits.support_ref_frame_mvs = (PIPE_ENC_FEATURE_SUPPORTED | PIPE_ENC_FEATURE_REQUIRED);
if ((codecSupport.av1_support.d3d12_caps.SupportedFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_SUPER_RESOLUTION) != 0)
codecSupport.av1_support.features.bits.support_superres = PIPE_ENC_FEATURE_SUPPORTED;
if ((codecSupport.av1_support.d3d12_caps.RequiredFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_SUPER_RESOLUTION) != 0)
codecSupport.av1_support.features.bits.support_superres = (PIPE_ENC_FEATURE_SUPPORTED | PIPE_ENC_FEATURE_REQUIRED);
if ((codecSupport.av1_support.d3d12_caps.SupportedFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_LOOP_RESTORATION_FILTER) != 0)
codecSupport.av1_support.features.bits.support_restoration = PIPE_ENC_FEATURE_SUPPORTED;
if ((codecSupport.av1_support.d3d12_caps.RequiredFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_LOOP_RESTORATION_FILTER) != 0)
codecSupport.av1_support.features.bits.support_restoration = (PIPE_ENC_FEATURE_SUPPORTED | PIPE_ENC_FEATURE_REQUIRED);
if ((codecSupport.av1_support.d3d12_caps.SupportedFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTRA_BLOCK_COPY) != 0)
codecSupport.av1_support.features.bits.support_allow_intrabc = PIPE_ENC_FEATURE_SUPPORTED;
if ((codecSupport.av1_support.d3d12_caps.RequiredFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTRA_BLOCK_COPY) != 0)
codecSupport.av1_support.features.bits.support_allow_intrabc = (PIPE_ENC_FEATURE_SUPPORTED | PIPE_ENC_FEATURE_REQUIRED);
if ((codecSupport.av1_support.d3d12_caps.SupportedFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_CDEF_FILTERING) != 0)
codecSupport.av1_support.features.bits.support_cdef_channel_strength = PIPE_ENC_FEATURE_SUPPORTED;
if ((codecSupport.av1_support.d3d12_caps.RequiredFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_CDEF_FILTERING) != 0)
codecSupport.av1_support.features.bits.support_cdef_channel_strength = (PIPE_ENC_FEATURE_SUPPORTED | PIPE_ENC_FEATURE_REQUIRED);
// pipe_av1_enc_cap_features_ext1
if ((codecSupport.av1_support.d3d12_caps.SupportedInterpolationFilters & D3D12_VIDEO_ENCODER_AV1_INTERPOLATION_FILTERS_FLAG_EIGHTTAP) != 0)
codecSupport.av1_support.features_ext1.bits.interpolation_filter |= PIPE_VIDEO_CAP_ENC_AV1_INTERPOLATION_FILTER_EIGHT_TAP;
if ((codecSupport.av1_support.d3d12_caps.SupportedInterpolationFilters & D3D12_VIDEO_ENCODER_AV1_INTERPOLATION_FILTERS_FLAG_EIGHTTAP_SMOOTH) != 0)
codecSupport.av1_support.features_ext1.bits.interpolation_filter |= PIPE_VIDEO_CAP_ENC_AV1_INTERPOLATION_FILTER_EIGHT_TAP_SMOOTH;
if ((codecSupport.av1_support.d3d12_caps.SupportedInterpolationFilters & D3D12_VIDEO_ENCODER_AV1_INTERPOLATION_FILTERS_FLAG_EIGHTTAP_SHARP) != 0)
codecSupport.av1_support.features_ext1.bits.interpolation_filter |= PIPE_VIDEO_CAP_ENC_AV1_INTERPOLATION_FILTER_EIGHT_TAP_SHARP;
if ((codecSupport.av1_support.d3d12_caps.SupportedInterpolationFilters & D3D12_VIDEO_ENCODER_AV1_INTERPOLATION_FILTERS_FLAG_BILINEAR) != 0)
codecSupport.av1_support.features_ext1.bits.interpolation_filter |= PIPE_VIDEO_CAP_ENC_AV1_INTERPOLATION_FILTER_BILINEAR;
if ((codecSupport.av1_support.d3d12_caps.SupportedInterpolationFilters & D3D12_VIDEO_ENCODER_AV1_INTERPOLATION_FILTERS_FLAG_SWITCHABLE) != 0)
codecSupport.av1_support.features_ext1.bits.interpolation_filter |= PIPE_VIDEO_CAP_ENC_AV1_INTERPOLATION_FILTER_SWITCHABLE;
if ((codecSupport.av1_support.d3d12_caps.SupportedSegmentationModes & D3D12_VIDEO_ENCODER_AV1_SEGMENTATION_MODE_FLAG_DISABLED) != 0)
codecSupport.av1_support.features_ext1.bits.segment_feature_support = 0;
if ((codecSupport.av1_support.d3d12_caps.SupportedSegmentationModes & D3D12_VIDEO_ENCODER_AV1_SEGMENTATION_MODE_FLAG_ALT_Q) != 0)
codecSupport.av1_support.features_ext1.bits.segment_feature_support |= 0x1;
if ((codecSupport.av1_support.d3d12_caps.SupportedSegmentationModes & D3D12_VIDEO_ENCODER_AV1_SEGMENTATION_MODE_FLAG_ALT_LF_Y_V) != 0)
codecSupport.av1_support.features_ext1.bits.segment_feature_support |= 0x2;
if ((codecSupport.av1_support.d3d12_caps.SupportedSegmentationModes & D3D12_VIDEO_ENCODER_AV1_SEGMENTATION_MODE_FLAG_ALT_LF_Y_H) != 0)
codecSupport.av1_support.features_ext1.bits.segment_feature_support |= 0x4;
if ((codecSupport.av1_support.d3d12_caps.SupportedSegmentationModes & D3D12_VIDEO_ENCODER_AV1_SEGMENTATION_MODE_FLAG_ALT_LF_U) != 0)
codecSupport.av1_support.features_ext1.bits.segment_feature_support |= 0x8;
if ((codecSupport.av1_support.d3d12_caps.SupportedSegmentationModes & D3D12_VIDEO_ENCODER_AV1_SEGMENTATION_MODE_FLAG_ALT_LF_V) != 0)
codecSupport.av1_support.features_ext1.bits.segment_feature_support |= 0x10;
if ((codecSupport.av1_support.d3d12_caps.SupportedSegmentationModes & D3D12_VIDEO_ENCODER_AV1_SEGMENTATION_MODE_FLAG_REF_FRAME) != 0)
codecSupport.av1_support.features_ext1.bits.segment_feature_support |= 0x20;
if ((codecSupport.av1_support.d3d12_caps.SupportedSegmentationModes & D3D12_VIDEO_ENCODER_AV1_SEGMENTATION_MODE_FLAG_ALT_SKIP) != 0)
codecSupport.av1_support.features_ext1.bits.segment_feature_support |= 0x40;
if ((codecSupport.av1_support.d3d12_caps.SupportedSegmentationModes & D3D12_VIDEO_ENCODER_AV1_SEGMENTATION_MODE_FLAG_ALT_GLOBALMV) != 0)
codecSupport.av1_support.features_ext1.bits.segment_feature_support |= 0x80;
// pipe_av1_enc_cap_features_ext2
codecSupport.av1_support.features_ext2.bits.obu_size_bytes_minus1 = 4 - 1; // Default 4 bytes (reported minus 1)
// tx_mode_support query cap
// Disregarding the VA mask definition for tx_mode_support, some apps use it as directly the value to be used
// in VAEncPictureParameterBufferAV1.mode_control_flags.bits.tx_mode
// So only return one based on preference order / driver support
{
// Check the mode is supported for all frame types, then report by preference priority
bool tx_mode_select_supported = true;
for(uint8_t i = D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_KEY_FRAME; i <= D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_SWITCH_FRAME; i++)
tx_mode_select_supported &= ((codecSupport.av1_support.d3d12_caps.SupportedTxModes[i] & D3D12_VIDEO_ENCODER_AV1_TX_MODE_FLAG_SELECT) != 0);
if (tx_mode_select_supported) {
// Workaround for apps consuming the tx_mode_support cap directly as the tx_mode param to be used
codecSupport.av1_support.features_ext2.bits.tx_mode_support = std::log2(static_cast<uint32_t>(PIPE_VIDEO_CAP_ENC_AV1_TX_MODE_SELECT));
} else {
bool tx_mode_largest_supported = true;
for(uint8_t i = D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_KEY_FRAME; i <= D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_SWITCH_FRAME; i++)
tx_mode_largest_supported &= ((codecSupport.av1_support.d3d12_caps.SupportedTxModes[i] & D3D12_VIDEO_ENCODER_AV1_TX_MODE_FLAG_LARGEST) != 0);
if (tx_mode_largest_supported) {
// Workaround for apps consuming the tx_mode_support cap directly as the tx_mode param to be used.
codecSupport.av1_support.features_ext2.bits.tx_mode_support = std::log2(static_cast<uint32_t>(PIPE_VIDEO_CAP_ENC_AV1_TX_MODE_LARGEST));
} else {
bool tx_mode_only_4x4_supported = true;
for(uint8_t i = D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_KEY_FRAME; i <= D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_SWITCH_FRAME; i++)
tx_mode_only_4x4_supported &= ((codecSupport.av1_support.d3d12_caps.SupportedTxModes[i] & D3D12_VIDEO_ENCODER_AV1_TX_MODE_FLAG_ONLY4x4) != 0);
if (tx_mode_only_4x4_supported) {
// Workaround for apps consuming the tx_mode_support cap directly as the tx_mode param to be used.
codecSupport.av1_support.features_ext2.bits.tx_mode_support = std::log2(static_cast<uint32_t>(PIPE_VIDEO_CAP_ENC_AV1_TX_MODE_ONLY_4X4));
} else {
assert(false); // As per d3d12 spec, driver must support at least one default mode for all frame types
}
}
}
}
supportsProfile = supportsProfile &&
d3d12_video_encode_supported_resolution_range(codecDesc, minRes, maxRes, spD3D12VideoDevice.Get());
uint32_t tile_size_bytes_minus1 = 0;
d3d12_video_encode_supported_tile_structures(codecDesc,
profDesc,
maxLvl,
spD3D12VideoDevice.Get(),
maxRes,
supportedSliceStructures, // out
tile_size_bytes_minus1 // out
);
// Cannot pass pipe 2 bit-field as reference, use aux variable instead.
codecSupport.av1_support.features_ext2.bits.tile_size_bytes_minus1 = tile_size_bytes_minus1;
DXGI_FORMAT encodeFormat = d3d12_convert_pipe_video_profile_to_dxgi_format(profile);
D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT1 capEncoderSupportData1;
D3D12_FEATURE_DATA_VIDEO_ENCODER_RESOLUTION_SUPPORT_LIMITS resolutionDepCaps;
supportsProfile = supportsProfile && d3d12_video_encode_support_caps(codecDesc,
maxRes,
encodeFormat,
spD3D12VideoDevice.Get(),
d3d12_codec_support,
(supportedSliceStructures == PIPE_VIDEO_CAP_SLICE_STRUCTURE_NONE) ?
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME
: D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_GRID_PARTITION,
capEncoderSupportData1,
resolutionDepCaps);
if (supportedSliceStructures == PIPE_VIDEO_CAP_SLICE_STRUCTURE_NONE)
maxSlices = 0;
else
maxSlices = resolutionDepCaps.MaxSubregionsNumber;
codecSupport.av1_support.features_ext2.bits.max_tile_num_minus1 = maxSlices - 1;
isRCMaxFrameSizeSupported = ((capEncoderSupportData1.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_MAX_FRAME_SIZE_AVAILABLE) != 0) ? 1 : 0;
}
}
} break;
#endif
default:
supportsProfile = false;
}
@ -1087,6 +1519,7 @@ d3d12_screen_get_video_param_encode(struct pipe_screen *pscreen,
uint32_t maxSlices = 0u;
uint32_t supportedSliceStructures = 0u;
uint32_t maxReferencesPerFrame = 0u;
uint32_t isRCMaxFrameSizeSupported = 0u;
struct d3d12_encode_codec_support codec_specific_support;
memset(&codec_specific_support, 0, sizeof(codec_specific_support));
switch (param) {
@ -1108,6 +1541,11 @@ d3d12_screen_get_video_param_encode(struct pipe_screen *pscreen,
case PIPE_VIDEO_CAP_ENC_HEVC_FEATURE_FLAGS:
case PIPE_VIDEO_CAP_ENC_HEVC_BLOCK_SIZES:
case PIPE_VIDEO_CAP_ENC_HEVC_PREDICTION_DIRECTION:
case PIPE_VIDEO_CAP_ENC_AV1_FEATURE:
case PIPE_VIDEO_CAP_ENC_AV1_FEATURE_EXT1:
case PIPE_VIDEO_CAP_ENC_AV1_FEATURE_EXT2:
case PIPE_VIDEO_CAP_ENC_SUPPORTS_TILE:
case PIPE_VIDEO_CAP_ENC_SUPPORTS_MAX_FRAME_SIZE:
{
if (d3d12_has_video_encode_support(pscreen,
profile,
@ -1117,7 +1555,8 @@ d3d12_screen_get_video_param_encode(struct pipe_screen *pscreen,
maxSlices,
supportedSliceStructures,
maxReferencesPerFrame,
codec_specific_support)) {
codec_specific_support,
isRCMaxFrameSizeSupported)) {
DXGI_FORMAT format = d3d12_convert_pipe_video_profile_to_dxgi_format(profile);
auto pipeFmt = d3d12_get_pipe_format(format);
@ -1141,6 +1580,8 @@ d3d12_screen_get_video_param_encode(struct pipe_screen *pscreen,
return supportedSliceStructures;
} else if (param == PIPE_VIDEO_CAP_ENC_MAX_REFERENCES_PER_FRAME) {
return maxReferencesPerFrame;
} else if (param == PIPE_VIDEO_CAP_ENC_SUPPORTS_MAX_FRAME_SIZE) {
return isRCMaxFrameSizeSupported;
} else if (param == PIPE_VIDEO_CAP_ENC_HEVC_FEATURE_FLAGS) {
/* get_video_param sets hevc_features.bits.config_supported = 1
to distinguish between supported cap with all bits off and unsupported by driver
@ -1154,8 +1595,21 @@ d3d12_screen_get_video_param_encode(struct pipe_screen *pscreen,
*/
return codec_specific_support.hevc_support.hevc_block_sizes.value;
} else if (param == PIPE_VIDEO_CAP_ENC_HEVC_PREDICTION_DIRECTION) {
return codec_specific_support.hevc_support.prediction_direction;
if (PIPE_VIDEO_FORMAT_HEVC == u_reduce_video_profile(profile))
return codec_specific_support.hevc_support.prediction_direction;
return 0;
}
#if D3D12_PREVIEW_SDK_VERSION >= 711
else if (param == PIPE_VIDEO_CAP_ENC_AV1_FEATURE) {
return codec_specific_support.av1_support.features.value;
} else if (param == PIPE_VIDEO_CAP_ENC_AV1_FEATURE_EXT1) {
return codec_specific_support.av1_support.features_ext1.value;
} else if (param == PIPE_VIDEO_CAP_ENC_AV1_FEATURE_EXT2) {
return codec_specific_support.av1_support.features_ext2.value;
} else if(param == PIPE_VIDEO_CAP_ENC_SUPPORTS_TILE) {
return (profile == PIPE_VIDEO_PROFILE_AV1_MAIN) && (maxSlices != 0);
}
#endif
}
}
return 0;
@ -1170,8 +1624,6 @@ d3d12_screen_get_video_param_encode(struct pipe_screen *pscreen,
return true;
case PIPE_VIDEO_CAP_SUPPORTS_CONTIGUOUS_PLANES_MAP:
return true;
case PIPE_VIDEO_CAP_ENC_SUPPORTS_MAX_FRAME_SIZE:
return true;
case PIPE_VIDEO_CAP_ENC_QUALITY_LEVEL:
/* VAEncMiscParameterBufferQualityLevel */
return 1;
@ -1228,6 +1680,9 @@ is_d3d12_video_encode_format_supported(struct pipe_screen *screen,
{
D3D12_VIDEO_ENCODER_PROFILE_H264 profH264 = {};
D3D12_VIDEO_ENCODER_PROFILE_HEVC profHEVC = {};
#if D3D12_PREVIEW_SDK_VERSION >= 711
D3D12_VIDEO_ENCODER_AV1_PROFILE profAV1 = {};
#endif
D3D12_FEATURE_DATA_VIDEO_ENCODER_INPUT_FORMAT capDataFmt = {};
capDataFmt.NodeIndex = 0;
capDataFmt.Codec = d3d12_video_encoder_convert_codec_to_d3d12_enc_codec(profile);
@ -1245,6 +1700,14 @@ is_d3d12_video_encode_format_supported(struct pipe_screen *screen,
capDataFmt.Profile.DataSize = sizeof(profHEVC);
capDataFmt.Profile.pHEVCProfile = &profHEVC;
} break;
#if D3D12_PREVIEW_SDK_VERSION >= 711
case PIPE_VIDEO_FORMAT_AV1:
{
profAV1 = d3d12_video_encoder_convert_profile_to_d3d12_enc_profile_av1(profile);
capDataFmt.Profile.DataSize = sizeof(profAV1);
capDataFmt.Profile.pAV1Profile = &profAV1;
} break;
#endif
default:
{
unreachable("Unsupported pipe_video_format");

View file

@ -37,6 +37,12 @@
#include <directx/d3d12video.h>
#include <dxguids/dxguids.h>
#if D3D12_PREVIEW_SDK_VERSION >= 711
#else // leave this way so #if D3D12_PREVIEW_SDK_VERSION >= 711 is easily searchable later
using D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT1 = D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT;
constexpr D3D12_FEATURE_VIDEO D3D12_FEATURE_VIDEO_ENCODER_SUPPORT1 = D3D12_FEATURE_VIDEO_ENCODER_SUPPORT;
#endif
#include <wrl/client.h>
using Microsoft::WRL::ComPtr;
@ -81,6 +87,18 @@ const uint64_t D3D12_VIDEO_ENC_METADATA_BUFFERS_COUNT = debug_get_num_option("D3
constexpr unsigned int D3D12_VIDEO_H264_MB_IN_PIXELS = 16;
constexpr size_t D3D12_DEFAULT_COMPBIT_STAGING_SIZE = (1024 /*1K*/ * 1024/*1MB*/) * 8/*8 MB*/; // 8MB
/* If enabled, the D3D12 AV1 encoder will use always ...CONFIGURABLE_GRID_PARTITION mode */
/* If disabled, the D3D12 AV1 encoder will try to use ...UNIFORM_GRID_PARTITION first and then fallback to ...CONFIGURABLE_GRID_PARTITION if not possible */
const bool D3D12_VIDEO_FORCE_TILE_MODE = debug_get_bool_option("D3D12_VIDEO_FORCE_TILE_MODE", false);
/**
* If enabled, the D3D12 AV1 encoder will insert a OBU_FRAME_HEADER with show_existing_frame = 1 after the current frame to show a previous
* show_frame = 0 encoded frame as reference and used by the current frame
*/
const bool D3D12_VIDEO_AV1_INSERT_SHOW_EXISTING_FRAME_HEADER = debug_get_bool_option("D3D12_VIDEO_AV1_INSERT_SHOW_EXISTING_FRAME_HEADER", false);
enum d3d12_video_decode_config_specific_flags
{
d3d12_video_decode_config_specific_flag_none = 0,
@ -128,10 +146,25 @@ d3d12_video_encoder_convert_from_d3d12_level_h264(D3D12_VIDEO_ENCODER_LEVELS_H26
void
d3d12_video_encoder_convert_from_d3d12_level_hevc(D3D12_VIDEO_ENCODER_LEVELS_HEVC level12,
uint32_t & specLevel);
#if D3D12_PREVIEW_SDK_VERSION >= 711
void
d3d12_video_encoder_convert_d3d12_to_spec_level_av1(D3D12_VIDEO_ENCODER_AV1_LEVELS level12,
uint32_t & specLevel);
void
d3d12_video_encoder_convert_spec_to_d3d12_level_av1(uint32_t specLevel,
D3D12_VIDEO_ENCODER_AV1_LEVELS& level12);
void
d3d12_video_encoder_convert_spec_to_d3d12_tier_av1(uint32_t specTier,
D3D12_VIDEO_ENCODER_AV1_TIER & tier12);
#endif
D3D12_VIDEO_ENCODER_PROFILE_H264
d3d12_video_encoder_convert_profile_to_d3d12_enc_profile_h264(enum pipe_video_profile profile);
D3D12_VIDEO_ENCODER_PROFILE_HEVC
d3d12_video_encoder_convert_profile_to_d3d12_enc_profile_hevc(enum pipe_video_profile profile);
#if D3D12_PREVIEW_SDK_VERSION >= 711
D3D12_VIDEO_ENCODER_AV1_PROFILE
d3d12_video_encoder_convert_profile_to_d3d12_enc_profile_av1(enum pipe_video_profile profile);
#endif
D3D12_VIDEO_ENCODER_CODEC
d3d12_video_encoder_convert_codec_to_d3d12_enc_codec(enum pipe_video_profile profile);
GUID

View file

@ -70,6 +70,16 @@ if with_gallium_d3d12_video
'd3d12_video_dec_av1.cpp',
'd3d12_video_dec_vp9.cpp',
]
# // TODO: Remove this and #ifdef checks for AV1 encode when header version is release 6xx
if dep_dxheaders.version().version_compare('>= 1.711.3')
files_libd3d12 += [
'd3d12_video_enc_av1.cpp',
'd3d12_video_encoder_references_manager_av1.cpp',
'd3d12_video_encoder_bitstream_builder_av1.cpp',
]
endif
endif
is_xbox = target_machine.system().startswith('Gaming.Xbox')