From 64da736286d0f1cba47ead6d93bb2e7766ef0ad4 Mon Sep 17 00:00:00 2001 From: Sil Vilerino Date: Tue, 20 Dec 2022 13:29:47 -0500 Subject: [PATCH] d3d12: AV1 Encode Reviewed-by: Jesse Natalie Part-of: --- src/gallium/drivers/d3d12/d3d12_video_enc.cpp | 698 ++++- src/gallium/drivers/d3d12/d3d12_video_enc.h | 192 +- .../drivers/d3d12/d3d12_video_enc_av1.cpp | 2695 +++++++++++++++++ .../drivers/d3d12/d3d12_video_enc_av1.h | 77 + .../drivers/d3d12/d3d12_video_enc_h264.cpp | 78 +- .../drivers/d3d12/d3d12_video_enc_hevc.cpp | 71 +- .../d3d12/d3d12_video_encoder_bitstream.cpp | 140 +- .../d3d12/d3d12_video_encoder_bitstream.h | 21 +- ...12_video_encoder_bitstream_builder_av1.cpp | 923 ++++++ ...3d12_video_encoder_bitstream_builder_av1.h | 252 ++ ...2_video_encoder_references_manager_av1.cpp | 431 +++ ...d12_video_encoder_references_manager_av1.h | 79 + .../drivers/d3d12/d3d12_video_screen.cpp | 627 +++- src/gallium/drivers/d3d12/d3d12_video_types.h | 33 + src/gallium/drivers/d3d12/meson.build | 10 + 15 files changed, 6023 insertions(+), 304 deletions(-) create mode 100644 src/gallium/drivers/d3d12/d3d12_video_enc_av1.cpp create mode 100644 src/gallium/drivers/d3d12/d3d12_video_enc_av1.h create mode 100644 src/gallium/drivers/d3d12/d3d12_video_encoder_bitstream_builder_av1.cpp create mode 100644 src/gallium/drivers/d3d12/d3d12_video_encoder_bitstream_builder_av1.h create mode 100644 src/gallium/drivers/d3d12/d3d12_video_encoder_references_manager_av1.cpp create mode 100644 src/gallium/drivers/d3d12/d3d12_video_encoder_references_manager_av1.h diff --git a/src/gallium/drivers/d3d12/d3d12_video_enc.cpp b/src/gallium/drivers/d3d12/d3d12_video_enc.cpp index 8a0024c9bac..5f83e6ac102 100644 --- a/src/gallium/drivers/d3d12/d3d12_video_enc.cpp +++ b/src/gallium/drivers/d3d12/d3d12_video_enc.cpp @@ -32,11 +32,17 @@ #include "d3d12_video_enc.h" #include "d3d12_video_enc_h264.h" #include "d3d12_video_enc_hevc.h" +#if D3D12_PREVIEW_SDK_VERSION >= 711 +#include "d3d12_video_enc_av1.h" +#endif #include "d3d12_video_buffer.h" #include "d3d12_video_texture_array_dpb_manager.h" #include "d3d12_video_array_of_textures_dpb_manager.h" #include "d3d12_video_encoder_references_manager_h264.h" #include "d3d12_video_encoder_references_manager_hevc.h" +#if D3D12_PREVIEW_SDK_VERSION >= 711 +#include "d3d12_video_encoder_references_manager_av1.h" +#endif #include "d3d12_residency.h" #include "vl/vl_video_buffer.h" @@ -61,7 +67,8 @@ d3d12_video_encoder_flush(struct pipe_video_codec *codec) assert(pD3D12Enc->m_spD3D12VideoDevice); assert(pD3D12Enc->m_spEncodeCommandQueue); - // Flush buffer_subdata batch and Wait the m_spEncodeCommandQueue for GPU upload completion + // Flush any work batched (ie. shaders blit on input texture, etc or bitstream headers buffer_subdata batched upload) + // and Wait the m_spEncodeCommandQueue for GPU upload completion // before recording EncodeFrame below. struct pipe_fence_handle *completion_fence = NULL; debug_printf("[d3d12_video_encoder] d3d12_video_encoder_flush - Flushing pD3D12Enc->base.context and GPU sync between Video/Context queues before flushing Video Encode Queue.\n"); @@ -179,7 +186,7 @@ d3d12_video_encoder_sync_completion(struct pipe_video_codec *codec, uint64_t fen d3d12_video_encoder_ensure_fence_finished(codec, fenceValueToWaitOn, timeout_ns); - debug_printf("[d3d12_video_encoder] d3d12_video_encoder_sync_completion - resetting ID3D12CommandAllocator %p...", + debug_printf("[d3d12_video_encoder] d3d12_video_encoder_sync_completion - resetting ID3D12CommandAllocator %p...\n", pD3D12Enc->m_inflightResourcesPool[fenceValueToWaitOn % D3D12_VIDEO_ENC_ASYNC_DEPTH].m_spCommandAllocator.Get()); hr = pD3D12Enc->m_inflightResourcesPool[fenceValueToWaitOn % D3D12_VIDEO_ENC_ASYNC_DEPTH].m_spCommandAllocator->Reset(); if(FAILED(hr)) { @@ -259,7 +266,12 @@ d3d12_video_encoder_update_picparams_tracking(struct d3d12_video_encoder *pD3D12 { d3d12_video_encoder_update_current_frame_pic_params_info_hevc(pD3D12Enc, srcTexture, picture, currentPicParams, bUsedAsReference); } break; - +#if D3D12_PREVIEW_SDK_VERSION >= 711 + case PIPE_VIDEO_FORMAT_AV1: + { + d3d12_video_encoder_update_current_frame_pic_params_info_av1(pD3D12Enc, srcTexture, picture, currentPicParams, bUsedAsReference); + } break; +#endif default: { unreachable("Unsupported pipe_video_format"); @@ -514,7 +526,24 @@ d3d12_video_encoder_create_reference_picture_manager(struct d3d12_video_encoder pD3D12Enc->m_upBitstreamBuilder = std::make_unique(); } break; +#if D3D12_PREVIEW_SDK_VERSION >= 711 + case PIPE_VIDEO_FORMAT_AV1: + { + bool hasInterFrames = + (pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_AV1SequenceStructure.InterFramePeriod > 0) && + ((pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_AV1SequenceStructure.IntraDistance == 0) || + (pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_AV1SequenceStructure.InterFramePeriod < + pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_AV1SequenceStructure.IntraDistance)); + pD3D12Enc->m_upDPBManager = std::make_unique( + hasInterFrames, + *pD3D12Enc->m_upDPBStorageManager + ); + + // We use packed headers and pist encode execution syntax for AV1 + pD3D12Enc->m_upBitstreamBuilder = std::make_unique(); + } break; +#endif default: { unreachable("Unsupported pipe_video_format"); @@ -550,7 +579,19 @@ d3d12_video_encoder_get_current_slice_param_settings(struct d3d12_video_encoder } return subregionData; } break; - +#if D3D12_PREVIEW_SDK_VERSION >= 711 + case PIPE_VIDEO_FORMAT_AV1: + { + D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA subregionData = {}; + if (pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigMode != + D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME) { + subregionData.pTilesPartition_AV1 = + &pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigDesc.m_TilesConfig_AV1.TilesPartition; + subregionData.DataSize = sizeof(D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_TILES); + } + return subregionData; + } break; +#endif default: { unreachable("Unsupported pipe_video_format"); @@ -578,7 +619,15 @@ d3d12_video_encoder_get_current_picture_param_settings(struct d3d12_video_encode curPicParamsData.DataSize = sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderPicParamsDesc.m_HEVCPicData); return curPicParamsData; } break; - +#if D3D12_PREVIEW_SDK_VERSION >= 711 + case PIPE_VIDEO_FORMAT_AV1: + { + D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA curPicParamsData = {}; + curPicParamsData.pAV1PicData = &pD3D12Enc->m_currentEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData; + curPicParamsData.DataSize = sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData); + return curPicParamsData; + } break; +#endif default: { unreachable("Unsupported pipe_video_format"); @@ -623,10 +672,22 @@ d3d12_video_encoder_get_current_rate_control_settings(struct d3d12_video_encoder } break; case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_QVBR: { - curRateControlDesc.ConfigParams.pConfiguration_QVBR = - &pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR; - curRateControlDesc.ConfigParams.DataSize = - sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR); +#if D3D12_PREVIEW_SDK_VERSION >= 711 + if ((curRateControlDesc.Flags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_EXTENDED_QVBR1_SUPPORT) != 0) + { + curRateControlDesc.ConfigParams.pConfiguration_QVBR1 = + &pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1; + curRateControlDesc.ConfigParams.DataSize = + sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1); + } + else +#endif + { + curRateControlDesc.ConfigParams.pConfiguration_QVBR = + &pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR; + curRateControlDesc.ConfigParams.DataSize = + sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR); + } } break; default: { @@ -657,7 +718,15 @@ d3d12_video_encoder_get_current_level_desc(struct d3d12_video_encoder *pD3D12Enc curLevelDesc.DataSize = sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderLevelDesc.m_HEVCLevelSetting); return curLevelDesc; } break; - +#if D3D12_PREVIEW_SDK_VERSION >= 711 + case PIPE_VIDEO_FORMAT_AV1: + { + D3D12_VIDEO_ENCODER_LEVEL_SETTING curLevelDesc = {}; + curLevelDesc.pAV1LevelSetting = &pD3D12Enc->m_currentEncodeConfig.m_encoderLevelDesc.m_AV1LevelSetting; + curLevelDesc.DataSize = sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderLevelDesc.m_AV1LevelSetting); + return curLevelDesc; + } break; +#endif default: { unreachable("Unsupported pipe_video_format"); @@ -665,21 +734,30 @@ d3d12_video_encoder_get_current_level_desc(struct d3d12_video_encoder *pD3D12Enc } } -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) { enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile); switch (codec) { case PIPE_VIDEO_FORMAT_MPEG4_AVC: { - return d3d12_video_encoder_build_codec_headers_h264(pD3D12Enc); - + postEncodeHeadersNeeded = false; + preEncodeGeneratedHeadersByteSize = d3d12_video_encoder_build_codec_headers_h264(pD3D12Enc); } break; case PIPE_VIDEO_FORMAT_HEVC: { - return d3d12_video_encoder_build_codec_headers_hevc(pD3D12Enc); + postEncodeHeadersNeeded = false; + preEncodeGeneratedHeadersByteSize = d3d12_video_encoder_build_codec_headers_hevc(pD3D12Enc); + } break; + case PIPE_VIDEO_FORMAT_AV1: + { + pD3D12Enc->m_BitstreamHeadersBuffer.resize(0); + postEncodeHeadersNeeded = true; + preEncodeGeneratedHeadersByteSize = 0; } break; default: @@ -687,7 +765,6 @@ d3d12_video_encoder_build_codec_headers(struct d3d12_video_encoder *pD3D12Enc) unreachable("Unsupported pipe_video_format"); } break; } - return 0u; } D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE @@ -713,6 +790,16 @@ d3d12_video_encoder_get_current_gop_desc(struct d3d12_video_encoder *pD3D12Enc) return curGOPDesc; } break; +#if D3D12_PREVIEW_SDK_VERSION >= 711 + case PIPE_VIDEO_FORMAT_AV1: + { + D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE curGOPDesc = {}; + curGOPDesc.pAV1SequenceStructure = + &pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_AV1SequenceStructure; + curGOPDesc.DataSize = sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_AV1SequenceStructure); + return curGOPDesc; + } break; +#endif default: { unreachable("Unsupported pipe_video_format"); @@ -743,6 +830,16 @@ d3d12_video_encoder_get_current_codec_config_desc(struct d3d12_video_encoder *pD return codecConfigDesc; } break; +#if D3D12_PREVIEW_SDK_VERSION >= 711 + case PIPE_VIDEO_FORMAT_AV1: + { + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION codecConfigDesc = {}; + codecConfigDesc.pAV1Config = &pD3D12Enc->m_currentEncodeConfig.m_encoderCodecSpecificConfigDesc.m_AV1Config; + codecConfigDesc.DataSize = + sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderCodecSpecificConfigDesc.m_AV1Config); + return codecConfigDesc; + } break; +#endif default: { unreachable("Unsupported pipe_video_format"); @@ -763,6 +860,12 @@ d3d12_video_encoder_get_current_codec(struct d3d12_video_encoder *pD3D12Enc) { 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 default: { unreachable("Unsupported pipe_video_format"); @@ -770,113 +873,228 @@ d3d12_video_encoder_get_current_codec(struct d3d12_video_encoder *pD3D12Enc) } } +static void +d3d12_video_encoder_disable_rc_vbv_sizes(struct D3D12EncodeRateControlState & rcState) +{ + rcState.m_Flags &= ~D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_VBV_SIZES; + switch (rcState.m_Mode) { + case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CBR: + { + rcState.m_Config.m_Configuration_CBR.VBVCapacity = 0; + rcState.m_Config.m_Configuration_CBR.InitialVBVFullness = 0; + } break; + case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_VBR: + { + rcState.m_Config.m_Configuration_VBR.VBVCapacity = 0; + rcState.m_Config.m_Configuration_VBR.InitialVBVFullness = 0; + } break; +#if D3D12_PREVIEW_SDK_VERSION >= 711 + case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_QVBR: + { + if ((rcState.m_Flags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_EXTENDED_QVBR1_SUPPORT) != 0) + { + rcState.m_Config.m_Configuration_QVBR1.VBVCapacity = 0; + rcState.m_Config.m_Configuration_QVBR1.InitialVBVFullness = 0; + } // No VBV Sizes in legacy QVBR + } break; +#endif + default: + { + unreachable("Unsupported D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE for VBV Sizes"); + } break; + } +} + +static void +d3d12_video_encoder_disable_rc_maxframesize(struct D3D12EncodeRateControlState & rcState) +{ + rcState.m_Flags &= ~D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_MAX_FRAME_SIZE; + switch (rcState.m_Mode) { + case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CBR: + { + rcState.m_Config.m_Configuration_CBR.MaxFrameBitSize = 0; + } break; + case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_VBR: + { + rcState.m_Config.m_Configuration_VBR.MaxFrameBitSize = 0; + } break; + case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_QVBR: + { +#if D3D12_PREVIEW_SDK_VERSION >= 711 + if ((rcState.m_Flags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_EXTENDED_QVBR1_SUPPORT) != 0) + { + rcState.m_Config.m_Configuration_QVBR1.MaxFrameBitSize = 0; + } else +#endif + { + rcState.m_Config.m_Configuration_QVBR.MaxFrameBitSize = 0; + } + } break; + default: + { + unreachable("Unsupported D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE for VBV Sizes"); + } break; + } +} + +static void +d3d12_video_encoder_disable_rc_minmaxqp(struct D3D12EncodeRateControlState & rcState) +{ + rcState.m_Flags &= ~D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_QP_RANGE; + switch (rcState.m_Mode) { + case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CBR: + { + rcState.m_Config.m_Configuration_CBR.MinQP = 0; + rcState.m_Config.m_Configuration_CBR.MaxQP = 0; + } break; + case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_VBR: + { + rcState.m_Config.m_Configuration_VBR.MinQP = 0; + rcState.m_Config.m_Configuration_VBR.MaxQP = 0; + } break; + case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_QVBR: + { +#if D3D12_PREVIEW_SDK_VERSION >= 711 + if ((rcState.m_Flags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_EXTENDED_QVBR1_SUPPORT) != 0) + { + rcState.m_Config.m_Configuration_QVBR1.MinQP = 0; + rcState.m_Config.m_Configuration_QVBR1.MaxQP = 0; + } else +#endif + { + rcState.m_Config.m_Configuration_QVBR.MinQP = 0; + rcState.m_Config.m_Configuration_QVBR.MaxQP = 0; + } + } break; + default: + { + unreachable("Unsupported D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE for VBV Sizes"); + } break; + } +} + +#if D3D12_PREVIEW_SDK_VERSION >= 711 +static void +d3d12_video_encoder_disable_rc_qvbr1_extended_to_qvbr_legacy(struct D3D12EncodeRateControlState & rcState) +{ + rcState.m_Flags &= ~D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_EXTENDED_QVBR1_SUPPORT; + // rcState.m_Configuration_QVBR and m_Configuration_QVBR1 are unions, need tmp storage memory + D3D12_VIDEO_ENCODER_RATE_CONTROL_QVBR legacyConf = {}; + legacyConf.InitialQP = rcState.m_Config.m_Configuration_QVBR1.InitialQP; + legacyConf.MinQP = rcState.m_Config.m_Configuration_QVBR1.MinQP; + legacyConf.MaxQP = rcState.m_Config.m_Configuration_QVBR1.MaxQP; + legacyConf.MaxFrameBitSize = rcState.m_Config.m_Configuration_QVBR1.MaxFrameBitSize; + legacyConf.TargetAvgBitRate = rcState.m_Config.m_Configuration_QVBR1.TargetAvgBitRate; + legacyConf.PeakBitRate = rcState.m_Config.m_Configuration_QVBR1.PeakBitRate; + legacyConf.ConstantQualityTarget = rcState.m_Config.m_Configuration_QVBR1.ConstantQualityTarget; + rcState.m_Config.m_Configuration_QVBR = legacyConf; +} +#endif + /// /// Call d3d12_video_encoder_query_d3d12_driver_caps and see if any optional feature requested /// is not supported, disable it, query again until finding a negotiated cap/feature set /// Note that with fallbacks, the upper layer will not get exactly the encoding seetings they requested /// but for very particular settings it's better to continue with warnings than failing the whole encoding process /// -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_negotiate_requested_features_and_d3d12_driver_caps(struct d3d12_video_encoder *pD3D12Enc, D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT1 &capEncoderSupportData1) { /// /// Check for general support /// Check for validation errors (some drivers return general support but also validation errors anyways, work around for those unexpected cases) /// - bool configSupported = d3d12_video_encoder_query_d3d12_driver_caps(pD3D12Enc, /*inout*/ capEncoderSupportData) - && (((capEncoderSupportData.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_GENERAL_SUPPORT_OK) != 0) - && (capEncoderSupportData.ValidationFlags == D3D12_VIDEO_ENCODER_VALIDATION_FLAG_NONE)); + bool configSupported = d3d12_video_encoder_query_d3d12_driver_caps(pD3D12Enc, /*inout*/ capEncoderSupportData1) + && (((capEncoderSupportData1.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_GENERAL_SUPPORT_OK) != 0) + && (capEncoderSupportData1.ValidationFlags == D3D12_VIDEO_ENCODER_VALIDATION_FLAG_NONE)); /// /// If rate control config is not supported, try falling back and check for caps again /// - if ((capEncoderSupportData.ValidationFlags & (D3D12_VIDEO_ENCODER_VALIDATION_FLAG_RATE_CONTROL_CONFIGURATION_NOT_SUPPORTED | D3D12_VIDEO_ENCODER_VALIDATION_FLAG_RATE_CONTROL_MODE_NOT_SUPPORTED)) != 0) { + if ((capEncoderSupportData1.ValidationFlags & (D3D12_VIDEO_ENCODER_VALIDATION_FLAG_RATE_CONTROL_CONFIGURATION_NOT_SUPPORTED | D3D12_VIDEO_ENCODER_VALIDATION_FLAG_RATE_CONTROL_MODE_NOT_SUPPORTED)) != 0) { debug_printf("[d3d12_video_encoder] WARNING: Requested rate control is not supported, trying fallback to unsetting optional features\n"); - bool isRequestingVBVSizesSupported = ((capEncoderSupportData.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_VBV_SIZE_CONFIG_AVAILABLE) != 0); + bool isRequestingVBVSizesSupported = ((capEncoderSupportData1.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_VBV_SIZE_CONFIG_AVAILABLE) != 0); bool isClientRequestingVBVSizes = ((pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags & D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_VBV_SIZES) != 0); if(isClientRequestingVBVSizes && !isRequestingVBVSizesSupported) { - debug_printf("[d3d12_video_encoder] WARNING: Requested D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_VBV_SIZES with VBVCapacity (bits): %" PRIu64 " and InitialVBVFullness (bits) %" PRIu64 " is not supported, will continue encoding unsetting this feature as fallback.\n", - pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR.VBVCapacity, - pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR.InitialVBVFullness); - - pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags &= ~D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_VBV_SIZES; - pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR.VBVCapacity = 0; - pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR.InitialVBVFullness = 0; + debug_printf("[d3d12_video_encoder] WARNING: Requested D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_VBV_SIZES with VBVCapacity and InitialVBVFullness is not supported, will continue encoding unsetting this feature as fallback.\n"); + d3d12_video_encoder_disable_rc_vbv_sizes(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc); } - bool isRequestingPeakFrameSizeSupported = ((capEncoderSupportData.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_MAX_FRAME_SIZE_AVAILABLE) != 0); + bool isRequestingPeakFrameSizeSupported = ((capEncoderSupportData1.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_MAX_FRAME_SIZE_AVAILABLE) != 0); bool isClientRequestingPeakFrameSize = ((pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags & D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_MAX_FRAME_SIZE) != 0); if(isClientRequestingPeakFrameSize && !isRequestingPeakFrameSizeSupported) { - debug_printf("[d3d12_video_encoder] WARNING: Requested D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_MAX_FRAME_SIZE with MaxFrameBitSize %" PRIu64 " but the feature is not supported, will continue encoding unsetting this feature as fallback.\n", - pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_VBR.MaxFrameBitSize); - - 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_VBR.MaxFrameBitSize = 0; + debug_printf("[d3d12_video_encoder] WARNING: Requested D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_MAX_FRAME_SIZE with MaxFrameBitSize but the feature is not supported, will continue encoding unsetting this feature as fallback.\n"); + d3d12_video_encoder_disable_rc_maxframesize(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc); } - bool isRequestingQPRangesSupported = ((capEncoderSupportData.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_ADJUSTABLE_QP_RANGE_AVAILABLE) != 0); + bool isRequestingQPRangesSupported = ((capEncoderSupportData1.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_ADJUSTABLE_QP_RANGE_AVAILABLE) != 0); bool isClientRequestingQPRanges = ((pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags & D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_QP_RANGE) != 0); if(isClientRequestingQPRanges && !isRequestingQPRangesSupported) { - debug_printf("[d3d12_video_encoder] WARNING: Requested D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_QP_RANGE with QPMin %d QPMax %d but the feature is not supported, will continue encoding unsetting this feature as fallback.\n", - pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR.MinQP, - pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR.MaxQP); - - 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_CBR.MinQP = 0; - pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR.MaxQP = 0; + debug_printf("[d3d12_video_encoder] WARNING: Requested D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_QP_RANGE with QPMin QPMax but the feature is not supported, will continue encoding unsetting this feature as fallback.\n"); + d3d12_video_encoder_disable_rc_minmaxqp(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc); } +#if D3D12_PREVIEW_SDK_VERSION >= 711 + bool isRequestingExtendedQVBRSupported = ((capEncoderSupportData1.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_EXTENDED_QVBR1_SUPPORT) != 0); + bool isClientRequestingExtendedQVBR = ((pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags & D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_EXTENDED_QVBR1_SUPPORT) != 0); + + if(isClientRequestingExtendedQVBR && !isRequestingExtendedQVBRSupported) { + debug_printf("[d3d12_video_encoder] WARNING: Requested D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_EXTENDED_QVBR1_SUPPORT but the feature is not supported, will continue encoding unsetting this feature as fallback.\n"); + d3d12_video_encoder_disable_rc_qvbr1_extended_to_qvbr_legacy(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc); + } +#endif + /// /// Try fallback configuration /// - configSupported = d3d12_video_encoder_query_d3d12_driver_caps(pD3D12Enc, /*inout*/ capEncoderSupportData) - && (((capEncoderSupportData.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_GENERAL_SUPPORT_OK) != 0) - && (capEncoderSupportData.ValidationFlags == D3D12_VIDEO_ENCODER_VALIDATION_FLAG_NONE)); + configSupported = d3d12_video_encoder_query_d3d12_driver_caps(pD3D12Enc, /*inout*/ capEncoderSupportData1) + && (((capEncoderSupportData1.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_GENERAL_SUPPORT_OK) != 0) + && (capEncoderSupportData1.ValidationFlags == D3D12_VIDEO_ENCODER_VALIDATION_FLAG_NONE)); } if(!configSupported) { debug_printf("[d3d12_video_encoder] Cap negotiation failed, see more details below:\n"); - if ((capEncoderSupportData.ValidationFlags & D3D12_VIDEO_ENCODER_VALIDATION_FLAG_CODEC_NOT_SUPPORTED) != 0) { + if ((capEncoderSupportData1.ValidationFlags & D3D12_VIDEO_ENCODER_VALIDATION_FLAG_CODEC_NOT_SUPPORTED) != 0) { debug_printf("[d3d12_video_encoder] Requested codec is not supported\n"); } - if ((capEncoderSupportData.ValidationFlags & + if ((capEncoderSupportData1.ValidationFlags & D3D12_VIDEO_ENCODER_VALIDATION_FLAG_RESOLUTION_NOT_SUPPORTED_IN_LIST) != 0) { debug_printf("[d3d12_video_encoder] Requested resolution is not supported\n"); } - if ((capEncoderSupportData.ValidationFlags & + if ((capEncoderSupportData1.ValidationFlags & D3D12_VIDEO_ENCODER_VALIDATION_FLAG_RATE_CONTROL_CONFIGURATION_NOT_SUPPORTED) != 0) { debug_printf("[d3d12_video_encoder] Requested bitrate or rc config is not supported\n"); } - if ((capEncoderSupportData.ValidationFlags & + if ((capEncoderSupportData1.ValidationFlags & D3D12_VIDEO_ENCODER_VALIDATION_FLAG_CODEC_CONFIGURATION_NOT_SUPPORTED) != 0) { debug_printf("[d3d12_video_encoder] Requested codec config is not supported\n"); } - if ((capEncoderSupportData.ValidationFlags & + if ((capEncoderSupportData1.ValidationFlags & D3D12_VIDEO_ENCODER_VALIDATION_FLAG_RATE_CONTROL_MODE_NOT_SUPPORTED) != 0) { debug_printf("[d3d12_video_encoder] Requested rate control mode is not supported\n"); } - if ((capEncoderSupportData.ValidationFlags & + if ((capEncoderSupportData1.ValidationFlags & D3D12_VIDEO_ENCODER_VALIDATION_FLAG_INTRA_REFRESH_MODE_NOT_SUPPORTED) != 0) { debug_printf("[d3d12_video_encoder] Requested intra refresh config is not supported\n"); } - if ((capEncoderSupportData.ValidationFlags & + if ((capEncoderSupportData1.ValidationFlags & D3D12_VIDEO_ENCODER_VALIDATION_FLAG_SUBREGION_LAYOUT_MODE_NOT_SUPPORTED) != 0) { debug_printf("[d3d12_video_encoder] Requested subregion layout mode is not supported\n"); } - if ((capEncoderSupportData.ValidationFlags & D3D12_VIDEO_ENCODER_VALIDATION_FLAG_INPUT_FORMAT_NOT_SUPPORTED) != + if ((capEncoderSupportData1.ValidationFlags & D3D12_VIDEO_ENCODER_VALIDATION_FLAG_INPUT_FORMAT_NOT_SUPPORTED) != 0) { debug_printf("[d3d12_video_encoder] Requested input dxgi format is not supported\n"); } @@ -885,45 +1103,58 @@ bool d3d12_video_encoder_negotiate_requested_features_and_d3d12_driver_caps(stru return configSupported; } -bool d3d12_video_encoder_query_d3d12_driver_caps(struct d3d12_video_encoder *pD3D12Enc, D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT &capEncoderSupportData) { - capEncoderSupportData.NodeIndex = pD3D12Enc->m_NodeIndex; - capEncoderSupportData.Codec = d3d12_video_encoder_get_current_codec(pD3D12Enc); - capEncoderSupportData.InputFormat = pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo.Format; - capEncoderSupportData.RateControl = d3d12_video_encoder_get_current_rate_control_settings(pD3D12Enc); - capEncoderSupportData.IntraRefresh = pD3D12Enc->m_currentEncodeConfig.m_IntraRefresh.Mode; - capEncoderSupportData.SubregionFrameEncoding = pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigMode; - capEncoderSupportData.ResolutionsListCount = 1; - capEncoderSupportData.pResolutionList = &pD3D12Enc->m_currentEncodeConfig.m_currentResolution; - capEncoderSupportData.CodecGopSequence = d3d12_video_encoder_get_current_gop_desc(pD3D12Enc); - capEncoderSupportData.MaxReferenceFramesInDPB = d3d12_video_encoder_get_current_max_dpb_capacity(pD3D12Enc); - capEncoderSupportData.CodecConfiguration = d3d12_video_encoder_get_current_codec_config_desc(pD3D12Enc); +bool d3d12_video_encoder_query_d3d12_driver_caps(struct d3d12_video_encoder *pD3D12Enc, D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT1 &capEncoderSupportData1) { + capEncoderSupportData1.NodeIndex = pD3D12Enc->m_NodeIndex; + capEncoderSupportData1.Codec = d3d12_video_encoder_get_current_codec(pD3D12Enc); + capEncoderSupportData1.InputFormat = pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo.Format; + capEncoderSupportData1.RateControl = d3d12_video_encoder_get_current_rate_control_settings(pD3D12Enc); + capEncoderSupportData1.IntraRefresh = pD3D12Enc->m_currentEncodeConfig.m_IntraRefresh.Mode; + capEncoderSupportData1.SubregionFrameEncoding = pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigMode; + capEncoderSupportData1.ResolutionsListCount = 1; + capEncoderSupportData1.pResolutionList = &pD3D12Enc->m_currentEncodeConfig.m_currentResolution; + capEncoderSupportData1.CodecGopSequence = d3d12_video_encoder_get_current_gop_desc(pD3D12Enc); + capEncoderSupportData1.MaxReferenceFramesInDPB = d3d12_video_encoder_get_current_max_dpb_capacity(pD3D12Enc); + capEncoderSupportData1.CodecConfiguration = d3d12_video_encoder_get_current_codec_config_desc(pD3D12Enc); enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile); switch (codec) { case PIPE_VIDEO_FORMAT_MPEG4_AVC: { - capEncoderSupportData.SuggestedProfile.pH264Profile = + capEncoderSupportData1.SuggestedProfile.pH264Profile = &pD3D12Enc->m_currentEncodeCapabilities.m_encoderSuggestedProfileDesc.m_H264Profile; - capEncoderSupportData.SuggestedProfile.DataSize = + capEncoderSupportData1.SuggestedProfile.DataSize = sizeof(pD3D12Enc->m_currentEncodeCapabilities.m_encoderSuggestedProfileDesc.m_H264Profile); - capEncoderSupportData.SuggestedLevel.pH264LevelSetting = + capEncoderSupportData1.SuggestedLevel.pH264LevelSetting = &pD3D12Enc->m_currentEncodeCapabilities.m_encoderLevelSuggestedDesc.m_H264LevelSetting; - capEncoderSupportData.SuggestedLevel.DataSize = + capEncoderSupportData1.SuggestedLevel.DataSize = sizeof(pD3D12Enc->m_currentEncodeCapabilities.m_encoderLevelSuggestedDesc.m_H264LevelSetting); } break; case PIPE_VIDEO_FORMAT_HEVC: { - capEncoderSupportData.SuggestedProfile.pHEVCProfile = + capEncoderSupportData1.SuggestedProfile.pHEVCProfile = &pD3D12Enc->m_currentEncodeCapabilities.m_encoderSuggestedProfileDesc.m_HEVCProfile; - capEncoderSupportData.SuggestedProfile.DataSize = + capEncoderSupportData1.SuggestedProfile.DataSize = sizeof(pD3D12Enc->m_currentEncodeCapabilities.m_encoderSuggestedProfileDesc.m_HEVCProfile); - capEncoderSupportData.SuggestedLevel.pHEVCLevelSetting = + capEncoderSupportData1.SuggestedLevel.pHEVCLevelSetting = &pD3D12Enc->m_currentEncodeCapabilities.m_encoderLevelSuggestedDesc.m_HEVCLevelSetting; - capEncoderSupportData.SuggestedLevel.DataSize = + capEncoderSupportData1.SuggestedLevel.DataSize = sizeof(pD3D12Enc->m_currentEncodeCapabilities.m_encoderLevelSuggestedDesc.m_HEVCLevelSetting); } break; +#if D3D12_PREVIEW_SDK_VERSION >= 711 + case PIPE_VIDEO_FORMAT_AV1: + { + capEncoderSupportData1.SuggestedProfile.pAV1Profile = + &pD3D12Enc->m_currentEncodeCapabilities.m_encoderSuggestedProfileDesc.m_AV1Profile; + capEncoderSupportData1.SuggestedProfile.DataSize = + sizeof(pD3D12Enc->m_currentEncodeCapabilities.m_encoderSuggestedProfileDesc.m_AV1Profile); + capEncoderSupportData1.SuggestedLevel.pAV1LevelSetting = + &pD3D12Enc->m_currentEncodeCapabilities.m_encoderLevelSuggestedDesc.m_AV1LevelSetting; + capEncoderSupportData1.SuggestedLevel.DataSize = + sizeof(pD3D12Enc->m_currentEncodeCapabilities.m_encoderLevelSuggestedDesc.m_AV1LevelSetting); + } break; +#endif default: { unreachable("Unsupported pipe_video_format"); @@ -931,18 +1162,32 @@ bool d3d12_video_encoder_query_d3d12_driver_caps(struct d3d12_video_encoder *pD3 } // prepare inout storage for the resolution dependent result. - capEncoderSupportData.pResolutionDependentSupport = + capEncoderSupportData1.pResolutionDependentSupport = &pD3D12Enc->m_currentEncodeCapabilities.m_currentResolutionSupportCaps; - - HRESULT hr = pD3D12Enc->m_spD3D12VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_ENCODER_SUPPORT, - &capEncoderSupportData, - sizeof(capEncoderSupportData)); + +#if D3D12_PREVIEW_SDK_VERSION >= 711 + capEncoderSupportData1.SubregionFrameEncodingData = d3d12_video_encoder_get_current_slice_param_settings(pD3D12Enc); +#endif + HRESULT hr = pD3D12Enc->m_spD3D12VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_ENCODER_SUPPORT1, + &capEncoderSupportData1, + sizeof(capEncoderSupportData1)); if (FAILED(hr)) { - debug_printf("CheckFeatureSupport failed with HR %x\n", hr); - return false; + 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"); + + // 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(&capEncoderSupportData1); + hr = pD3D12Enc->m_spD3D12VideoDevice->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; + } } - pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags = capEncoderSupportData.SupportFlags; - pD3D12Enc->m_currentEncodeCapabilities.m_ValidationFlags = capEncoderSupportData.ValidationFlags; + pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags = capEncoderSupportData1.SupportFlags; + pD3D12Enc->m_currentEncodeCapabilities.m_ValidationFlags = capEncoderSupportData1.ValidationFlags; return true; } @@ -984,7 +1229,15 @@ d3d12_video_encoder_get_current_profile_desc(struct d3d12_video_encoder *pD3D12E curProfDesc.DataSize = sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderProfileDesc.m_HEVCProfile); return curProfDesc; } break; - +#if D3D12_PREVIEW_SDK_VERSION >= 711 + case PIPE_VIDEO_FORMAT_AV1: + { + D3D12_VIDEO_ENCODER_PROFILE_DESC curProfDesc = {}; + curProfDesc.pAV1Profile = &pD3D12Enc->m_currentEncodeConfig.m_encoderProfileDesc.m_AV1Profile; + curProfDesc.DataSize = sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderProfileDesc.m_AV1Profile); + return curProfDesc; + } break; +#endif default: { unreachable("Unsupported pipe_video_format"); @@ -1014,7 +1267,12 @@ d3d12_video_encoder_update_current_encoder_config_state(struct d3d12_video_encod { return d3d12_video_encoder_update_current_encoder_config_state_hevc(pD3D12Enc, srcTexture, picture); } break; - +#if D3D12_PREVIEW_SDK_VERSION >= 711 + case PIPE_VIDEO_FORMAT_AV1: + { + return d3d12_video_encoder_update_current_encoder_config_state_av1(pD3D12Enc, srcTexture, picture); + } break; +#endif default: { unreachable("Unsupported pipe_video_format"); @@ -1168,7 +1426,9 @@ d3d12_video_encoder_prepare_output_buffers(struct d3d12_video_encoder *pD3D12Enc uint64_t current_metadata_slot = (pD3D12Enc->m_fenceValue % D3D12_VIDEO_ENC_METADATA_BUFFERS_COUNT); + enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile); d3d12_video_encoder_calculate_metadata_resolved_buffer_size( + codec, pD3D12Enc->m_currentEncodeCapabilities.m_MaxSlicesInOutput, pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].bufferSize); @@ -1290,10 +1550,27 @@ fail: } 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) { bufferSize = sizeof(D3D12_VIDEO_ENCODER_OUTPUT_METADATA) + (maxSliceNumber * sizeof(D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA)); + + switch (codec) { + case PIPE_VIDEO_FORMAT_MPEG4_AVC: + case PIPE_VIDEO_FORMAT_HEVC: + break; +#if D3D12_PREVIEW_SDK_VERSION >= 711 + case PIPE_VIDEO_FORMAT_AV1: + { + size_t extra_av1_size = d3d12_video_encoder_calculate_metadata_resolved_buffer_size_av1(maxSliceNumber); + bufferSize += extra_av1_size; + } break; +#endif + default: + { + unreachable("Unsupported pipe_video_format"); + } break; + } } // Returns the number of slices that the output will contain for fixed slicing modes @@ -1371,8 +1648,6 @@ d3d12_video_encoder_encode_bitstream(struct pipe_video_codec * codec, uint32_t inputVideoD3D12Subresource = 0u; struct d3d12_resource *pOutputBitstreamBuffer = (struct d3d12_resource *) destination; - assert(pOutputBitstreamBuffer); - ID3D12Resource *pOutputBufferD3D12Res = d3d12_resource_resource(pOutputBitstreamBuffer); // Make them permanently resident for video use d3d12_promote_to_permanent_residency(pD3D12Enc->m_pD3D12Screen, pOutputBitstreamBuffer); @@ -1380,12 +1655,21 @@ d3d12_video_encoder_encode_bitstream(struct pipe_video_codec * codec, uint64_t current_metadata_slot = (pD3D12Enc->m_fenceValue % D3D12_VIDEO_ENC_METADATA_BUFFERS_COUNT); + /* Warning if the previous finished async execution stored was read not by get_feedback() + before overwriting. This should be handled correctly by the app by calling vaSyncBuffer/vaSyncSurface + without having the async depth going beyond D3D12_VIDEO_ENC_METADATA_BUFFERS_COUNT frames without syncing */ + if(!pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].bRead) { + debug_printf("WARNING: [d3d12_video_encoder] d3d12_video_encoder_encode_bitstream - overwriting metadata slot %" PRIu64 " before calling get_feedback", current_metadata_slot); + assert(false); + } + pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].bRead = false; + /// /// Record Encode operation /// /// - /// pInputVideoD3D12Res and pOutputBufferD3D12Res are unwrapped from pipe_resource objects that are passed externally + /// pInputVideoBuffer and pOutputBitstreamBuffer are passed externally /// and could be tracked by pipe_context and have pending ops. Flush any work on them and transition to /// D3D12_RESOURCE_STATE_COMMON before issuing work in Video command queue below. After the video work is done in the /// GPU, transition back to D3D12_RESOURCE_STATE_COMMON @@ -1397,11 +1681,11 @@ d3d12_video_encoder_encode_bitstream(struct pipe_video_codec * codec, d3d12_transition_resource_state( d3d12_context(pD3D12Enc->base.context), - pInputVideoBuffer->texture, // d3d12_resource wrapper for pInputVideoD3D12Res + pInputVideoBuffer->texture, D3D12_RESOURCE_STATE_COMMON, D3D12_TRANSITION_FLAG_INVALIDATE_BINDINGS); d3d12_transition_resource_state(d3d12_context(pD3D12Enc->base.context), - pOutputBitstreamBuffer, // d3d12_resource wrapped for pOutputBufferD3D12Res + pOutputBitstreamBuffer, D3D12_RESOURCE_STATE_COMMON, D3D12_TRANSITION_FLAG_INVALIDATE_BINDINGS); d3d12_apply_resource_states(d3d12_context(pD3D12Enc->base.context), false); @@ -1411,6 +1695,79 @@ d3d12_video_encoder_encode_bitstream(struct pipe_video_codec * codec, false /*wantToWrite*/); d3d12_resource_wait_idle(d3d12_context(pD3D12Enc->base.context), pOutputBitstreamBuffer, true /*wantToWrite*/); + /// + /// Process pre-encode bitstream headers + /// + + // Decide the D3D12 buffer EncodeFrame will write to based on pre-post encode headers generation policy + ID3D12Resource *pOutputBufferD3D12Res = nullptr; + + d3d12_video_encoder_build_pre_encode_codec_headers(pD3D12Enc, + pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].postEncodeHeadersNeeded, + pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersByteSize); + assert(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersByteSize == pD3D12Enc->m_BitstreamHeadersBuffer.size()); + + // Only upload headers now and leave prefix offset space gap in compressed bitstream if the codec builds headers before execution. + if (!pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].postEncodeHeadersNeeded) + { + + // Headers are written before encode execution, have EncodeFrame write directly into the pipe destination buffer + pOutputBufferD3D12Res = d3d12_resource_resource(pOutputBitstreamBuffer); + + // It can happen that codecs like H264/HEVC don't write pre-headers for all frames (ie. reuse previous PPS) + if (pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersByteSize > 0) + { + // If driver needs offset alignment for bitstream resource, we will pad zeroes on the codec header to this end. + if ( + (pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.CompressedBitstreamBufferAccessAlignment > 1) + && ((pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersByteSize % pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.CompressedBitstreamBufferAccessAlignment) != 0) + ) { + pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersByteSize = ALIGN(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersByteSize, pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.CompressedBitstreamBufferAccessAlignment); + pD3D12Enc->m_BitstreamHeadersBuffer.resize(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersByteSize, 0); + } + + // Upload the CPU buffers with the bitstream headers to the compressed bitstream resource in the interval + // [0..pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersByteSize) + // Note: The buffer_subdata is queued in pD3D12Enc->base.context but doesn't execute immediately + // Will flush and sync this batch in d3d12_video_encoder_flush with the rest of the Video Encode Queue GPU work + + pD3D12Enc->base.context->buffer_subdata( + pD3D12Enc->base.context, // context + &pOutputBitstreamBuffer->base.b, // dst buffer + PIPE_MAP_WRITE, // usage PIPE_MAP_x + 0, // offset + pD3D12Enc->m_BitstreamHeadersBuffer.size(), + pD3D12Enc->m_BitstreamHeadersBuffer.data()); + } + } + else + { + assert(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersByteSize == 0); + if (pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].spStagingBitstream == nullptr) { + D3D12_HEAP_PROPERTIES Properties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT); + CD3DX12_RESOURCE_DESC resolvedMetadataBufferDesc = CD3DX12_RESOURCE_DESC::Buffer(D3D12_DEFAULT_COMPBIT_STAGING_SIZE); + HRESULT hr = pD3D12Enc->m_pD3D12Screen->dev->CreateCommittedResource( + &Properties, + D3D12_HEAP_FLAG_NONE, + &resolvedMetadataBufferDesc, + D3D12_RESOURCE_STATE_COMMON, + nullptr, + IID_PPV_ARGS(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].spStagingBitstream.GetAddressOf())); + + if (FAILED(hr)) { + debug_printf("CreateCommittedResource failed with HR %x\n", hr); + assert(false); + } + } + + // Headers are written after execution, have EncodeFrame write into a staging buffer + // and then get_feedback will pack the finalized bitstream and copy into comp_bit_destination + pOutputBufferD3D12Res = pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].spStagingBitstream.Get(); + + // Save the pipe destination buffer the headers need to be written to in get_feedback + pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].comp_bit_destination = &pOutputBitstreamBuffer->base.b; + } + std::vector rgCurrentFrameStateTransitions = { CD3DX12_RESOURCE_BARRIER::Transition(pInputVideoD3D12Res, D3D12_RESOURCE_STATE_COMMON, @@ -1528,16 +1885,10 @@ d3d12_video_encoder_encode_bitstream(struct pipe_video_codec * codec, d3d12_video_encoder_get_current_picture_param_settings(pD3D12Enc); pD3D12Enc->m_upDPBManager->get_current_frame_picture_control_data(currentPicParams); - uint32_t prefixGeneratedHeadersByteSize = d3d12_video_encoder_build_codec_headers(pD3D12Enc); - - // If driver needs offset alignment for bitstream resource, we will pad zeroes on the codec header to this end. - if ( - (prefixGeneratedHeadersByteSize > 0) - && (pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.CompressedBitstreamBufferAccessAlignment > 1) - && ((prefixGeneratedHeadersByteSize % pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.CompressedBitstreamBufferAccessAlignment) != 0) - ) { - prefixGeneratedHeadersByteSize = ALIGN(prefixGeneratedHeadersByteSize, pD3D12Enc->m_currentEncodeCapabilities.m_ResourceRequirementsCaps.CompressedBitstreamBufferAccessAlignment); - pD3D12Enc->m_BitstreamHeadersBuffer.resize(prefixGeneratedHeadersByteSize, 0); + // Stores D3D12_VIDEO_ENCODER_AV1_REFERENCE_PICTURE_DESCRIPTOR in the associated metadata + // for header generation after execution (if applicable) + if (pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].postEncodeHeadersNeeded) { + d3d12_video_encoder_store_current_picture_references(pD3D12Enc, current_metadata_slot); } const D3D12_VIDEO_ENCODER_ENCODEFRAME_INPUT_ARGUMENTS inputStreamArguments = { @@ -1563,7 +1914,7 @@ d3d12_video_encoder_encode_bitstream(struct pipe_video_codec * codec, referenceFramesDescriptor }, pInputVideoD3D12Res, inputVideoD3D12Subresource, - prefixGeneratedHeadersByteSize // hint for driver to know header size in final bitstream for rate control internal + static_cast(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersByteSize) // budgeting. - User can also calculate headers fixed size beforehand (eg. no VUI, // etc) and build them with final values after EncodeFrame is executed }; @@ -1572,8 +1923,7 @@ d3d12_video_encoder_encode_bitstream(struct pipe_video_codec * codec, // D3D12_VIDEO_ENCODER_COMPRESSED_BITSTREAM { pOutputBufferD3D12Res, - prefixGeneratedHeadersByteSize, // Start writing after the reserved interval [0, - // prefixGeneratedHeadersByteSize) for bitstream headers + pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersByteSize, }, // D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE reconPicOutputTextureDesc, @@ -1581,26 +1931,6 @@ d3d12_video_encoder_encode_bitstream(struct pipe_video_codec * codec, { pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_spMetadataOutputBuffer.Get(), 0 } }; - // Upload the CPU buffers with the bitstream headers to the compressed bitstream resource in the interval [0, - // prefixGeneratedHeadersByteSize) - assert(prefixGeneratedHeadersByteSize == pD3D12Enc->m_BitstreamHeadersBuffer.size()); - - // Store this info for get_feedback to be able to calculate final bitstream size - pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].codecHeadersSize = prefixGeneratedHeadersByteSize; - - if (prefixGeneratedHeadersByteSize > 0) { - pD3D12Enc->base.context->buffer_subdata( - pD3D12Enc->base.context, // context - destination, // dst buffer - "destination" is the pipe_resource object - // wrapping pOutputBitstreamBuffer and eventually pOutputBufferD3D12Res - PIPE_MAP_WRITE, // usage PIPE_MAP_x - 0, // offset - pD3D12Enc->m_BitstreamHeadersBuffer.size(), - pD3D12Enc->m_BitstreamHeadersBuffer.data()); - } - // Note: The buffer_subdata is queued in pD3D12Enc->base.context but doesn't execute immediately - // Will flush and sync this batch in d3d12_video_encoder_flush with the rest of the Video Encode Queue GPU work - // Record EncodeFrame pD3D12Enc->m_spEncodeCommandList->EncodeFrame(pD3D12Enc->m_spVideoEncoder.Get(), pD3D12Enc->m_spVideoEncoderHeap.Get(), @@ -1709,31 +2039,87 @@ d3d12_video_encoder_get_feedback(struct pipe_video_codec *codec, void *feedback, return; } - D3D12_VIDEO_ENCODER_OUTPUT_METADATA encoderMetadata; - std::vector pSubregionsMetadata; - d3d12_video_encoder_extract_encode_metadata( - pD3D12Enc, - pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].spBuffer.Get(), - pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].bufferSize, - encoderMetadata, - pSubregionsMetadata); + if(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].postEncodeHeadersNeeded) + { + /// + /// If we didn't write headers before encode execution, finalize the codec specific bitsteam now + /// - // Read metadata from encoderMetadata - if (encoderMetadata.EncodeErrorFlags != D3D12_VIDEO_ENCODER_ENCODE_ERROR_FLAG_NO_ERROR) { - debug_printf("[d3d12_video_encoder] Encode GPU command for fence %" PRIu64 " failed - EncodeErrorFlags: %" PRIu64 "\n", - requested_metadata_fence, - encoderMetadata.EncodeErrorFlags); - *size = 0; - assert(false); - return; + *size = d3d12_video_encoder_build_post_encode_codec_bitstream( + // Current encoder + pD3D12Enc, + // Associated frame fenceValue + requested_metadata_fence, + // metadata desc + pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot] + ); } + else + { + /// + /// If we wrote headers (if any) before encode execution, use that size to calculate feedback size of complete bitstream. + /// - assert(encoderMetadata.EncodedBitstreamWrittenBytesCount > 0u); - *size = static_cast(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].codecHeadersSize + encoderMetadata.EncodedBitstreamWrittenBytesCount); + D3D12_VIDEO_ENCODER_OUTPUT_METADATA encoderMetadata; + std::vector pSubregionsMetadata; + d3d12_video_encoder_extract_encode_metadata( + pD3D12Enc, + pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].spBuffer.Get(), + pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].bufferSize, + encoderMetadata, + pSubregionsMetadata); + + // Read metadata from encoderMetadata + if (encoderMetadata.EncodeErrorFlags != D3D12_VIDEO_ENCODER_ENCODE_ERROR_FLAG_NO_ERROR) { + debug_printf("[d3d12_video_encoder] Encode GPU command for fence %" PRIu64 " failed - EncodeErrorFlags: %" PRIu64 "\n", + requested_metadata_fence, + encoderMetadata.EncodeErrorFlags); + *size = 0; + assert(false); + return; + } + + assert(encoderMetadata.EncodedBitstreamWrittenBytesCount > 0u); + + *size = static_cast(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersByteSize + encoderMetadata.EncodedBitstreamWrittenBytesCount); + } + debug_printf("[d3d12_video_encoder_get_feedback] Requested metadata for encoded frame at fence %" PRIu64 " is %d (feedback was requested at current fence %" PRIu64 ")\n", requested_metadata_fence, *size, pD3D12Enc->m_fenceValue); + + pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].bRead = true; +} + +unsigned +d3d12_video_encoder_build_post_encode_codec_bitstream(struct d3d12_video_encoder * pD3D12Enc, + uint64_t associated_fence_value, + EncodedBitstreamResolvedMetadata& associatedMetadata) +{ + enum pipe_video_format codec_format = u_reduce_video_profile(pD3D12Enc->base.profile); + switch (codec_format) { + case PIPE_VIDEO_FORMAT_MPEG4_AVC: + case PIPE_VIDEO_FORMAT_HEVC: + { + return 0; + } break; // Do not need post encode values in headers +#if D3D12_PREVIEW_SDK_VERSION >= 711 + case PIPE_VIDEO_FORMAT_AV1: + { + return d3d12_video_encoder_build_post_encode_codec_bitstream_av1( + // Current encoder + pD3D12Enc, + // associated fence value + associated_fence_value, + // Metadata desc + associatedMetadata + ); + } break; +#endif + default: + unreachable("Unsupported pipe_video_format"); + } } void @@ -1831,3 +2217,31 @@ d3d12_video_encoder_end_frame(struct pipe_video_codec * codec, pD3D12Enc->m_bPendingWorkNotFlushed = true; } + +void +d3d12_video_encoder_store_current_picture_references(d3d12_video_encoder *pD3D12Enc, + uint64_t current_metadata_slot) +{ + enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile); + switch (codec) { + case PIPE_VIDEO_FORMAT_MPEG4_AVC: + { + // Not needed (not post encode headers) + } break; + + case PIPE_VIDEO_FORMAT_HEVC: + { + // Not needed (not post encode headers) + } break; +#if D3D12_PREVIEW_SDK_VERSION >= 711 + case PIPE_VIDEO_FORMAT_AV1: + { + d3d12_video_encoder_store_current_picture_references_av1(pD3D12Enc, current_metadata_slot); + } break; +#endif + default: + { + unreachable("Unsupported pipe_video_format"); + } break; + } +} diff --git a/src/gallium/drivers/d3d12/d3d12_video_enc.h b/src/gallium/drivers/d3d12/d3d12_video_enc.h index 7c5b23afa8b..1a365dc69b1 100644 --- a/src/gallium/drivers/d3d12/d3d12_video_enc.h +++ b/src/gallium/drivers/d3d12/d3d12_video_enc.h @@ -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 /// /// 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 pendingShowableFrames; + } m_encoderCodecSpecificStateDescAV1; + +}; + +struct EncodedBitstreamResolvedMetadata +{ + ComPtr spBuffer; + uint64_t bufferSize = 0; + + ComPtr 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 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 m_StagingBitstreamConstruction; }; struct d3d12_video_encoder @@ -250,14 +368,6 @@ struct d3d12_video_encoder std::shared_ptr m_upDPBStorageManager; std::unique_ptr m_upBitstreamBuilder; - struct EncodedBitstreamResolvedMetadata - { - ComPtr spBuffer; - uint64_t bufferSize = 0; - uint64_t codecHeadersSize = 0; - ComPtr m_spMetadataOutputBuffer; - }; - std::vector m_BitstreamHeadersBuffer; std::vector m_StagingHeadersBuffer; std::vector 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 diff --git a/src/gallium/drivers/d3d12/d3d12_video_enc_av1.cpp b/src/gallium/drivers/d3d12/d3d12_video_enc_av1.cpp new file mode 100644 index 00000000000..4a47fc42e8b --- /dev/null +++ b/src/gallium/drivers/d3d12/d3d12_video_enc_av1.cpp @@ -0,0 +1,2695 @@ +/* + * 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 "util/u_video.h" +#include "d3d12_resource.h" +#include "d3d12_screen.h" +#include "d3d12_format.h" +#include + +void +d3d12_video_encoder_update_current_rate_control_av1(struct d3d12_video_encoder *pD3D12Enc, + pipe_av1_enc_picture_desc *picture) +{ + auto previousConfig = pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc; + + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc = {}; + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_FrameRate.Numerator = picture->rc[0].frame_rate_num; + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_FrameRate.Denominator = picture->rc[0].frame_rate_den; + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags = D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_NONE; + + switch (picture->rc[0].rate_ctrl_method) { + case PIPE_H2645_ENC_RATE_CONTROL_METHOD_VARIABLE_SKIP: + case PIPE_H2645_ENC_RATE_CONTROL_METHOD_VARIABLE: + { + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Mode = D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_VBR; + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_VBR.TargetAvgBitRate = + picture->rc[0].target_bitrate; + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_VBR.PeakBitRate = + picture->rc[0].peak_bitrate; + + if (D3D12_VIDEO_ENC_CBR_FORCE_VBV_EQUAL_BITRATE) { + debug_printf( + "[d3d12_video_encoder_av1] d3d12_video_encoder_update_current_rate_control_av1 " + "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_CBR.TargetBitRate); + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags |= + D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_VBV_SIZES; + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR.VBVCapacity = + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR.TargetBitRate; + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR.InitialVBVFullness = + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR.TargetBitRate; + } else if (picture->rc[0].app_requested_hrd_buffer) { + debug_printf( + "[d3d12_video_encoder_av1] d3d12_video_encoder_update_current_rate_control_av1 HRD required by app," + " setting VBV Size = %d (bits) - VBV Initial Capacity %d (bits)\n", + picture->rc[0].vbv_buffer_size, + picture->rc[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_Config.m_Configuration_VBR.VBVCapacity = + picture->rc[0].vbv_buffer_size; + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_VBR.InitialVBVFullness = + picture->rc[0].vbv_buf_initial_size; + } + + if (picture->rc[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_VBR.MaxFrameBitSize = + picture->rc[0].max_au_size; + + debug_printf( + "[d3d12_video_encoder_av1] d3d12_video_encoder_update_current_rate_control_av1 " + "Upper layer requested explicit MaxFrameBitSize: %" PRIu64 "\n", + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_VBR.MaxFrameBitSize); + } + + if (picture->rc[0].app_requested_qp_range) { + debug_printf("[d3d12_video_encoder_av1] d3d12_video_encoder_update_current_rate_control_av1 " + "Upper layer requested explicit MinQP: %d MaxQP: %d\n", + picture->rc[0].min_qp, + picture->rc[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_VBR.MinQP = + picture->rc[0].min_qp; + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_VBR.MaxQP = + picture->rc[0].max_qp; + } + + } break; + case PIPE_H2645_ENC_RATE_CONTROL_METHOD_QUALITY_VARIABLE: + { + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Mode = D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_QVBR; + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1.TargetAvgBitRate = + picture->rc[0].target_bitrate; + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1.PeakBitRate = + picture->rc[0].peak_bitrate; + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1.ConstantQualityTarget = + picture->rc[0].vbr_quality_factor; + + if (D3D12_VIDEO_ENC_CBR_FORCE_VBV_EQUAL_BITRATE) { + debug_printf("[d3d12_video_encoder_av1] d3d12_video_encoder_update_current_rate_control_av1 " + "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[0].app_requested_hrd_buffer) { + debug_printf( + "[d3d12_video_encoder_av1] d3d12_video_encoder_update_current_rate_control_av1 HRD required by app," + " setting VBV Size = %d (bits) - VBV Initial Capacity %d (bits)\n", + picture->rc[0].vbv_buffer_size, + picture->rc[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->rc[0].vbv_buffer_size; + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1.InitialVBVFullness = + picture->rc[0].vbv_buf_initial_size; + } + + if (picture->rc[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->rc[0].max_au_size; + + debug_printf( + "[d3d12_video_encoder_av1] d3d12_video_encoder_update_current_rate_control_av1 " + "Upper layer requested explicit MaxFrameBitSize: %" PRIu64 "\n", + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1.MaxFrameBitSize); + } + + if (picture->rc[0].app_requested_qp_range) { + debug_printf("[d3d12_video_encoder_av1] d3d12_video_encoder_update_current_rate_control_av1 " + "Upper layer requested explicit MinQP: %d MaxQP: %d\n", + picture->rc[0].min_qp, + picture->rc[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->rc[0].min_qp; + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_QVBR1.MaxQP = + picture->rc[0].max_qp; + } + + } break; + case PIPE_H2645_ENC_RATE_CONTROL_METHOD_CONSTANT_SKIP: + case PIPE_H2645_ENC_RATE_CONTROL_METHOD_CONSTANT: + { + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Mode = D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CBR; + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR.TargetBitRate = + picture->rc[0].target_bitrate; + + /* For CBR mode, to guarantee bitrate of generated stream complies with + * target bitrate (e.g. no over +/-10%), vbv_buffer_size and initial capacity should be same + * as target bitrate. Controlled by OS env var D3D12_VIDEO_ENC_CBR_FORCE_VBV_EQUAL_BITRATE + */ + if (D3D12_VIDEO_ENC_CBR_FORCE_VBV_EQUAL_BITRATE) { + debug_printf( + "[d3d12_video_encoder_av1] d3d12_video_encoder_update_current_rate_control_av1 " + "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_CBR.TargetBitRate); + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Flags |= + D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_VBV_SIZES; + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR.VBVCapacity = + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR.TargetBitRate; + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR.InitialVBVFullness = + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR.TargetBitRate; + } else if (picture->rc[0].app_requested_hrd_buffer) { + debug_printf( + "[d3d12_video_encoder_av1] d3d12_video_encoder_update_current_rate_control_av1 HRD required by app," + " setting VBV Size = %d (bits) - VBV Initial Capacity %d (bits)\n", + picture->rc[0].vbv_buffer_size, + picture->rc[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_Config.m_Configuration_CBR.VBVCapacity = + picture->rc[0].vbv_buffer_size; + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR.InitialVBVFullness = + picture->rc[0].vbv_buf_initial_size; + } + + if (picture->rc[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_CBR.MaxFrameBitSize = + picture->rc[0].max_au_size; + + debug_printf( + "[d3d12_video_encoder_av1] d3d12_video_encoder_update_current_rate_control_av1 " + "Upper layer requested explicit MaxFrameBitSize: %" PRIu64 "\n", + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR.MaxFrameBitSize); + } + + if (picture->rc[0].app_requested_qp_range) { + debug_printf("[d3d12_video_encoder_av1] d3d12_video_encoder_update_current_rate_control_av1 " + "Upper layer requested explicit MinQP: %d MaxQP: %d\n", + picture->rc[0].min_qp, + picture->rc[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_CBR.MinQP = + picture->rc[0].min_qp; + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CBR.MaxQP = + picture->rc[0].max_qp; + } + + } break; + case PIPE_H2645_ENC_RATE_CONTROL_METHOD_DISABLE: + { + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Mode = D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CQP; + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CQP + .ConstantQP_FullIntracodedFrame = picture->rc[0].app_requested_initial_qp ? picture->rc[0].qp : 0; + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CQP + .ConstantQP_InterPredictedFrame_PrevRefOnly = + picture->rc[0].app_requested_initial_qp ? picture->rc[0].qp : 0; + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CQP + .ConstantQP_InterPredictedFrame_BiDirectionalRef = + picture->rc[0].app_requested_initial_qp ? picture->rc[0].qp : 0; + } break; + default: + { + debug_printf("[d3d12_video_encoder_av1] d3d12_video_encoder_update_current_rate_control_av1 invalid RC " + "config, using default RC CQP mode\n"); + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Mode = D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CQP; + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CQP + .ConstantQP_FullIntracodedFrame = 30; + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CQP + .ConstantQP_InterPredictedFrame_PrevRefOnly = 30; + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc.m_Config.m_Configuration_CQP + .ConstantQP_InterPredictedFrame_BiDirectionalRef = 30; + } break; + } + + if (memcmp(&previousConfig, + &pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc, + sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc)) != 0) { + pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_rate_control; + } +} + +// +// Returns AV1 extra size on top of the usual base metadata layout size +// +size_t +d3d12_video_encoder_calculate_metadata_resolved_buffer_size_av1(uint32_t maxSliceNumber) +{ + return sizeof(D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_TILES) + + sizeof(D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES); +} + +void +d3d12_video_encoder_convert_d3d12_to_spec_level_av1(D3D12_VIDEO_ENCODER_AV1_LEVELS level12, uint32_t &specLevel) +{ + // Enum matches values as in seq_level_idx + specLevel = (uint32_t) level12; +} + +void +d3d12_video_encoder_convert_d3d12_to_spec_tier_av1(D3D12_VIDEO_ENCODER_AV1_TIER tier12, uint32_t &specTier) +{ + // Enum matches values as in seq_level_idx + specTier = (uint32_t) tier12; +} + +void +d3d12_video_encoder_convert_spec_to_d3d12_level_av1(uint32_t specLevel, D3D12_VIDEO_ENCODER_AV1_LEVELS &level12) +{ + // Enum matches values as in seq_level_idx + level12 = (D3D12_VIDEO_ENCODER_AV1_LEVELS) specLevel; +} + +void +d3d12_video_encoder_convert_spec_to_d3d12_tier_av1(uint32_t specTier, D3D12_VIDEO_ENCODER_AV1_TIER &tier12) +{ + // Enum matches values as in seq_tier + tier12 = (D3D12_VIDEO_ENCODER_AV1_TIER) specTier; +} + +uint32_t +d3d12_video_encoder_convert_d3d12_profile_to_spec_profile_av1(D3D12_VIDEO_ENCODER_AV1_PROFILE profile) +{ + switch (profile) { + case D3D12_VIDEO_ENCODER_AV1_PROFILE_MAIN: + { + return 0; + } break; + default: + { + unreachable("Unsupported D3D12_VIDEO_ENCODER_AV1_PROFILE"); + } break; + } +} + +D3D12_VIDEO_ENCODER_AV1_PROFILE +d3d12_video_encoder_convert_profile_to_d3d12_enc_profile_av1(enum pipe_video_profile profile) +{ + switch (profile) { + case PIPE_VIDEO_PROFILE_AV1_MAIN: + { + return D3D12_VIDEO_ENCODER_AV1_PROFILE_MAIN; + } break; + default: + { + unreachable("Unsupported pipe_video_profile"); + } break; + } +} + +bool +d3d12_video_encoder_update_av1_gop_configuration(struct d3d12_video_encoder *pD3D12Enc, + pipe_av1_enc_picture_desc *picture) +{ + static_assert((unsigned) D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_KEY_FRAME == (unsigned) PIPE_AV1_ENC_FRAME_TYPE_KEY); + static_assert((unsigned) D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_INTER_FRAME == (unsigned) PIPE_AV1_ENC_FRAME_TYPE_INTER); + static_assert((unsigned) D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_INTRA_ONLY_FRAME == + (unsigned) PIPE_AV1_ENC_FRAME_TYPE_INTRA_ONLY); + static_assert((unsigned) D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_SWITCH_FRAME == + (unsigned) PIPE_AV1_ENC_FRAME_TYPE_SWITCH); + + // Only update GOP when it begins + // This triggers DPB/encoder/heap re-creation, so only check on IDR when a GOP might change + if ((picture->frame_type == PIPE_AV1_ENC_FRAME_TYPE_INTRA_ONLY) || + (picture->frame_type == PIPE_AV1_ENC_FRAME_TYPE_KEY)) { + uint32_t GOPLength = picture->seq.intra_period; + uint32_t PPicturePeriod = picture->seq.ip_period; + + // Set dirty flag if m_AV1SequenceStructure changed + auto previousGOPConfig = pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_AV1SequenceStructure; + pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_AV1SequenceStructure = { + GOPLength, + PPicturePeriod, + }; + + if (memcmp(&previousGOPConfig, + &pD3D12Enc->m_currentEncodeConfig.m_encoderGOPConfigDesc.m_AV1SequenceStructure, + sizeof(D3D12_VIDEO_ENCODER_AV1_SEQUENCE_STRUCTURE)) != 0) { + pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_gop; + } + } + return true; +} + +D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE +d3d12_video_encoder_convert_av1_motion_configuration(struct d3d12_video_encoder *pD3D12Enc, + pipe_av1_enc_picture_desc *picture) +{ + return D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_MAXIMUM; +} + +bool +d3d12_video_encoder_compare_tile_config_av1( + D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE currentTilesMode, + D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE requestedTilesMode, + D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_TILES currentTilePartition, + D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_TILES requestedTilePartition) +{ + if (currentTilesMode != requestedTilesMode) + return false; + + if (memcmp(¤tTilePartition, + &requestedTilePartition, + sizeof(D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_TILES))) + return false; + + return true; +} + +/// +/// Tries to configurate the encoder using the requested tile configuration +/// +bool +d3d12_video_encoder_negotiate_current_av1_tiles_configuration(struct d3d12_video_encoder *pD3D12Enc, + pipe_av1_enc_picture_desc *pAV1Pic) +{ + D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_TILES tilePartition = {}; + tilePartition.RowCount = pAV1Pic->tile_rows; + tilePartition.ColCount = pAV1Pic->tile_cols; + tilePartition.ContextUpdateTileId = pAV1Pic->context_update_tile_id; + + // VA-API interface has 63 entries for tile cols/rows. When 64 requested, + // last one has to be calculated from the frame width/height in superblocks. + + // Copy the tile col sizes (up to 63 defined in VA-API interface array sizes) + size_t accum_cols_sb = 0; + uint8_t src_cols_count = MIN2(63, pAV1Pic->tile_cols); + for (uint8_t i = 0; i < src_cols_count; i++) { + tilePartition.ColWidths[i] = pAV1Pic->width_in_sbs_minus_1[i] + 1; + accum_cols_sb += tilePartition.ColWidths[i]; + } + + // If there are 64 cols, calculate the last one manually as the difference + // between frame width in sb minus the accumulated tiles sb sizes + if (pAV1Pic->tile_cols == 64) + tilePartition.ColWidths[63] = pAV1Pic->frame_width_sb - accum_cols_sb; + + // Copy the tile row sizes (up to 63 defined in VA-API interface array sizes) + size_t accum_rows_sb = 0; + uint8_t src_rows_count = MIN2(63, pAV1Pic->tile_rows); + for (uint8_t i = 0; i < src_rows_count; i++) { + tilePartition.RowHeights[i] = pAV1Pic->height_in_sbs_minus_1[i] + 1; + accum_rows_sb += tilePartition.RowHeights[i]; + } + + // If there are 64 rows, calculate the last one manually as the difference + // between frame height in sb minus the accumulated tiles sb sizes + if (pAV1Pic->tile_rows == 64) + tilePartition.RowHeights[63] = pAV1Pic->frame_height_sb - accum_rows_sb; + + // Iterate the tiles and see if they're uniformly partitioned to decide + // which D3D12 tile mode to use + // Ignore the last row and last col width + bool tilesUniform = !D3D12_VIDEO_FORCE_TILE_MODE && util_is_power_of_two_or_zero(tilePartition.RowCount) && + util_is_power_of_two_or_zero(tilePartition.ColCount); + // Iterate again now that the 63/64 edge case has been handled above. + for (uint8_t i = 1; tilesUniform && (i < tilePartition.RowCount - 1) /* Ignore last row */; i++) + tilesUniform = tilesUniform && (tilePartition.RowHeights[i - 1] == tilePartition.RowHeights[i]); + + for (uint8_t i = 1; tilesUniform && (i < tilePartition.ColCount - 1) /* Ignore last col */; i++) + tilesUniform = tilesUniform && (tilePartition.ColWidths[i - 1] == tilePartition.ColWidths[i]); + + D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE requestedTilesMode = + tilesUniform ? D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_GRID_PARTITION : + D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_CONFIGURABLE_GRID_PARTITION; + + assert(pAV1Pic->num_tile_groups <= 128); // ARRAY_SIZE(TilesGroups) + pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigDesc.m_TilesConfig_AV1.TilesGroupsCount = + pAV1Pic->num_tile_groups; + for (uint8_t i = 0; i < pAV1Pic->num_tile_groups; i++) { + pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigDesc.m_TilesConfig_AV1.TilesGroups[i].tg_start = + pAV1Pic->tile_groups[i].tile_group_start; + pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigDesc.m_TilesConfig_AV1.TilesGroups[i].tg_end = + pAV1Pic->tile_groups[i].tile_group_end; + } + + if (!d3d12_video_encoder_compare_tile_config_av1( + pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigMode, + requestedTilesMode, + pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigDesc.m_TilesConfig_AV1.TilesPartition, + tilePartition)) { + pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_slices; + } + + // Update the encoder state with the tile config + pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigMode = requestedTilesMode; + pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigDesc.m_TilesConfig_AV1.TilesPartition = tilePartition; + + D3D12_FEATURE_DATA_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_CONFIG capDataTilesSupport = {}; + capDataTilesSupport.NodeIndex = pD3D12Enc->m_NodeIndex; + capDataTilesSupport.Codec = D3D12_VIDEO_ENCODER_CODEC_AV1; + capDataTilesSupport.Profile.DataSize = sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderProfileDesc.m_AV1Profile); + capDataTilesSupport.Profile.pAV1Profile = &pD3D12Enc->m_currentEncodeConfig.m_encoderProfileDesc.m_AV1Profile; + capDataTilesSupport.Level.DataSize = sizeof(pD3D12Enc->m_currentEncodeConfig.m_encoderLevelDesc.m_AV1LevelSetting); + capDataTilesSupport.Level.pAV1LevelSetting = &pD3D12Enc->m_currentEncodeConfig.m_encoderLevelDesc.m_AV1LevelSetting; + capDataTilesSupport.FrameResolution.Width = pAV1Pic->frame_width; + capDataTilesSupport.FrameResolution.Height = pAV1Pic->frame_height; + capDataTilesSupport.SubregionMode = requestedTilesMode; + capDataTilesSupport.CodecSupport.DataSize = + sizeof(pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1TileCaps); + capDataTilesSupport.CodecSupport.pAV1Support = + &pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1TileCaps; + pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1TileCaps.Use128SuperBlocks = false; + // return units in 64x64 default size + pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1TileCaps.TilesConfiguration = + pD3D12Enc->m_currentEncodeConfig.m_encoderSliceConfigDesc.m_TilesConfig_AV1.TilesPartition; + HRESULT hr = + pD3D12Enc->m_spD3D12VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_CONFIG, + &capDataTilesSupport, + sizeof(capDataTilesSupport)); + if (FAILED(hr) || !capDataTilesSupport.IsSupported) { + debug_printf("D3D12_FEATURE_VIDEO_ENCODER_SUBREGION_TILES_SUPPORT HR (0x%x) error or IsSupported: (%d).\n", + hr, + capDataTilesSupport.IsSupported); + return false; + } + + return true; +} + +D3D12_VIDEO_ENCODER_AV1_CODEC_CONFIGURATION +d3d12_video_encoder_convert_av1_codec_configuration(struct d3d12_video_encoder *pD3D12Enc, + pipe_av1_enc_picture_desc *pAV1Pic, + bool &is_supported) +{ + is_supported = true; + D3D12_VIDEO_ENCODER_AV1_CODEC_CONFIGURATION config = { + // D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAGS FeatureFlags; + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_NONE, + // UINT OrderHintBitsMinus1; + pAV1Pic->seq.order_hint_bits - 1, + }; + + // + // Query AV1 caps and store in m_currentEncodeCapabilities + // + + D3D12_FEATURE_DATA_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT capCodecConfigData = {}; + capCodecConfigData.NodeIndex = pD3D12Enc->m_NodeIndex; + capCodecConfigData.Codec = D3D12_VIDEO_ENCODER_CODEC_AV1; + D3D12_VIDEO_ENCODER_AV1_PROFILE prof = + d3d12_video_encoder_convert_profile_to_d3d12_enc_profile_av1(pD3D12Enc->base.profile); + capCodecConfigData.Profile.pAV1Profile = &prof; + capCodecConfigData.Profile.DataSize = sizeof(prof); + capCodecConfigData.CodecSupportLimits.pAV1Support = + &pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps; + capCodecConfigData.CodecSupportLimits.DataSize = + sizeof(pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps); + + if (FAILED( + pD3D12Enc->m_spD3D12VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT, + &capCodecConfigData, + sizeof(capCodecConfigData))) || + !capCodecConfigData.IsSupported) { + debug_printf("D3D12_FEATURE_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT arguments not supported.\n"); + is_supported = false; + return config; + } + + // Enable features requested by gallium + + if (pAV1Pic->seq.seq_bits.use_128x128_superblock) + config.FeatureFlags |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_128x128_SUPERBLOCK; + if (pAV1Pic->seq.seq_bits.enable_filter_intra) + config.FeatureFlags |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_FILTER_INTRA; + if (pAV1Pic->seq.seq_bits.enable_intra_edge_filter) + config.FeatureFlags |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTRA_EDGE_FILTER; + if (pAV1Pic->seq.seq_bits.enable_interintra_compound) + config.FeatureFlags |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTERINTRA_COMPOUND; + if (pAV1Pic->seq.seq_bits.enable_masked_compound) + config.FeatureFlags |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_MASKED_COMPOUND; + if (pAV1Pic->seq.seq_bits.enable_warped_motion) + config.FeatureFlags |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_WARPED_MOTION; + if (pAV1Pic->seq.seq_bits.enable_dual_filter) + config.FeatureFlags |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_DUAL_FILTER; + if (pAV1Pic->seq.seq_bits.enable_order_hint) + config.FeatureFlags |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_ORDER_HINT_TOOLS; + if (pAV1Pic->seq.seq_bits.enable_jnt_comp) + config.FeatureFlags |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_JNT_COMP; + if (pAV1Pic->seq.seq_bits.enable_ref_frame_mvs) + config.FeatureFlags |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_FRAME_REFERENCE_MOTION_VECTORS; + if (pAV1Pic->seq.seq_bits.enable_superres) + config.FeatureFlags |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_SUPER_RESOLUTION; + if (pAV1Pic->seq.seq_bits.enable_cdef) + config.FeatureFlags |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_CDEF_FILTERING; + if (pAV1Pic->seq.seq_bits.enable_restoration) + config.FeatureFlags |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_LOOP_RESTORATION_FILTER; + + // No pipe flag for the following features, associated pic flags can be selected per frame. Enable if supported. + + if ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.SupportedFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_FORCED_INTEGER_MOTION_VECTORS) != 0) // seq_force_integer_mv + config.FeatureFlags |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_FORCED_INTEGER_MOTION_VECTORS; + + if ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.SupportedFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_PALETTE_ENCODING) != 0) + config.FeatureFlags |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_PALETTE_ENCODING; + + if ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.SupportedFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTRA_BLOCK_COPY) != 0) + config.FeatureFlags |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTRA_BLOCK_COPY; + + if ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.SupportedFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_LOOP_FILTER_DELTAS) != 0) + config.FeatureFlags |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_LOOP_FILTER_DELTAS; + + if ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.SupportedFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_QUANTIZATION_DELTAS) != 0) + config.FeatureFlags |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_QUANTIZATION_DELTAS; + + if ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.SupportedFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_QUANTIZATION_MATRIX) != 0) + config.FeatureFlags |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_QUANTIZATION_MATRIX; + + if ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.SupportedFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_REDUCED_TX_SET) != 0) + config.FeatureFlags |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_REDUCED_TX_SET; + + if ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.SupportedFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_MOTION_MODE_SWITCHABLE) != 0) + config.FeatureFlags |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_MOTION_MODE_SWITCHABLE; + + if ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.SupportedFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_ALLOW_HIGH_PRECISION_MV) != 0) + config.FeatureFlags |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_ALLOW_HIGH_PRECISION_MV; + + // + // Add any missing mandatory/required features we didn't enable before + // + if ((config.FeatureFlags & + pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags) != + pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags) { + debug_printf( + "[d3d12_video_encoder_convert_av1_codec_configuration] Adding required by caps but not selected already in " + "config.FeatureFlags...\n"); + + if (((config.FeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_128x128_SUPERBLOCK) == 0) && + ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_128x128_SUPERBLOCK) != 0)) { + pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.RequiredNotRequestedFeatureFlags |= + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_128x128_SUPERBLOCK; + debug_printf( + "[d3d12_video_encoder_convert_av1_codec_configuration] Adding " + "D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_128x128_SUPERBLOCK required by caps but not selected already in " + "config.FeatureFlags...\n"); + } + + if (((config.FeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_FILTER_INTRA) == 0) && + ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_FILTER_INTRA) != 0)) { + pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.RequiredNotRequestedFeatureFlags |= + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_FILTER_INTRA; + debug_printf( + "[d3d12_video_encoder_convert_av1_codec_configuration] " + "D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_FILTER_INTRA Adding required by caps but not selected already in " + "config.FeatureFlags...\n"); + } + + if (((config.FeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTRA_EDGE_FILTER) == 0) && + ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTRA_EDGE_FILTER) != 0)) { + pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.RequiredNotRequestedFeatureFlags |= + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTRA_EDGE_FILTER; + debug_printf( + "[d3d12_video_encoder_convert_av1_codec_configuration] Adding " + "D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTRA_EDGE_FILTER required by caps but not selected already in " + "config.FeatureFlags...\n"); + } + + if (((config.FeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTERINTRA_COMPOUND) == 0) && + ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTERINTRA_COMPOUND) != 0)) { + pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.RequiredNotRequestedFeatureFlags |= + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTERINTRA_COMPOUND; + debug_printf( + "[d3d12_video_encoder_convert_av1_codec_configuration] Adding " + "D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTERINTRA_COMPOUND required by caps but not selected already in " + "config.FeatureFlags...\n"); + } + + if (((config.FeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_MASKED_COMPOUND) == 0) && + ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_MASKED_COMPOUND) != 0)) { + pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.RequiredNotRequestedFeatureFlags |= + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_MASKED_COMPOUND; + debug_printf( + "[d3d12_video_encoder_convert_av1_codec_configuration] Adding " + "D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_MASKED_COMPOUND required by caps but not selected already in " + "config.FeatureFlags...\n"); + } + + if (((config.FeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_WARPED_MOTION) == 0) && + ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_WARPED_MOTION) != 0)) { + pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.RequiredNotRequestedFeatureFlags |= + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_WARPED_MOTION; + debug_printf( + "[d3d12_video_encoder_convert_av1_codec_configuration] " + "D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_WARPED_MOTION Adding required by caps but not selected already in " + "config.FeatureFlags...\n"); + } + + if (((config.FeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_DUAL_FILTER) == 0) && + ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_DUAL_FILTER) != 0)) { + pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.RequiredNotRequestedFeatureFlags |= + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_DUAL_FILTER; + debug_printf("[d3d12_video_encoder_convert_av1_codec_configuration] == Adding required by caps but not " + "selected already in " + "config.FeatureFlags...\n"); + } + + if (((config.FeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_JNT_COMP) == 0) && + ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_JNT_COMP) != 0)) { + pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.RequiredNotRequestedFeatureFlags |= + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_JNT_COMP; + debug_printf("[d3d12_video_encoder_convert_av1_codec_configuration] 0 Adding required by caps but not " + "selected already in " + "config.FeatureFlags...\n"); + } + + if (((config.FeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_FORCED_INTEGER_MOTION_VECTORS) == 0) && + ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_FORCED_INTEGER_MOTION_VECTORS) != 0)) { + pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.RequiredNotRequestedFeatureFlags |= + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_FORCED_INTEGER_MOTION_VECTORS; + debug_printf( + "[d3d12_video_encoder_convert_av1_codec_configuration] Adding required by " + "D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_FORCED_INTEGER_MOTION_VECTORS caps but not selected already in " + "config.FeatureFlags...\n"); + } + + if (((config.FeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_SUPER_RESOLUTION) == 0) && + ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_SUPER_RESOLUTION) != 0)) { + pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.RequiredNotRequestedFeatureFlags |= + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_SUPER_RESOLUTION; + debug_printf( + "[d3d12_video_encoder_convert_av1_codec_configuration] Adding " + "D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_SUPER_RESOLUTION required by caps but not selected already in " + "config.FeatureFlags...\n"); + } + + if (((config.FeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_LOOP_RESTORATION_FILTER) == 0) && + ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_LOOP_RESTORATION_FILTER) != 0)) { + pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.RequiredNotRequestedFeatureFlags |= + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_LOOP_RESTORATION_FILTER; + debug_printf( + "[d3d12_video_encoder_convert_av1_codec_configuration] Adding " + "D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_LOOP_RESTORATION_FILTER required by caps but not selected already in " + "config.FeatureFlags...\n"); + } + + if (((config.FeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_PALETTE_ENCODING) == 0) && + ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_PALETTE_ENCODING) != 0)) { + pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.RequiredNotRequestedFeatureFlags |= + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_PALETTE_ENCODING; + debug_printf( + "[d3d12_video_encoder_convert_av1_codec_configuration] Adding " + "D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_PALETTE_ENCODING required by caps but not selected already in " + "config.FeatureFlags...\n"); + } + + if (((config.FeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_CDEF_FILTERING) == 0) && + ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_CDEF_FILTERING) != 0)) { + pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.RequiredNotRequestedFeatureFlags |= + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_CDEF_FILTERING; + debug_printf( + "[d3d12_video_encoder_convert_av1_codec_configuration] " + "D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_CDEF_FILTERING Adding required by caps but not selected already in " + "config.FeatureFlags...\n"); + } + + if (((config.FeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTRA_BLOCK_COPY) == 0) && + ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTRA_BLOCK_COPY) != 0)) { + pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.RequiredNotRequestedFeatureFlags |= + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTRA_BLOCK_COPY; + debug_printf( + "[d3d12_video_encoder_convert_av1_codec_configuration] Adding " + "D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTRA_BLOCK_COPY required by caps but not selected already in " + "config.FeatureFlags...\n"); + } + + if (((config.FeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_FRAME_REFERENCE_MOTION_VECTORS) == 0) && + ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_FRAME_REFERENCE_MOTION_VECTORS) != 0)) { + pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.RequiredNotRequestedFeatureFlags |= + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_FRAME_REFERENCE_MOTION_VECTORS; + debug_printf( + "[d3d12_video_encoder_convert_av1_codec_configuration] Adding required by " + "D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_FRAME_REFERENCE_MOTION_VECTORS caps but not selected already in " + "config.FeatureFlags...\n"); + } + + if (((config.FeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_ORDER_HINT_TOOLS) == 0) && + ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_ORDER_HINT_TOOLS) != 0)) { + pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.RequiredNotRequestedFeatureFlags |= + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_ORDER_HINT_TOOLS; + debug_printf( + "[d3d12_video_encoder_convert_av1_codec_configuration] Adding " + "D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_ORDER_HINT_TOOLS required by caps but not selected already in " + "config.FeatureFlags...\n"); + } + + if (((config.FeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_AUTO_SEGMENTATION) == 0) && + ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_AUTO_SEGMENTATION) != 0)) { + pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.RequiredNotRequestedFeatureFlags |= + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_AUTO_SEGMENTATION; + debug_printf( + "[d3d12_video_encoder_convert_av1_codec_configuration] Adding " + "D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_AUTO_SEGMENTATION required by caps but not selected already in " + "config.FeatureFlags...\n"); + } + + if (((config.FeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_CUSTOM_SEGMENTATION) == 0) && + ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_CUSTOM_SEGMENTATION) != 0)) { + pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.RequiredNotRequestedFeatureFlags |= + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_CUSTOM_SEGMENTATION; + debug_printf( + "[d3d12_video_encoder_convert_av1_codec_configuration] Adding " + "D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_CUSTOM_SEGMENTATION required by caps but not selected already in " + "config.FeatureFlags...\n"); + } + + if (((config.FeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_LOOP_FILTER_DELTAS) == 0) && + ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_LOOP_FILTER_DELTAS) != 0)) { + pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.RequiredNotRequestedFeatureFlags |= + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_LOOP_FILTER_DELTAS; + debug_printf( + "[d3d12_video_encoder_convert_av1_codec_configuration] Adding " + "D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_LOOP_FILTER_DELTAS required by caps but not selected already in " + "config.FeatureFlags...\n"); + } + + if (((config.FeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_QUANTIZATION_DELTAS) == 0) && + ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_QUANTIZATION_DELTAS) != 0)) { + pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.RequiredNotRequestedFeatureFlags |= + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_QUANTIZATION_DELTAS; + debug_printf( + "[d3d12_video_encoder_convert_av1_codec_configuration] Adding " + "D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_QUANTIZATION_DELTAS required by caps but not selected already in " + "config.FeatureFlags...\n"); + } + + if (((config.FeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_QUANTIZATION_MATRIX) == 0) && + ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_QUANTIZATION_MATRIX) != 0)) { + pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.RequiredNotRequestedFeatureFlags |= + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_QUANTIZATION_MATRIX; + debug_printf( + "[d3d12_video_encoder_convert_av1_codec_configuration] Adding " + "D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_QUANTIZATION_MATRIX required by caps but not selected already in " + "config.FeatureFlags...\n"); + } + + if (((config.FeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_REDUCED_TX_SET) == 0) && + ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_REDUCED_TX_SET) != 0)) { + pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.RequiredNotRequestedFeatureFlags |= + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_REDUCED_TX_SET; + debug_printf( + "[d3d12_video_encoder_convert_av1_codec_configuration] " + "D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_REDUCED_TX_SET Adding required by caps but not selected already in " + "config.FeatureFlags...\n"); + } + + if (((config.FeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_MOTION_MODE_SWITCHABLE) == 0) && + ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_MOTION_MODE_SWITCHABLE) != 0)) { + pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.RequiredNotRequestedFeatureFlags |= + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_MOTION_MODE_SWITCHABLE; + debug_printf( + "[d3d12_video_encoder_convert_av1_codec_configuration] Adding " + "D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_MOTION_MODE_SWITCHABLE required by caps but not selected already in " + "config.FeatureFlags...\n"); + } + + if (((config.FeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_ALLOW_HIGH_PRECISION_MV) == 0) && + ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_ALLOW_HIGH_PRECISION_MV) != 0)) { + pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.RequiredNotRequestedFeatureFlags |= + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_ALLOW_HIGH_PRECISION_MV; + debug_printf( + "[d3d12_video_encoder_convert_av1_codec_configuration] Adding " + "D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_ALLOW_HIGH_PRECISION_MV required by caps but not selected already in " + "config.FeatureFlags...\n"); + } + + // Enable all required flags previously not selected + config.FeatureFlags |= + pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags; + } + + // Check config.FeatureFlags against SupportedFeatureFlags and assign is_supported + if ((config.FeatureFlags & + pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.SupportedFeatureFlags) != + config.FeatureFlags) { + debug_printf( + "[d3d12_video_encoder_convert_av1_codec_configuration] AV1 Configuration flags requested 0x%x not supported " + "by " + "m_AV1CodecCaps.SupportedFeatureFlags 0x%x\n", + config.FeatureFlags, + pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.SupportedFeatureFlags); + + is_supported = false; + } + + return config; +} + +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) +{ + struct pipe_av1_enc_picture_desc *av1Pic = (struct pipe_av1_enc_picture_desc *) picture; + + // Reset reconfig dirty flags + pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags = d3d12_video_encoder_config_dirty_flag_none; + // Reset sequence changes flags + pD3D12Enc->m_currentEncodeConfig.m_seqFlags = D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAG_NONE; + + // Set codec + if (pD3D12Enc->m_currentEncodeConfig.m_encoderCodecDesc != D3D12_VIDEO_ENCODER_CODEC_AV1) { + pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_codec; + } + pD3D12Enc->m_currentEncodeConfig.m_encoderCodecDesc = D3D12_VIDEO_ENCODER_CODEC_AV1; + + // Set input format + DXGI_FORMAT targetFmt = d3d12_get_format(srcTexture->buffer_format); + if (pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo.Format != targetFmt) { + pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_input_format; + } + + pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo = {}; + pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo.Format = targetFmt; + HRESULT hr = + pD3D12Enc->m_pD3D12Screen->dev->CheckFeatureSupport(D3D12_FEATURE_FORMAT_INFO, + &pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo, + sizeof(pD3D12Enc->m_currentEncodeConfig.m_encodeFormatInfo)); + if (FAILED(hr)) { + debug_printf("CheckFeatureSupport failed with HR 0x%x\n", hr); + return false; + } + + // Set resolution (ie. frame_size) + if ((pD3D12Enc->m_currentEncodeConfig.m_currentResolution.Width != srcTexture->width) || + (pD3D12Enc->m_currentEncodeConfig.m_currentResolution.Height != srcTexture->height)) { + pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_resolution; + } + pD3D12Enc->m_currentEncodeConfig.m_currentResolution.Width = srcTexture->width; + pD3D12Enc->m_currentEncodeConfig.m_currentResolution.Height = srcTexture->height; + + // render_size + pD3D12Enc->m_currentEncodeConfig.m_FrameCroppingCodecConfig.right = av1Pic->frame_width; + pD3D12Enc->m_currentEncodeConfig.m_FrameCroppingCodecConfig.bottom = av1Pic->frame_height; + + // Set profile + auto targetProfile = d3d12_video_encoder_convert_profile_to_d3d12_enc_profile_av1(pD3D12Enc->base.profile); + if (pD3D12Enc->m_currentEncodeConfig.m_encoderProfileDesc.m_AV1Profile != targetProfile) { + pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_profile; + } + pD3D12Enc->m_currentEncodeConfig.m_encoderProfileDesc.m_AV1Profile = targetProfile; + + // Set level and tier + D3D12_VIDEO_ENCODER_AV1_LEVELS targetLevel = {}; + D3D12_VIDEO_ENCODER_AV1_TIER targetTier = {}; + d3d12_video_encoder_convert_spec_to_d3d12_level_av1(av1Pic->seq.level, targetLevel); + d3d12_video_encoder_convert_spec_to_d3d12_tier_av1(av1Pic->seq.tier, targetTier); + + if ((pD3D12Enc->m_currentEncodeConfig.m_encoderLevelDesc.m_AV1LevelSetting.Level != targetLevel) || + (pD3D12Enc->m_currentEncodeConfig.m_encoderLevelDesc.m_AV1LevelSetting.Tier != targetTier)) { + pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_level; + } + pD3D12Enc->m_currentEncodeConfig.m_encoderLevelDesc.m_AV1LevelSetting.Tier = targetTier; + pD3D12Enc->m_currentEncodeConfig.m_encoderLevelDesc.m_AV1LevelSetting.Level = targetLevel; + + // + // Validate caps support returned values against current settings + // + if (pD3D12Enc->m_currentEncodeConfig.m_encoderProfileDesc.m_AV1Profile != + pD3D12Enc->m_currentEncodeCapabilities.m_encoderSuggestedProfileDesc.m_AV1Profile) { + debug_printf("[d3d12_video_encoder_av1] Warning: Requested D3D12_VIDEO_ENCODER_PROFILE_AV1 by upper layer: %d " + "mismatches UMD suggested D3D12_VIDEO_ENCODER_PROFILE_AV1: %d\n", + pD3D12Enc->m_currentEncodeConfig.m_encoderProfileDesc.m_AV1Profile, + pD3D12Enc->m_currentEncodeCapabilities.m_encoderSuggestedProfileDesc.m_AV1Profile); + } + + if ((pD3D12Enc->m_currentEncodeConfig.m_encoderLevelDesc.m_AV1LevelSetting.Level != + pD3D12Enc->m_currentEncodeCapabilities.m_encoderLevelSuggestedDesc.m_AV1LevelSetting.Level) || + (pD3D12Enc->m_currentEncodeConfig.m_encoderLevelDesc.m_AV1LevelSetting.Tier != + pD3D12Enc->m_currentEncodeCapabilities.m_encoderLevelSuggestedDesc.m_AV1LevelSetting.Tier)) { + debug_printf( + "[d3d12_video_encoder_av1] Warning: Requested D3D12_VIDEO_ENCODER_LEVELS_AV1 by upper layer: level %d tier %d " + "mismatches UMD suggested D3D12_VIDEO_ENCODER_LEVELS_AV1: level %d tier %d\n", + pD3D12Enc->m_currentEncodeConfig.m_encoderLevelDesc.m_AV1LevelSetting.Level, + pD3D12Enc->m_currentEncodeConfig.m_encoderLevelDesc.m_AV1LevelSetting.Tier, + pD3D12Enc->m_currentEncodeCapabilities.m_encoderLevelSuggestedDesc.m_AV1LevelSetting.Level, + pD3D12Enc->m_currentEncodeCapabilities.m_encoderLevelSuggestedDesc.m_AV1LevelSetting.Tier); + } + + // Set codec config + bool is_supported = false; + auto targetCodecConfig = d3d12_video_encoder_convert_av1_codec_configuration(pD3D12Enc, av1Pic, is_supported); + if (!is_supported) { + return false; + } + + if (memcmp(&pD3D12Enc->m_currentEncodeConfig.m_encoderCodecSpecificConfigDesc.m_AV1Config, + &targetCodecConfig, + sizeof(D3D12_VIDEO_ENCODER_AV1_CODEC_CONFIGURATION)) != 0) { + pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_codec_config; + } + pD3D12Enc->m_currentEncodeConfig.m_encoderCodecSpecificConfigDesc.m_AV1Config = targetCodecConfig; + + // Set rate control + d3d12_video_encoder_update_current_rate_control_av1(pD3D12Enc, av1Pic); + + // Set tiles config + if (!d3d12_video_encoder_negotiate_current_av1_tiles_configuration(pD3D12Enc, av1Pic)) { + debug_printf("d3d12_video_encoder_negotiate_current_av1_tiles_configuration failed!\n"); + return false; + } + + // Set GOP config + if (!d3d12_video_encoder_update_av1_gop_configuration(pD3D12Enc, av1Pic)) { + debug_printf("d3d12_video_encoder_update_av1_gop_configuration failed!\n"); + return false; + } + + // m_currentEncodeConfig.m_encoderPicParamsDesc pic params are set in d3d12_video_encoder_reconfigure_encoder_objects + // after re-allocating objects if needed + + // Set motion estimation config + auto targetMotionLimit = d3d12_video_encoder_convert_av1_motion_configuration(pD3D12Enc, av1Pic); + if (pD3D12Enc->m_currentEncodeConfig.m_encoderMotionPrecisionLimit != targetMotionLimit) { + pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= + d3d12_video_encoder_config_dirty_flag_motion_precision_limit; + } + pD3D12Enc->m_currentEncodeConfig.m_encoderMotionPrecisionLimit = targetMotionLimit; + + // Will call for d3d12 driver support based on the initial requested (non codec specific) features, then + // try to fallback if any of them is not supported and return the negotiated d3d12 settings + 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_av1] After negotiating caps, D3D12_FEATURE_VIDEO_ENCODER_SUPPORT1 " + "arguments are not supported - " + "ValidationFlags: 0x%x - SupportFlags: 0x%x\n", + capEncoderSupportData1.ValidationFlags, + capEncoderSupportData1.SupportFlags); + return false; + } + + pD3D12Enc->m_currentEncodeCapabilities.m_MaxSlicesInOutput = (av1Pic->tile_cols * av1Pic->tile_rows); + if (pD3D12Enc->m_currentEncodeCapabilities.m_MaxSlicesInOutput > + pD3D12Enc->m_currentEncodeCapabilities.m_currentResolutionSupportCaps.MaxSubregionsNumber) { + debug_printf("[d3d12_video_encoder_av1] Desired number of subregions is not supported (higher than max " + "reported slice number in query caps)\n."); + return false; + } + + return true; +} + +/* + * Called at begin_frame record time + */ +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) +{ + struct pipe_av1_enc_picture_desc *pAV1Pic = (struct pipe_av1_enc_picture_desc *) picture; + + // Output param bUsedAsReference + bUsedAsReference = (pAV1Pic->refresh_frame_flags != 0); + + // D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAGS Flags; + picParams.pAV1PicData->Flags = D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_NONE; + + if (pAV1Pic->error_resilient_mode) + picParams.pAV1PicData->Flags |= D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ENABLE_ERROR_RESILIENT_MODE; + + if (pAV1Pic->disable_cdf_update) + picParams.pAV1PicData->Flags |= D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_DISABLE_CDF_UPDATE; + + if (pAV1Pic->palette_mode_enable) + picParams.pAV1PicData->Flags |= D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ENABLE_PALETTE_ENCODING; + + // Override if required feature + if ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_PALETTE_ENCODING) != 0) { + debug_printf("[d3d12_video_encoder_update_current_frame_pic_params_info_av1] Overriding required feature " + "D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ENABLE_PALETTE_ENCODING\n"); + picParams.pAV1PicData->Flags |= D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ENABLE_PALETTE_ENCODING; + } + + if (pAV1Pic->skip_mode_present) + picParams.pAV1PicData->Flags |= D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ENABLE_SKIP_MODE; + + if ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_SKIP_MODE_PRESENT) != 0) { + debug_printf("[d3d12_video_encoder_update_current_frame_pic_params_info_av1] Overriding required feature " + "D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ENABLE_SKIP_MODE\n"); + picParams.pAV1PicData->Flags |= D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ENABLE_SKIP_MODE; + } + + if (pAV1Pic->use_ref_frame_mvs) + picParams.pAV1PicData->Flags |= D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_FRAME_REFERENCE_MOTION_VECTORS; + + // Override if required feature + if ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_FRAME_REFERENCE_MOTION_VECTORS) != 0) { + debug_printf("[d3d12_video_encoder_update_current_frame_pic_params_info_av1] Overriding required feature " + "D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_FRAME_REFERENCE_MOTION_VECTORS\n"); + picParams.pAV1PicData->Flags |= D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_FRAME_REFERENCE_MOTION_VECTORS; + } + + // No pipe flag for force_integer_mv (D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_FORCE_INTEGER_MOTION_VECTORS) + // choose default based on required/supported underlying codec flags + if ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_FORCED_INTEGER_MOTION_VECTORS) != 0) { + debug_printf("[d3d12_video_encoder_update_current_frame_pic_params_info_av1] Overriding required feature " + "D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_FORCE_INTEGER_MOTION_VECTORS\n"); + picParams.pAV1PicData->Flags |= D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_FORCE_INTEGER_MOTION_VECTORS; + } + + if (pAV1Pic->allow_intrabc) + picParams.pAV1PicData->Flags |= D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ALLOW_INTRA_BLOCK_COPY; + + // Override if required feature + if ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTRA_BLOCK_COPY) != 0) { + debug_printf("[d3d12_video_encoder_update_current_frame_pic_params_info_av1] Overriding required feature " + "D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ALLOW_INTRA_BLOCK_COPY\n"); + picParams.pAV1PicData->Flags |= D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ALLOW_INTRA_BLOCK_COPY; + } + + if (pAV1Pic->use_superres) + picParams.pAV1PicData->Flags |= D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_USE_SUPER_RESOLUTION; + + if (pAV1Pic->disable_frame_end_update_cdf) + picParams.pAV1PicData->Flags |= D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_DISABLE_FRAME_END_UPDATE_CDF; + + // No pipe flag for D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ENABLE_FRAME_SEGMENTATION_AUTO + // choose default based on required/supported underlying codec flags + if ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_AUTO_SEGMENTATION) != 0) { + debug_printf("[d3d12_video_encoder_update_current_frame_pic_params_info_av1] Overriding required feature " + "D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ENABLE_FRAME_SEGMENTATION_AUTO\n"); + picParams.pAV1PicData->Flags |= D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ENABLE_FRAME_SEGMENTATION_AUTO; + } + + // No pipe flag for D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ENABLE_FRAME_SEGMENTATION_CUSTOM + // choose default based on required/supported underlying codec flags + if ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_CUSTOM_SEGMENTATION) != 0) { + debug_printf("[d3d12_video_encoder_update_current_frame_pic_params_info_av1] Overriding required feature " + "D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ENABLE_FRAME_SEGMENTATION_CUSTOM\n"); + picParams.pAV1PicData->Flags |= D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ENABLE_FRAME_SEGMENTATION_CUSTOM; + assert(false); // Not implemented + } + + // No pipe flag for allow_warped_motion (D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ENABLE_WARPED_MOTION) + // choose default based on required/supported underlying codec flags + if ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_WARPED_MOTION) != 0) { + debug_printf("[d3d12_video_encoder_update_current_frame_pic_params_info_av1] Overriding required feature " + "D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ENABLE_WARPED_MOTION\n"); + picParams.pAV1PicData->Flags |= D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ENABLE_WARPED_MOTION; + } + + if (pAV1Pic->reduced_tx_set) + picParams.pAV1PicData->Flags |= D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_REDUCED_TX_SET; + + // Override if required feature + if ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_REDUCED_TX_SET) != 0) { + debug_printf("[d3d12_video_encoder_update_current_frame_pic_params_info_av1] Overriding required feature " + "D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_REDUCED_TX_SET\n"); + picParams.pAV1PicData->Flags |= D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_REDUCED_TX_SET; + } + + // Only enable if supported + if ((pAV1Pic->allow_high_precision_mv) && + (pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.SupportedFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_ALLOW_HIGH_PRECISION_MV) != 0) { + picParams.pAV1PicData->Flags |= D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ALLOW_HIGH_PRECISION_MV; + } + + // Override if required feature + if ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_ALLOW_HIGH_PRECISION_MV) != 0) { + debug_printf("[d3d12_video_encoder_update_current_frame_pic_params_info_av1] Overriding required feature " + "D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ALLOW_HIGH_PRECISION_MV\n"); + picParams.pAV1PicData->Flags |= D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ALLOW_HIGH_PRECISION_MV; + } + + // No pipe flag for is_motion_mode_switchable (D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_MOTION_MODE_SWITCHABLE) + // choose default based on required/supported underlying codec flags + if ((pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1CodecCaps.RequiredFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_MOTION_MODE_SWITCHABLE) != 0) { + debug_printf("[d3d12_video_encoder_update_current_frame_pic_params_info_av1] Overriding required feature " + "D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_MOTION_MODE_SWITCHABLE\n"); + picParams.pAV1PicData->Flags |= D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_MOTION_MODE_SWITCHABLE; + } + + // D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE FrameType; + // AV1 spec matches w/D3D12 enum definition + picParams.pAV1PicData->FrameType = static_cast(pAV1Pic->frame_type); + + if (picParams.pAV1PicData->FrameType == D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_KEY_FRAME) + debug_printf("Encoding FrameType: D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_KEY_FRAME\n"); + if (picParams.pAV1PicData->FrameType == D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_INTER_FRAME) + debug_printf("Encoding FrameType: D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_INTER_FRAME\n"); + if (picParams.pAV1PicData->FrameType == D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_INTRA_ONLY_FRAME) + debug_printf("Encoding FrameType: D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_INTRA_ONLY_FRAME\n"); + if (picParams.pAV1PicData->FrameType == D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_SWITCH_FRAME) + debug_printf("Encoding FrameType: D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_SWITCH_FRAME\n"); + + // D3D12_VIDEO_ENCODER_AV1_COMP_PREDICTION_TYPE CompoundPredictionType; + picParams.pAV1PicData->CompoundPredictionType = (pAV1Pic->compound_reference_mode == 0) ? + D3D12_VIDEO_ENCODER_AV1_COMP_PREDICTION_TYPE_SINGLE_REFERENCE : + D3D12_VIDEO_ENCODER_AV1_COMP_PREDICTION_TYPE_COMPOUND_REFERENCE; + + // D3D12_VIDEO_ENCODER_AV1_INTERPOLATION_FILTERS InterpolationFilter; + // AV1 spec matches w/D3D12 enum definition + picParams.pAV1PicData->InterpolationFilter = + static_cast(pAV1Pic->interpolation_filter); + + // D3D12_VIDEO_ENCODER_AV1_RESTORATION_CONFIG FrameRestorationConfig; + // AV1 spec matches w/D3D12 FrameRestorationType enum definition + + picParams.pAV1PicData->FrameRestorationConfig.FrameRestorationType[0] = + static_cast(pAV1Pic->restoration.yframe_restoration_type); + picParams.pAV1PicData->FrameRestorationConfig.FrameRestorationType[1] = + static_cast(pAV1Pic->restoration.cbframe_restoration_type); + picParams.pAV1PicData->FrameRestorationConfig.FrameRestorationType[2] = + static_cast(pAV1Pic->restoration.crframe_restoration_type); + + if (picParams.pAV1PicData->FrameRestorationConfig.FrameRestorationType[0] != + D3D12_VIDEO_ENCODER_AV1_RESTORATION_TYPE_DISABLED) { + picParams.pAV1PicData->FrameRestorationConfig.LoopRestorationPixelSize[0] = + d3d12_video_encoder_looprestorationsize_uint_to_d3d12_av1(1 << (6 + pAV1Pic->restoration.lr_unit_shift)); + } + + if (picParams.pAV1PicData->FrameRestorationConfig.FrameRestorationType[1] != + D3D12_VIDEO_ENCODER_AV1_RESTORATION_TYPE_DISABLED) { + picParams.pAV1PicData->FrameRestorationConfig.LoopRestorationPixelSize[1] = + d3d12_video_encoder_looprestorationsize_uint_to_d3d12_av1( + 1 << (6 + pAV1Pic->restoration.lr_unit_shift - pAV1Pic->restoration.lr_uv_shift)); + } + + if (picParams.pAV1PicData->FrameRestorationConfig.FrameRestorationType[2] != + D3D12_VIDEO_ENCODER_AV1_RESTORATION_TYPE_DISABLED) { + picParams.pAV1PicData->FrameRestorationConfig.LoopRestorationPixelSize[2] = + d3d12_video_encoder_looprestorationsize_uint_to_d3d12_av1( + 1 << (6 + pAV1Pic->restoration.lr_unit_shift - pAV1Pic->restoration.lr_uv_shift)); + } + + // D3D12_VIDEO_ENCODER_AV1_TX_MODE TxMode; + // AV1 spec matches w/D3D12 enum definition + picParams.pAV1PicData->TxMode = static_cast(pAV1Pic->tx_mode); + + // UINT SuperResDenominator; + picParams.pAV1PicData->SuperResDenominator = pAV1Pic->superres_scale_denominator; + + // UINT OrderHint; + picParams.pAV1PicData->OrderHint = pAV1Pic->order_hint; + + // UINT PictureIndex - Substract the last_key_frame_num to make it modulo KEY frame + picParams.pAV1PicData->PictureIndex = pAV1Pic->frame_num - pAV1Pic->last_key_frame_num; + + // UINT TemporalLayerIndexPlus1; + assert(pAV1Pic->temporal_id == pAV1Pic->tg_obu_header.temporal_id); + picParams.pAV1PicData->TemporalLayerIndexPlus1 = pAV1Pic->temporal_id + 1; + + // UINT SpatialLayerIndexPlus1; + picParams.pAV1PicData->SpatialLayerIndexPlus1 = pAV1Pic->tg_obu_header.spatial_id + 1; + + // + // Reference Pictures + // + { + for (uint8_t i = 0; i < ARRAY_SIZE(picParams.pAV1PicData->ReferenceIndices); i++) { + picParams.pAV1PicData->ReferenceIndices[i] = pAV1Pic->ref_frame_idx[i]; + } + + bool FrameIsIntra = (picParams.pAV1PicData->FrameType == D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_INTRA_ONLY_FRAME || + picParams.pAV1PicData->FrameType == D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_KEY_FRAME); + if (FrameIsIntra) + picParams.pAV1PicData->PrimaryRefFrame = 7; /* PRIMARY_REF_NONE */ + else + picParams.pAV1PicData->PrimaryRefFrame = pAV1Pic->primary_ref_frame; + debug_printf("App requested primary_ref_frame: %" PRIu32 "\n", pAV1Pic->primary_ref_frame); + picParams.pAV1PicData->RefreshFrameFlags = pAV1Pic->refresh_frame_flags; + } + + // D3D12_VIDEO_ENCODER_CODEC_AV1_LOOP_FILTER_CONFIG LoopFilter; + picParams.pAV1PicData->LoopFilter.LoopFilterLevel[0] = pAV1Pic->loop_filter.filter_level[0]; + picParams.pAV1PicData->LoopFilter.LoopFilterLevel[1] = pAV1Pic->loop_filter.filter_level[1]; + picParams.pAV1PicData->LoopFilter.LoopFilterLevelU = pAV1Pic->loop_filter.filter_level_u; + picParams.pAV1PicData->LoopFilter.LoopFilterLevelV = pAV1Pic->loop_filter.filter_level_v; + picParams.pAV1PicData->LoopFilter.LoopFilterSharpnessLevel = pAV1Pic->loop_filter.sharpness_level; + picParams.pAV1PicData->LoopFilter.LoopFilterDeltaEnabled = pAV1Pic->loop_filter.mode_ref_delta_enabled; + picParams.pAV1PicData->LoopFilter.UpdateRefDelta = pAV1Pic->loop_filter.mode_ref_delta_update; + if (picParams.pAV1PicData->LoopFilter.UpdateRefDelta) { + for (uint8_t i = 0; i < ARRAY_SIZE(picParams.pAV1PicData->LoopFilter.RefDeltas); i++) { + picParams.pAV1PicData->LoopFilter.RefDeltas[i] = pAV1Pic->loop_filter.ref_deltas[i]; + } + } + picParams.pAV1PicData->LoopFilter.UpdateModeDelta = pAV1Pic->loop_filter.mode_ref_delta_update; + if (picParams.pAV1PicData->LoopFilter.UpdateModeDelta) { + for (uint8_t i = 0; i < ARRAY_SIZE(picParams.pAV1PicData->LoopFilter.ModeDeltas); i++) { + picParams.pAV1PicData->LoopFilter.ModeDeltas[i] = pAV1Pic->loop_filter.mode_deltas[i]; + } + } + + // D3D12_VIDEO_ENCODER_CODEC_AV1_LOOP_FILTER_DELTA_CONFIG LoopFilterDelta; + picParams.pAV1PicData->LoopFilterDelta.DeltaLFMulti = pAV1Pic->loop_filter.delta_lf_multi; + picParams.pAV1PicData->LoopFilterDelta.DeltaLFPresent = pAV1Pic->loop_filter.delta_lf_present; + picParams.pAV1PicData->LoopFilterDelta.DeltaLFRes = pAV1Pic->loop_filter.delta_lf_res; + + // D3D12_VIDEO_ENCODER_CODEC_AV1_QUANTIZATION_CONFIG Quantization; + picParams.pAV1PicData->Quantization.BaseQIndex = pAV1Pic->quantization.base_qindex; + picParams.pAV1PicData->Quantization.YDCDeltaQ = pAV1Pic->quantization.y_dc_delta_q; + picParams.pAV1PicData->Quantization.UDCDeltaQ = pAV1Pic->quantization.u_dc_delta_q; + picParams.pAV1PicData->Quantization.UACDeltaQ = pAV1Pic->quantization.u_ac_delta_q; + picParams.pAV1PicData->Quantization.VDCDeltaQ = pAV1Pic->quantization.v_dc_delta_q; + picParams.pAV1PicData->Quantization.VACDeltaQ = pAV1Pic->quantization.v_ac_delta_q; + picParams.pAV1PicData->Quantization.UsingQMatrix = pAV1Pic->quantization.using_qmatrix; + picParams.pAV1PicData->Quantization.QMY = pAV1Pic->quantization.qm_y; + picParams.pAV1PicData->Quantization.QMU = pAV1Pic->quantization.qm_u; + picParams.pAV1PicData->Quantization.QMV = pAV1Pic->quantization.qm_v; + + // D3D12_VIDEO_ENCODER_CODEC_AV1_QUANTIZATION_DELTA_CONFIG QuantizationDelta; + picParams.pAV1PicData->QuantizationDelta.DeltaQPresent = pAV1Pic->quantization.delta_q_present; + picParams.pAV1PicData->QuantizationDelta.DeltaQRes = pAV1Pic->quantization.delta_q_res; + + // D3D12_VIDEO_ENCODER_AV1_CDEF_CONFIG CDEF; + picParams.pAV1PicData->CDEF.CdefBits = pAV1Pic->cdef.cdef_bits; + picParams.pAV1PicData->CDEF.CdefDampingMinus3 = pAV1Pic->cdef.cdef_damping_minus_3; + for (uint32_t i = 0; i < 8; i++) { + picParams.pAV1PicData->CDEF.CdefYPriStrength[i] = (pAV1Pic->cdef.cdef_y_strengths[i] >> 2); + picParams.pAV1PicData->CDEF.CdefYSecStrength[i] = (pAV1Pic->cdef.cdef_y_strengths[i] & 0x03); + picParams.pAV1PicData->CDEF.CdefUVPriStrength[i] = (pAV1Pic->cdef.cdef_uv_strengths[i] >> 2); + picParams.pAV1PicData->CDEF.CdefUVSecStrength[i] = (pAV1Pic->cdef.cdef_uv_strengths[i] & 0x03); + } + + // + // Set values for mandatory but not requested features (ie. RequiredFeature flags reported by driver not requested + // by pipe) + // + + // For the ones that are more trivial (ie. 128x128 SB) than enabling their flag, and have more parameters to be + // set (ie Restoration Filter) Set defaults based on such feature driver d3d12 caps + + // These are a trivial flag enabling + // D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_128x128_SUPERBLOCK + // D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_FILTER_INTRA + // D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTRA_EDGE_FILTER + // D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTERINTRA_COMPOUND + // D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_MASKED_COMPOUND + // D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_WARPED_MOTION + // D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_DUAL_FILTER + // D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_JNT_COMP + // D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_FORCED_INTEGER_MOTION_VECTORS + // D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_SUPER_RESOLUTION + // D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_PALETTE_ENCODING + // D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTRA_BLOCK_COPY + // D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_FRAME_REFERENCE_MOTION_VECTORS + // D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_ORDER_HINT_TOOLS + // D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_REDUCED_TX_SET + // D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_MOTION_MODE_SWITCHABLE + // D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_ALLOW_HIGH_PRECISION_MV + + // If driver requires these, can use post encode values to enforce specific values + // D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_CDEF_FILTERING + // D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_AUTO_SEGMENTATION // Only useful with post encode values as driver + // controls D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_CUSTOM_SEGMENTATION // There are more Segmentation caps in + // D3D12_VIDEO_ENCODER_AV1_CODEC_CONFIGURATION_SUPPORT for these one too + // D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_LOOP_FILTER_DELTAS + // D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_QUANTIZATION_DELTAS + // D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_QUANTIZATION_MATRIX + + // If driver requires this one, use driver restoration caps to set default values + // D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_LOOP_RESTORATION_FILTER + if (pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.RequiredNotRequestedFeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_LOOP_RESTORATION_FILTER) { + debug_printf( + "[d3d12_video_encoder_update_current_frame_pic_params_info_av1] Adding default params for " + "D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_LOOP_RESTORATION_FILTER required by caps but not selected already in " + "config.FeatureFlags...\n"); + + // Set Y, U, V luma plane restoration params + for (uint32_t planeIdx = 0; planeIdx < 3; planeIdx++) { + // Let's see which filters and with which sizes are supported for planeIdx + bool foundSupportedFilter = false; + for (uint32_t filterIdx = 0; ((filterIdx < 3) && !foundSupportedFilter); filterIdx++) { + auto curFilterSupport = pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps + .m_AV1CodecCaps.SupportedRestorationParams[filterIdx][planeIdx]; + for (uint32_t curFilterSize = D3D12_VIDEO_ENCODER_AV1_RESTORATION_TILESIZE_32x32; + ((curFilterSize <= D3D12_VIDEO_ENCODER_AV1_RESTORATION_TILESIZE_256x256) && !foundSupportedFilter); + curFilterSize++) { + + // Check if there's support for curFilter on CurPlane and choose the first supported restoration size + // If yes, set restoration params for planeIdx + if (curFilterSupport & + (1 << (curFilterSize - 1))) { /* Converts D3D12_VIDEO_ENCODER_AV1_RESTORATION_TILESIZE definition + into D3D12_VIDEO_ENCODER_AV1_RESTORATION_SUPPORT_FLAGS definition */ + { + foundSupportedFilter = true; + // As per d3d12 spec filterIdx corresponds to D3D12_VIDEO_ENCODER_AV1_RESTORATION_TYPE - 1 + // (SupportedRestorationParams definition skips D3D12_VIDEO_ENCODER_AV1_RESTORATION_TYPE_DISABLED) + D3D12_VIDEO_ENCODER_AV1_RESTORATION_TYPE curFilter = + static_cast(filterIdx + 1); + + picParams.pAV1PicData->FrameRestorationConfig.FrameRestorationType[planeIdx] = curFilter; + picParams.pAV1PicData->FrameRestorationConfig.LoopRestorationPixelSize[planeIdx] = + static_cast( + curFilterSize); /* loop uses enum type */ + } + } + } + } + } + } + + // Save state snapshot from record time to resolve headers at get_feedback time + uint64_t current_metadata_slot = (pD3D12Enc->m_fenceValue % D3D12_VIDEO_ENC_METADATA_BUFFERS_COUNT); + pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_associatedEncodeCapabilities = + pD3D12Enc->m_currentEncodeCapabilities; + pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_associatedEncodeConfig = + pD3D12Enc->m_currentEncodeConfig; + pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_CodecSpecificData.AV1HeadersInfo.enable_frame_obu = + pAV1Pic->enable_frame_obu; + pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_CodecSpecificData.AV1HeadersInfo.obu_has_size_field = + (pAV1Pic->tg_obu_header.obu_has_size_field == 1); + // Disabling for now as the libva spec does not allow these but some apps send down anyway. It's possible in the future + // the libva spec may be retro-fitted to allow this given existing apps in the wild doing it. + // pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot] + // .m_CodecSpecificData.AV1HeadersInfo.temporal_delim_rendered = pAV1Pic->temporal_delim_rendered; +} + +void +fill_av1_seq_header(EncodedBitstreamResolvedMetadata &associatedMetadata, av1_seq_header_t *seq_header) +{ + // Set all zero by default + memset(seq_header, 0, sizeof(av1_seq_header_t)); + + seq_header->seq_profile = d3d12_video_encoder_convert_d3d12_profile_to_spec_profile_av1( + associatedMetadata.m_associatedEncodeConfig.m_encoderProfileDesc.m_AV1Profile); + // seq_header->still_picture; // coded in bitstream by default as 0 + // seq_header->reduced_still_picture_header; // coded in bitstream by default as 0 + // seq_header->timing_info_present_flag; // coded in bitstream by default as 0 + // seq_header->decoder_model_info_present_flag; // coded in bitstream by default as 0 + // seq_header->operating_points_cnt_minus_1; // memset default as 0 + // seq_header->operating_point_idc[32]; // memset default as 0 + d3d12_video_encoder_convert_d3d12_to_spec_level_av1( + associatedMetadata.m_associatedEncodeConfig.m_encoderLevelDesc.m_AV1LevelSetting.Level, + seq_header->seq_level_idx[0]); + d3d12_video_encoder_convert_d3d12_to_spec_tier_av1( + associatedMetadata.m_associatedEncodeConfig.m_encoderLevelDesc.m_AV1LevelSetting.Tier, + seq_header->seq_tier[0]); + // seq_header->decoder_model_present_for_this_op[32]; // memset default as 0 + // seq_header->initial_display_delay_present_flag; // coded in bitstream by default as 0 + // seq_header->initial_display_delay_minus_1[32]; + // seq_header->initial_display_delay_present_for_this_op[32] + // seq_header->frame_width_bits; // coded in bitstream by default as 16 + // seq_header->frame_height_bits; // coded in bitstream by default as 16 + + // frame_size (comes from input texture size) + seq_header->max_frame_width = associatedMetadata.m_associatedEncodeConfig.m_currentResolution.Width; + seq_header->max_frame_height = associatedMetadata.m_associatedEncodeConfig.m_currentResolution.Height; + + // seq_header->frame_id_numbers_present_flag; // coded in bitstream by default as 0 + seq_header->use_128x128_superblock = + ((associatedMetadata.m_associatedEncodeConfig.m_encoderCodecSpecificConfigDesc.m_AV1Config.FeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_128x128_SUPERBLOCK) != 0) ? + 1 : + 0; + seq_header->enable_filter_intra = + ((associatedMetadata.m_associatedEncodeConfig.m_encoderCodecSpecificConfigDesc.m_AV1Config.FeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_FILTER_INTRA) != 0) ? + 1 : + 0; + seq_header->enable_intra_edge_filter = + ((associatedMetadata.m_associatedEncodeConfig.m_encoderCodecSpecificConfigDesc.m_AV1Config.FeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTRA_EDGE_FILTER) != 0) ? + 1 : + 0; + seq_header->enable_interintra_compound = + ((associatedMetadata.m_associatedEncodeConfig.m_encoderCodecSpecificConfigDesc.m_AV1Config.FeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTERINTRA_COMPOUND) != 0) ? + 1 : + 0; + seq_header->enable_masked_compound = + ((associatedMetadata.m_associatedEncodeConfig.m_encoderCodecSpecificConfigDesc.m_AV1Config.FeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_MASKED_COMPOUND) != 0) ? + 1 : + 0; + seq_header->enable_warped_motion = + ((associatedMetadata.m_associatedEncodeConfig.m_encoderCodecSpecificConfigDesc.m_AV1Config.FeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_WARPED_MOTION) != 0) ? + 1 : + 0; + seq_header->enable_dual_filter = + ((associatedMetadata.m_associatedEncodeConfig.m_encoderCodecSpecificConfigDesc.m_AV1Config.FeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_DUAL_FILTER) != 0) ? + 1 : + 0; + seq_header->enable_order_hint = + ((associatedMetadata.m_associatedEncodeConfig.m_encoderCodecSpecificConfigDesc.m_AV1Config.FeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_ORDER_HINT_TOOLS) != 0) ? + 1 : + 0; + seq_header->enable_jnt_comp = + ((associatedMetadata.m_associatedEncodeConfig.m_encoderCodecSpecificConfigDesc.m_AV1Config.FeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_JNT_COMP) != 0) ? + 1 : + 0; + seq_header->enable_ref_frame_mvs = + ((associatedMetadata.m_associatedEncodeConfig.m_encoderCodecSpecificConfigDesc.m_AV1Config.FeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_FRAME_REFERENCE_MOTION_VECTORS) != 0) ? + 1 : + 0; + seq_header->seq_choose_screen_content_tools = 0; // coded in bitstream by default as 0 + seq_header->seq_force_screen_content_tools = 0; // coded in bitstream by default as 0 + seq_header->seq_choose_integer_mv = 0; // coded in bitstream by default as 0 + seq_header->seq_force_integer_mv = 0; // coded in bitstream by default as 0 + seq_header->order_hint_bits_minus1 = + associatedMetadata.m_associatedEncodeConfig.m_encoderCodecSpecificConfigDesc.m_AV1Config.OrderHintBitsMinus1; + seq_header->enable_superres = + ((associatedMetadata.m_associatedEncodeConfig.m_encoderCodecSpecificConfigDesc.m_AV1Config.FeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_SUPER_RESOLUTION) != 0) ? + 1 : + 0; + seq_header->enable_cdef = + ((associatedMetadata.m_associatedEncodeConfig.m_encoderCodecSpecificConfigDesc.m_AV1Config.FeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_CDEF_FILTERING) != 0) ? + 1 : + 0; + seq_header->enable_restoration = + ((associatedMetadata.m_associatedEncodeConfig.m_encoderCodecSpecificConfigDesc.m_AV1Config.FeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_LOOP_RESTORATION_FILTER) != 0) ? + 1 : + 0; + + seq_header->color_config.bit_depth = associatedMetadata.m_associatedEncodeConfig.m_encodeFormatInfo.Format; + // seq_header->color_config.mono_chrome; // coded in bitstream by default as 0 + // seq_header->color_config.color_description_present_flag; // memset default as 0 + // seq_header->color_config.color_primaries; // memset default as 0 + // seq_header->color_config.transfer_characteristics; // memset default as 0 + // seq_header->color_config.matrix_coefficients; // memset default as 0 + // seq_header->color_config.color_range; // memset default as 0 + seq_header->color_config.chroma_sample_position = 0; // CSP_UNKNOWN + seq_header->color_config.subsampling_x = 1; // DX12 4:2:0 only + seq_header->color_config.subsampling_y = 1; // DX12 4:2:0 only + seq_header->color_config.separate_uv_delta_q = 1; + + // seq_header->film_grain_params_present; // coded in bitstream by default as 1 +} + +void +fill_av1_pic_header(EncodedBitstreamResolvedMetadata &associatedMetadata, + av1_pic_header_t *pic_header, + const av1_seq_header_t *seqHdr, + const D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES *pParsedPostEncodeValues, + const D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_TILES *pParsedTilePartitions) +{ + // Set all zero by default + memset(pic_header, 0, sizeof(av1_pic_header_t)); + + { + pic_header->quantization_params = pParsedPostEncodeValues->Quantization; + + debug_printf("[d3d12_video_enc_av1] Parsing driver post encode " + "values...D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAG_QUANTIZATION\n"); + + debug_printf("Post encode quantization_params.BaseQIndex: %" PRIu64 "\n", + pic_header->quantization_params.BaseQIndex); + debug_printf("Post encode quantization_params.YDCDeltaQ: %" PRId64 "\n", + pic_header->quantization_params.YDCDeltaQ); + debug_printf("Post encode quantization_params.UDCDeltaQ: %" PRId64 "\n", + pic_header->quantization_params.UDCDeltaQ); + debug_printf("Post encode quantization_params.UACDeltaQ: %" PRId64 "\n", + pic_header->quantization_params.UACDeltaQ); + debug_printf("Post encode quantization_params.VDCDeltaQ: %" PRId64 "\n", + pic_header->quantization_params.VDCDeltaQ); + debug_printf("Post encode quantization_params.VACDeltaQ: %" PRId64 "\n", + pic_header->quantization_params.VACDeltaQ); + debug_printf("Post encode quantization_params.UsingQMatrix: %" PRIu64 "\n", + pic_header->quantization_params.UsingQMatrix); + debug_printf("Post encode quantization_params.QMY: %" PRIu64 "\n", pic_header->quantization_params.QMY); + debug_printf("Post encode quantization_params.QMU: %" PRIu64 "\n", pic_header->quantization_params.QMU); + debug_printf("Post encode quantization_params.QMV: %" PRIu64 "\n", pic_header->quantization_params.QMV); + } + + pic_header->segmentation_enabled = 0; + if ((associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData.Flags & + D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ENABLE_FRAME_SEGMENTATION_AUTO) != 0) { + + // The segmentation info comes from the driver + pic_header->segmentation_config = pParsedPostEncodeValues->SegmentationConfig; + pic_header->segmentation_enabled = (pic_header->segmentation_config.NumSegments != 0); + } + + { + debug_printf("[d3d12_video_enc_av1] Parsing driver post encode " + "values...D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAG_QUANTIZATION_DELTA\n"); + pic_header->delta_q_params = pParsedPostEncodeValues->QuantizationDelta; + + debug_printf("Post encode delta_q_params.DeltaQPresent: %" PRIu64 "\n", pic_header->delta_q_params.DeltaQPresent); + debug_printf("Post encode delta_q_params.DeltaQRes: %" PRIu64 "\n", pic_header->delta_q_params.DeltaQRes); + } + + { + debug_printf("[d3d12_video_enc_av1] Parsing driver post encode " + "values...D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAG_LOOP_FILTER_DELTA\n"); + pic_header->delta_lf_params = pParsedPostEncodeValues->LoopFilterDelta; + + debug_printf("Post encode delta_lf_params.DeltaLFMulti: %" PRIu64 "\n", pic_header->delta_lf_params.DeltaLFMulti); + debug_printf("Post encode delta_lf_params.DeltaLFPresent: %" PRIu64 "\n", + pic_header->delta_lf_params.DeltaLFPresent); + debug_printf("Post encode delta_lf_params.DeltaLFRes: %" PRIu64 "\n", pic_header->delta_lf_params.DeltaLFRes); + } + + { + debug_printf("[d3d12_video_enc_av1] Parsing driver post encode " + "values...D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAG_LOOP_FILTER\n"); + pic_header->loop_filter_params = pParsedPostEncodeValues->LoopFilter; + + debug_printf("Post encode loop_filter_params.LoopFilterDeltaEnabled: %" PRIu64 "\n", + pic_header->loop_filter_params.LoopFilterDeltaEnabled); + debug_printf("Post encode loop_filter_params.LoopFilterLevel[0]: %" PRIu64 "\n", + pic_header->loop_filter_params.LoopFilterLevel[0]); + debug_printf("Post encode loop_filter_params.LoopFilterLevel[1]: %" PRIu64 "\n", + pic_header->loop_filter_params.LoopFilterLevel[1]); + debug_printf("Post encode loop_filter_params.LoopFilterLevelU: %" PRIu64 "\n", + pic_header->loop_filter_params.LoopFilterLevelU); + debug_printf("Post encode loop_filter_params.LoopFilterLevelV: %" PRIu64 "\n", + pic_header->loop_filter_params.LoopFilterLevelV); + debug_printf("Post encode loop_filter_params.LoopFilterSharpnessLevel: %" PRIu64 "\n", + pic_header->loop_filter_params.LoopFilterSharpnessLevel); + debug_printf("Post encode loop_filter_params.ModeDeltas[0]: %" PRIu64 "\n", + pic_header->loop_filter_params.ModeDeltas[0]); + debug_printf("Post encode loop_filter_params.ModeDeltas[1]: %" PRIu64 "\n", + pic_header->loop_filter_params.ModeDeltas[1]); + for (uint8_t i = 0; i < 8; i++) { + debug_printf("Post encode loop_filter_params.RefDeltas[%d]: %" PRIu64 "\n", + i, + pic_header->loop_filter_params.RefDeltas[i]); + } + debug_printf("Post encode loop_filter_params.UpdateModeDelta: %" PRIu64 "\n", + pic_header->loop_filter_params.UpdateModeDelta); + debug_printf("Post encode loop_filter_params.UpdateRefDelta: %" PRIu64 "\n", + pic_header->loop_filter_params.UpdateRefDelta); + } + + { + debug_printf("[d3d12_video_enc_av1] Parsing driver post encode " + "values...D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAG_CDEF_DATA\n"); + pic_header->cdef_params = pParsedPostEncodeValues->CDEF; + + debug_printf("Post encode cdef_params.CdefBits: %" PRIu64 "\n", pic_header->cdef_params.CdefBits); + + debug_printf("Post encode cdef_params.CdefDampingMinus3: %" PRIu64 "\n", + pic_header->cdef_params.CdefDampingMinus3); + + for (uint8_t i = 0; i < 8; i++) { + debug_printf("Post encode cdef_params.CdefYPriStrength[%d]: %" PRIu64 "\n", + i, + pic_header->cdef_params.CdefYPriStrength[i]); + + debug_printf("Post encode cdef_params.CdefUVPriStrength[%d]: %" PRIu64 "\n", + i, + pic_header->cdef_params.CdefUVPriStrength[i]); + + debug_printf("Post encode cdef_params.CdefYSecStrength[%d]: %" PRIu64 "\n", + i, + pic_header->cdef_params.CdefYSecStrength[i]); + + debug_printf("Post encode cdef_params.CdefUVSecStrength[%d]: %" PRIu64 "\n", + i, + pic_header->cdef_params.CdefUVSecStrength[i]); + } + } + + { + debug_printf("[d3d12_video_enc_av1] Parsing driver post encode " + "values...D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAG_COMPOUND_PREDICTION_MODE\n"); + pic_header->reference_select = (pParsedPostEncodeValues->CompoundPredictionType == + D3D12_VIDEO_ENCODER_AV1_COMP_PREDICTION_TYPE_SINGLE_REFERENCE) ? + 0 : + 1; + debug_printf("Post encode reference_select: %" PRIu32 "\n", pic_header->reference_select); + } + + // if ((pevFlags & D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAG_CONTEXT_UPDATE_TILE_ID) != 0) + // We just copy the whole structure, the driver must copy it even if the pev flag is not set + pic_header->tile_info.tile_partition = *pParsedTilePartitions; + + { + debug_printf("[d3d12_video_enc_av1] Parsing driver post encode " + "values...D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAG_PRIMARY_REF_FRAME\n"); + pic_header->primary_ref_frame = static_cast(pParsedPostEncodeValues->PrimaryRefFrame); + debug_printf("Post encode primary_ref_frame: %" PRIu32 "\n", pic_header->primary_ref_frame); + } + + debug_printf("Post encode tile_info.tile_partition.ContextUpdateTileId: %" PRIu64 "\n", + pic_header->tile_info.tile_partition.ContextUpdateTileId); + debug_printf("Post encode tile_info.tile_partition.ColCount: %" PRIu64 "\n", + pic_header->tile_info.tile_partition.ColCount); + debug_printf("Post encode tile_info.tile_partition.RowCount: %" PRIu64 "\n", + pic_header->tile_info.tile_partition.RowCount); + + assert(pic_header->tile_info.tile_partition.ColCount < 64); + for (uint8_t i = 0; i < pic_header->tile_info.tile_partition.ColCount; i++) { + debug_printf("Post encode tile_info.tile_partition.ColWidths[%d]: %" PRIu64 "\n", + i, + pic_header->tile_info.tile_partition.ColWidths[i]); + } + + assert(pic_header->tile_info.tile_partition.RowCount < 64); + for (uint8_t i = 0; i < pic_header->tile_info.tile_partition.RowCount; i++) { + debug_printf("Post encode tile_info.tile_partition.RowHeights[%d]: %" PRIu64 "\n", + i, + pic_header->tile_info.tile_partition.RowHeights[i]); + } + + pic_header->show_existing_frame = 0; + pic_header->frame_to_show_map_idx = 0; + + // pic_header->display_frame_id; // frame_id_numbers_present_flag coded in bitstream by default as 0 + + pic_header->frame_type = associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData.FrameType; + { + UINT EncodeOrderInGop = + (associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData.PictureIndex % + associatedMetadata.m_associatedEncodeConfig.m_encoderGOPConfigDesc.m_AV1SequenceStructure.IntraDistance); + + UINT ShowOrderInGop = + (associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData.OrderHint % + associatedMetadata.m_associatedEncodeConfig.m_encoderGOPConfigDesc.m_AV1SequenceStructure.IntraDistance); + + pic_header->show_frame = (ShowOrderInGop <= EncodeOrderInGop); + } + + pic_header->showable_frame = // this will always be showable for P/B frames, even when using show_frame for B + // frame playback reordering + associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData.FrameType != + D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_KEY_FRAME; + pic_header->error_resilient_mode = + ((associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData.Flags & + D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ENABLE_ERROR_RESILIENT_MODE) != 0); + pic_header->disable_cdf_update = + ((associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData.Flags & + D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_DISABLE_CDF_UPDATE) != 0); + + pic_header->allow_screen_content_tools = + ((associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData.Flags & + D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ENABLE_PALETTE_ENCODING) != 0); + pic_header->force_integer_mv = + ((associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData.Flags & + D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_FORCE_INTEGER_MOTION_VECTORS) != 0); + + // frame_size_override_flag is not coded + default as 1 for SWITCH_FRAME and explicitly coded otherwise + if (pic_header->frame_type == D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_SWITCH_FRAME) + pic_header->frame_size_override_flag = 1; // As per AV1 codec spec + else + pic_header->frame_size_override_flag = 0; // Set as default as 0 when explicitly coded + + pic_header->order_hint = associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData.OrderHint; + + pic_header->refresh_frame_flags = + associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData.RefreshFrameFlags; + + // frame_size (comes from input texture size) + pic_header->FrameWidth = associatedMetadata.m_associatedEncodeConfig.m_currentResolution.Width; + pic_header->UpscaledWidth = associatedMetadata.m_associatedEncodeConfig.m_currentResolution.Width; + pic_header->FrameHeight = associatedMetadata.m_associatedEncodeConfig.m_currentResolution.Height; + + // render_size (comes from AV1 pipe picparams pic) + pic_header->RenderWidth = associatedMetadata.m_associatedEncodeConfig.m_FrameCroppingCodecConfig.right; + pic_header->RenderHeight = associatedMetadata.m_associatedEncodeConfig.m_FrameCroppingCodecConfig.bottom; + + bool use_128x128_superblock = + ((associatedMetadata.m_associatedEncodeConfig.m_encoderCodecSpecificConfigDesc.m_AV1Config.FeatureFlags & + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_128x128_SUPERBLOCK) != 0) ? + 1 : + 0; + unsigned MiCols = 2 * ((pic_header->FrameWidth + 7) >> 3); + unsigned MiRows = 2 * ((pic_header->FrameHeight + 7) >> 3); + pic_header->frame_width_sb = use_128x128_superblock ? ((MiCols + 31) >> 5) : ((MiCols + 15) >> 4); + pic_header->frame_height_sb = use_128x128_superblock ? ((MiRows + 31) >> 5) : ((MiRows + 15) >> 4); + + pic_header->use_superres = ((associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData.Flags & + D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_USE_SUPER_RESOLUTION) != 0); + pic_header->SuperresDenom = + associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData.SuperResDenominator; + + pic_header->allow_intrabc = ((associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData.Flags & + D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ALLOW_INTRA_BLOCK_COPY) != 0); + + for (unsigned i = 0; i < ARRAY_SIZE(associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData + .ReferenceFramesReconPictureDescriptors); + i++) { + pic_header->ref_order_hint[i] = associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData + .ReferenceFramesReconPictureDescriptors[i] + .OrderHint; + } + + for (uint8_t i = 0; i < ARRAY_SIZE(pParsedPostEncodeValues->ReferenceIndices); i++) + pic_header->ref_frame_idx[i] = pParsedPostEncodeValues->ReferenceIndices[i]; + + pic_header->allow_high_precision_mv = + ((associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData.Flags & + D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ALLOW_HIGH_PRECISION_MV) != 0); + pic_header->interpolation_filter = + associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData.InterpolationFilter; + pic_header->is_motion_mode_switchable = + ((associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData.Flags & + D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_MOTION_MODE_SWITCHABLE) != 0); + pic_header->use_ref_frame_mvs = + ((associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData.Flags & + D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_FRAME_REFERENCE_MOTION_VECTORS) != 0); + pic_header->disable_frame_end_update_cdf = + ((associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData.Flags & + D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_DISABLE_FRAME_END_UPDATE_CDF) != 0); + + pic_header->tile_info.tile_mode = associatedMetadata.m_associatedEncodeConfig.m_encoderSliceConfigMode; + pic_header->tile_info.tile_support_caps = + associatedMetadata.m_associatedEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1TileCaps; + + pic_header->tile_info.uniform_tile_spacing_flag = 0; + if ((pic_header->tile_info.tile_mode == D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_UNIFORM_GRID_PARTITION) || + (pic_header->tile_info.tile_mode == D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME)) + pic_header->tile_info.uniform_tile_spacing_flag = 1; + else if (pic_header->tile_info.tile_mode == + D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_CONFIGURABLE_GRID_PARTITION) + pic_header->tile_info.uniform_tile_spacing_flag = 0; + else + assert(false); // Unknown pic_header->tile_info.tile_mode for AV1 + + // lr_params + for (uint32_t i = 0; i < 3; i++) + pic_header->lr_params.lr_type[i] = associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData + .FrameRestorationConfig.FrameRestorationType[i]; + bool lr_enabled = + pic_header->lr_params.lr_type[0] || pic_header->lr_params.lr_type[1] || pic_header->lr_params.lr_type[2]; + if (lr_enabled) { + uint8_t luma_shift_total = log2(d3d12_video_encoder_looprestorationsize_d3d12_to_uint_av1( + associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData + .FrameRestorationConfig.LoopRestorationPixelSize[0])) - + 6; + pic_header->lr_params.lr_unit_shift = (luma_shift_total > 0) ? 1 : 0; + pic_header->lr_params.lr_unit_extra_shift = (luma_shift_total > 1) ? 1 : 0; + assert(associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData.FrameRestorationConfig + .LoopRestorationPixelSize[1] == associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc + .m_AV1PicData.FrameRestorationConfig.LoopRestorationPixelSize[2]); + + if (associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData.FrameRestorationConfig + .LoopRestorationPixelSize[1]) { + pic_header->lr_params.lr_uv_shift = log2(d3d12_video_encoder_looprestorationsize_d3d12_to_uint_av1( + associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc + .m_AV1PicData.FrameRestorationConfig.LoopRestorationPixelSize[1])) + + 6 + luma_shift_total; + } else { + pic_header->lr_params.lr_uv_shift = 0; + } + } + + pic_header->TxMode = associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData.TxMode; + pic_header->skip_mode_present = + ((associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData.Flags & + D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ENABLE_SKIP_MODE) != 0); + pic_header->allow_warped_motion = + ((associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData.Flags & + D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ENABLE_WARPED_MOTION) != 0); + pic_header->reduced_tx_set = + ((associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData.Flags & + D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_REDUCED_TX_SET) != 0); + // pic_header->frame_refs_short_signaling; // coded in bitstream by default as 0 +} + +D3D12_VIDEO_ENCODER_AV1_RESTORATION_TILESIZE +d3d12_video_encoder_looprestorationsize_uint_to_d3d12_av1(uint32_t pixel_size) +{ + switch (pixel_size) { + case 32: + { + return D3D12_VIDEO_ENCODER_AV1_RESTORATION_TILESIZE_32x32; + } break; + case 64: + { + return D3D12_VIDEO_ENCODER_AV1_RESTORATION_TILESIZE_64x64; + } break; + case 128: + { + return D3D12_VIDEO_ENCODER_AV1_RESTORATION_TILESIZE_128x128; + } break; + case 256: + { + return D3D12_VIDEO_ENCODER_AV1_RESTORATION_TILESIZE_256x256; + } break; + default: + { + unreachable("Unsupported D3D12_VIDEO_ENCODER_AV1_PROFILE"); + } break; + } +} + +unsigned +d3d12_video_encoder_looprestorationsize_d3d12_to_uint_av1(D3D12_VIDEO_ENCODER_AV1_RESTORATION_TILESIZE d3d12_type) +{ + switch (d3d12_type) { + case D3D12_VIDEO_ENCODER_AV1_RESTORATION_TILESIZE_32x32: + { + return 32; + } break; + case D3D12_VIDEO_ENCODER_AV1_RESTORATION_TILESIZE_64x64: + { + return 64; + } break; + case D3D12_VIDEO_ENCODER_AV1_RESTORATION_TILESIZE_128x128: + { + return 128; + } break; + case D3D12_VIDEO_ENCODER_AV1_RESTORATION_TILESIZE_256x256: + { + return 256; + } break; + default: + { + unreachable("Unsupported D3D12_VIDEO_ENCODER_AV1_PROFILE"); + } break; + } +} + +/* + * Called at get_feedback time FOR A PREVIOUSLY RECORDED AND EXECUTED FRAME + */ +unsigned +d3d12_video_encoder_build_post_encode_codec_bitstream_av1(struct d3d12_video_encoder *pD3D12Enc, + uint64_t associated_fence_value, + EncodedBitstreamResolvedMetadata &associatedMetadata) +{ + // Parse the AV1 resolved metadata + + ID3D12Resource *pResolvedMetadataBuffer = associatedMetadata.spBuffer.Get(); + uint64_t resourceMetadataSize = associatedMetadata.bufferSize; + + struct d3d12_screen *pD3D12Screen = (struct d3d12_screen *) pD3D12Enc->m_pD3D12Screen; + pipe_resource *pPipeResolvedMetadataBuffer = + d3d12_resource_from_resource(&pD3D12Screen->base, pResolvedMetadataBuffer); + assert(resourceMetadataSize < INT_MAX); + struct pipe_box box = { + 0, // x + 0, // y + 0, // z + static_cast(resourceMetadataSize), // width + 1, // height + 1 // depth + }; + struct pipe_transfer *mapTransferMetadata; + uint8_t *pMetadataBufferSrc = + reinterpret_cast(pD3D12Enc->base.context->buffer_map(pD3D12Enc->base.context, + pPipeResolvedMetadataBuffer, + 0, + PIPE_MAP_READ, + &box, + &mapTransferMetadata)); + + debug_printf("[d3d12_video_enc_av1] Parsing driver resolved encode metadata...\n"); + + D3D12_VIDEO_ENCODER_OUTPUT_METADATA *pParsedMetadata = + reinterpret_cast(pMetadataBufferSrc); + pMetadataBufferSrc += sizeof(D3D12_VIDEO_ENCODER_OUTPUT_METADATA); + + D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA *pFrameSubregionMetadata = + reinterpret_cast(pMetadataBufferSrc); + pMetadataBufferSrc += + (sizeof(D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA) * pParsedMetadata->WrittenSubregionsCount); + + D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_TILES *pParsedTilePartitions = + reinterpret_cast(pMetadataBufferSrc); + pMetadataBufferSrc += sizeof(D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_TILES); + + D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES *pParsedPostEncodeValues = + reinterpret_cast(pMetadataBufferSrc); + pMetadataBufferSrc += sizeof(D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES); + + debug_printf("[d3d12_video_enc_av1] D3D12_VIDEO_ENCODER_OUTPUT_METADATA.EncodeErrorFlags: %" PRIx64 " \n", + pParsedMetadata->EncodeErrorFlags); + debug_printf("[d3d12_video_enc_av1] D3D12_VIDEO_ENCODER_OUTPUT_METADATA.EncodeStats.AverageQP: %" PRIu64 " \n", + pParsedMetadata->EncodeStats.AverageQP); + debug_printf("[d3d12_video_enc_av1] D3D12_VIDEO_ENCODER_OUTPUT_METADATA.EncodeStats.IntraCodingUnitsCount: %" PRIu64 + " \n", + pParsedMetadata->EncodeStats.IntraCodingUnitsCount); + debug_printf("[d3d12_video_enc_av1] D3D12_VIDEO_ENCODER_OUTPUT_METADATA.EncodeStats.InterCodingUnitsCount: %" PRIu64 + " \n", + pParsedMetadata->EncodeStats.InterCodingUnitsCount); + debug_printf("[d3d12_video_enc_av1] D3D12_VIDEO_ENCODER_OUTPUT_METADATA.EncodeStats.SkipCodingUnitsCount: %" PRIu64 + " \n", + pParsedMetadata->EncodeStats.SkipCodingUnitsCount); + debug_printf("[d3d12_video_enc_av1] " + "D3D12_VIDEO_ENCODER_OUTPUT_METADATA.EncodeStats.AverageMotionEstimationXDirection: %" PRIu64 " \n", + pParsedMetadata->EncodeStats.AverageMotionEstimationXDirection); + debug_printf("[d3d12_video_enc_av1] " + "D3D12_VIDEO_ENCODER_OUTPUT_METADATA.EncodeStats.AverageMotionEstimationYDirection: %" PRIu64 " \n", + pParsedMetadata->EncodeStats.AverageMotionEstimationYDirection); + debug_printf("[d3d12_video_enc_av1] D3D12_VIDEO_ENCODER_OUTPUT_METADATA.EncodedBitstreamWrittenBytesCount: %" PRIu64 + " \n", + pParsedMetadata->EncodedBitstreamWrittenBytesCount); + debug_printf("[d3d12_video_enc_av1] D3D12_VIDEO_ENCODER_OUTPUT_METADATA.WrittenSubregionsCount: %" PRIu64 " \n", + pParsedMetadata->WrittenSubregionsCount); + + for (uint8_t i = 0; i < pParsedMetadata->WrittenSubregionsCount; i++) { + debug_printf("[d3d12_video_enc_av1] D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA[%d].bHeaderSize: %" PRIu64 " \n", + i, + pFrameSubregionMetadata[i].bHeaderSize); + debug_printf("[d3d12_video_enc_av1] D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA[%d].bStartOffset: %" PRIu64 + " \n", + i, + pFrameSubregionMetadata[i].bStartOffset); + debug_printf("[d3d12_video_enc_av1] D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA[%d].bSize: %" PRIu64 " \n", + i, + pFrameSubregionMetadata[i].bSize); + } + + if (pParsedMetadata->EncodeErrorFlags != D3D12_VIDEO_ENCODER_ENCODE_ERROR_FLAG_NO_ERROR) { + debug_printf("[d3d12_video_enc_av1] Encode GPU command for fence %" PRIu64 " failed - EncodeErrorFlags: %" PRIu64 + "\n", + associated_fence_value, + pParsedMetadata->EncodeErrorFlags); + assert(false); + return 0; + } + + if (pParsedMetadata->EncodedBitstreamWrittenBytesCount == 0u) { + debug_printf("[d3d12_video_enc_av1] Encode GPU command for fence %" PRIu64 + " failed - EncodedBitstreamWrittenBytesCount: 0\n", + associated_fence_value); + assert(false); + return 0; + } + + // Create headers + + av1_seq_header_t seqHdr = {}; + fill_av1_seq_header(associatedMetadata, &seqHdr); + av1_pic_header_t picHdr = {}; + fill_av1_pic_header(associatedMetadata, &picHdr, &seqHdr, pParsedPostEncodeValues, pParsedTilePartitions); + + bool bNeedSeqUpdate = false; + bool diff_uv_delta_from_pev = (picHdr.quantization_params.VDCDeltaQ != picHdr.quantization_params.UDCDeltaQ) || + (picHdr.quantization_params.VACDeltaQ != picHdr.quantization_params.UACDeltaQ); + debug_printf("[Calculated] Post encode diff_uv_delta_from_pev: %d\n", diff_uv_delta_from_pev); + + // Make sure the Seq header allows diff_uv_delta from pev + if (diff_uv_delta_from_pev && !seqHdr.color_config.separate_uv_delta_q) { + seqHdr.color_config.separate_uv_delta_q = 1; + bNeedSeqUpdate = true; + } + + d3d12_video_bitstream_builder_av1 *pAV1BitstreamBuilder = + static_cast(pD3D12Enc->m_upBitstreamBuilder.get()); + assert(pAV1BitstreamBuilder); + + size_t writtenTemporalDelimBytes = 0; + if (picHdr.show_frame && associatedMetadata.m_CodecSpecificData.AV1HeadersInfo.temporal_delim_rendered) { + pAV1BitstreamBuilder->write_temporal_delimiter_obu( + pD3D12Enc->m_BitstreamHeadersBuffer, + pD3D12Enc->m_BitstreamHeadersBuffer.begin(), // placingPositionStart + writtenTemporalDelimBytes // Bytes Written AFTER placingPositionStart arg above + ); + assert(pD3D12Enc->m_BitstreamHeadersBuffer.size() == writtenTemporalDelimBytes); + debug_printf("Written OBU_TEMPORAL_DELIMITER bytes: %" PRIu64 "\n", writtenTemporalDelimBytes); + } + + size_t writtenSequenceBytes = 0; + bool isFirstFrame = (associated_fence_value == 1); + + // on first frame or on resolution change or if we changed seq hdr values with post encode values + bool writeNewSeqHeader = isFirstFrame || bNeedSeqUpdate || + ((associatedMetadata.m_associatedEncodeConfig.m_seqFlags & + D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAG_RESOLUTION_CHANGE) != 0); + + if (writeNewSeqHeader) { + pAV1BitstreamBuilder->write_sequence_header( + &seqHdr, + pD3D12Enc->m_BitstreamHeadersBuffer, + pD3D12Enc->m_BitstreamHeadersBuffer.begin() + writtenTemporalDelimBytes, // placingPositionStart + writtenSequenceBytes // Bytes Written AFTER placingPositionStart arg above + ); + assert(pD3D12Enc->m_BitstreamHeadersBuffer.size() == (writtenSequenceBytes + writtenTemporalDelimBytes)); + debug_printf("Written OBU_SEQUENCE_HEADER bytes: %" PRIu64 "\n", writtenSequenceBytes); + } + + // Only supported bitstream format is with obu_size for now. + assert(associatedMetadata.m_CodecSpecificData.AV1HeadersInfo.obu_has_size_field); + + size_t writtenFrameBytes = 0; + size_t writtenTileBytes = 0; + + pipe_resource *src_driver_bitstream = + d3d12_resource_from_resource(&pD3D12Enc->m_pD3D12Screen->base, associatedMetadata.spStagingBitstream.Get()); + assert(src_driver_bitstream); + + size_t comp_bitstream_offset = 0; + if (associatedMetadata.m_CodecSpecificData.AV1HeadersInfo.enable_frame_obu) { + // FRAME_OBU combined frame and tile data case + + assert(associatedMetadata.m_associatedEncodeConfig.m_encoderSliceConfigDesc.m_TilesConfig_AV1.TilesGroupsCount == + 1); + + // write_frame_header writes OBU_FRAME except tile_obu_group, but included obu_size for tile_group_obu as + // calculated below + size_t tile_group_obu_size = 0; + size_t decode_tile_elements_size = 0; + pAV1BitstreamBuilder->calculate_tile_group_obu_size( + pParsedMetadata, + pFrameSubregionMetadata, + associatedMetadata.m_associatedEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1TileCaps + .TileSizeBytesMinus1 + + 1, + associatedMetadata.m_associatedEncodeConfig.m_encoderSliceConfigDesc.m_TilesConfig_AV1.TilesPartition, + associatedMetadata.m_associatedEncodeConfig.m_encoderSliceConfigDesc.m_TilesConfig_AV1.TilesGroups[0], + tile_group_obu_size, + decode_tile_elements_size); + + pAV1BitstreamBuilder->write_frame_header( + &seqHdr, + &picHdr, + OBU_FRAME, + tile_group_obu_size, // We need it to code obu_size of OBU_FRAME open_bitstream_unit() + pD3D12Enc->m_BitstreamHeadersBuffer, + pD3D12Enc->m_BitstreamHeadersBuffer.begin() + writtenSequenceBytes + + writtenTemporalDelimBytes, // placingPositionStart + writtenFrameBytes // Bytes Written AFTER placingPositionStart arg above + ); + + debug_printf("Written OBU_FRAME bytes: %" PRIu64 "\n", writtenFrameBytes); + + assert(pD3D12Enc->m_BitstreamHeadersBuffer.size() == + (writtenSequenceBytes + writtenTemporalDelimBytes + writtenFrameBytes)); + + debug_printf("Uploading %" PRIu64 + " bytes from OBU sequence and/or picture headers to comp_bit_destination %p at offset 0\n", + pD3D12Enc->m_BitstreamHeadersBuffer.size(), + associatedMetadata.comp_bit_destination); + + // Upload headers to the finalized compressed bitstream buffer + // Note: The buffer_subdata is queued in pD3D12Enc->base.context but doesn't execute immediately + pD3D12Enc->base.context->buffer_subdata(pD3D12Enc->base.context, // context + associatedMetadata.comp_bit_destination, // comp. bitstream + PIPE_MAP_WRITE, // usage PIPE_MAP_x + 0, // offset + pD3D12Enc->m_BitstreamHeadersBuffer.size(), + pD3D12Enc->m_BitstreamHeadersBuffer.data()); + + comp_bitstream_offset = pD3D12Enc->m_BitstreamHeadersBuffer.size(); + size_t written_bytes_to_staging_bitstream_buffer = 0; + + upload_tile_group_obu( + pD3D12Enc, + tile_group_obu_size, + decode_tile_elements_size, + associatedMetadata.m_StagingBitstreamConstruction, + 0, // staging_bitstream_buffer_offset, + src_driver_bitstream, + associatedMetadata.comp_bit_destination, + comp_bitstream_offset, + pFrameSubregionMetadata, + associatedMetadata.m_associatedEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1TileCaps + .TileSizeBytesMinus1 + + 1, + associatedMetadata.m_associatedEncodeConfig.m_encoderSliceConfigDesc.m_TilesConfig_AV1.TilesPartition, + associatedMetadata.m_associatedEncodeConfig.m_encoderSliceConfigDesc.m_TilesConfig_AV1.TilesGroups[0], + written_bytes_to_staging_bitstream_buffer); + + writtenTileBytes += tile_group_obu_size; + comp_bitstream_offset += writtenTileBytes; + + // Flush batch with upload work and wait on this CPU thread for GPU work completion + { + struct pipe_fence_handle *pUploadGPUCompletionFence = NULL; + pD3D12Enc->base.context->flush(pD3D12Enc->base.context, + &pUploadGPUCompletionFence, + PIPE_FLUSH_ASYNC | PIPE_FLUSH_HINT_FINISH); + assert(pUploadGPUCompletionFence); + debug_printf( + "[d3d12_video_encoder] d3d12_video_encoder_build_post_encode_codec_bitstream_av1 - Waiting on GPU " + "completion fence for " + " uploading finalized codec headers to compressed bitstream.\n"); + + pD3D12Enc->m_pD3D12Screen->base.fence_finish(&pD3D12Enc->m_pD3D12Screen->base, + NULL, + pUploadGPUCompletionFence, + OS_TIMEOUT_INFINITE); + pD3D12Enc->m_pD3D12Screen->base.fence_reference(&pD3D12Enc->m_pD3D12Screen->base, + &pUploadGPUCompletionFence, + NULL); + } + } else { + // FRAME_HEADER_OBU + OBU_TILE_GROUP concatenated case + + // Build open_bitstream_unit for OBU_FRAME_HEADER + pAV1BitstreamBuilder->write_frame_header(&seqHdr, + &picHdr, + OBU_FRAME_HEADER, + 0, // no tile_obu_group in OBU_FRAME_HEADER + pD3D12Enc->m_BitstreamHeadersBuffer, + pD3D12Enc->m_BitstreamHeadersBuffer.begin() + writtenSequenceBytes + + writtenTemporalDelimBytes, // placingPositionStart + writtenFrameBytes // Bytes Written AFTER placingPositionStart arg above + ); + + debug_printf("Written OBU_FRAME_HEADER bytes: %" PRIu64 "\n", writtenFrameBytes); + + assert(pD3D12Enc->m_BitstreamHeadersBuffer.size() == + (writtenSequenceBytes + writtenTemporalDelimBytes + writtenFrameBytes)); + + debug_printf("Uploading %" PRIu64 " bytes from OBU headers to comp_bit_destination %p at offset 0\n", + pD3D12Enc->m_BitstreamHeadersBuffer.size(), + associatedMetadata.comp_bit_destination); + + // Upload headers to the finalized compressed bitstream buffer + // Note: The buffer_subdata is queued in pD3D12Enc->base.context but doesn't execute immediately + pD3D12Enc->base.context->buffer_subdata(pD3D12Enc->base.context, // context + associatedMetadata.comp_bit_destination, // comp. bitstream + PIPE_MAP_WRITE, // usage PIPE_MAP_x + 0, // offset + pD3D12Enc->m_BitstreamHeadersBuffer.size(), + pD3D12Enc->m_BitstreamHeadersBuffer.data()); + + comp_bitstream_offset = pD3D12Enc->m_BitstreamHeadersBuffer.size(); + size_t staging_bitstream_buffer_offset = 0; + + for (int tg_idx = 0; + tg_idx < + associatedMetadata.m_associatedEncodeConfig.m_encoderSliceConfigDesc.m_TilesConfig_AV1.TilesGroupsCount; + tg_idx++) { + auto ¤tTg = + associatedMetadata.m_associatedEncodeConfig.m_encoderSliceConfigDesc.m_TilesConfig_AV1.TilesGroups[tg_idx]; + + debug_printf("Uploading tile group %d to comp_bit_destination %p at offset %" PRIu64 "\n", + tg_idx, + associatedMetadata.comp_bit_destination, + comp_bitstream_offset); + + size_t tile_group_obu_size = 0; + size_t decode_tile_elements_size = 0; + pAV1BitstreamBuilder->calculate_tile_group_obu_size( + pParsedMetadata, + pFrameSubregionMetadata, + associatedMetadata.m_associatedEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1TileCaps + .TileSizeBytesMinus1 + + 1, + associatedMetadata.m_associatedEncodeConfig.m_encoderSliceConfigDesc.m_TilesConfig_AV1.TilesPartition, + currentTg, + tile_group_obu_size, + decode_tile_elements_size); + + size_t writtenTileObuPrefixBytes = 0; + pAV1BitstreamBuilder->write_obu_tile_group_header( + tile_group_obu_size, // tile_group_obu() size to pack OBU_TILE_GROUP obu_size element + associatedMetadata.m_StagingBitstreamConstruction, // Source CPU buffer cannot be overwritten until + // GPU upload flush finishes. + associatedMetadata.m_StagingBitstreamConstruction.begin() + + staging_bitstream_buffer_offset, // placingPositionStart + writtenTileObuPrefixBytes); // Bytes Written AFTER placingPositionStart arg above + + debug_printf("Written %" PRIu64 " bytes for OBU_TILE_GROUP open_bitstream_unit() prefix with obu_header() and " + "obu_size to staging_bitstream_buffer %p at offset %" PRIu64 "\n", + writtenTileObuPrefixBytes, + associatedMetadata.m_StagingBitstreamConstruction.data(), + staging_bitstream_buffer_offset); + + writtenTileBytes += writtenTileObuPrefixBytes; + + // Note: The buffer_subdata is queued in pD3D12Enc->base.context but doesn't execute immediately + pD3D12Enc->base.context->buffer_subdata( + pD3D12Enc->base.context, // context + associatedMetadata.comp_bit_destination, // comp. bitstream + PIPE_MAP_WRITE, // usage PIPE_MAP_x + comp_bitstream_offset, // offset + writtenTileObuPrefixBytes, + associatedMetadata.m_StagingBitstreamConstruction.data() + staging_bitstream_buffer_offset); + + debug_printf("Uploading %" PRIu64 " bytes for OBU_TILE_GROUP open_bitstream_unit() prefix with obu_header() " + "and obu_size: %" PRIu64 " to comp_bit_destination %p at offset %" PRIu64 "\n", + writtenTileObuPrefixBytes, + tile_group_obu_size, + associatedMetadata.comp_bit_destination, + comp_bitstream_offset); + + staging_bitstream_buffer_offset += writtenTileObuPrefixBytes; + + comp_bitstream_offset += writtenTileObuPrefixBytes; + + size_t written_bytes_to_staging_bitstream_buffer = 0; + upload_tile_group_obu( + pD3D12Enc, + tile_group_obu_size, + decode_tile_elements_size, + associatedMetadata.m_StagingBitstreamConstruction, + staging_bitstream_buffer_offset, + src_driver_bitstream, + associatedMetadata.comp_bit_destination, + comp_bitstream_offset, + pFrameSubregionMetadata, + associatedMetadata.m_associatedEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_AV1TileCaps + .TileSizeBytesMinus1 + + 1, + associatedMetadata.m_associatedEncodeConfig.m_encoderSliceConfigDesc.m_TilesConfig_AV1.TilesPartition, + currentTg, + written_bytes_to_staging_bitstream_buffer); + + staging_bitstream_buffer_offset += written_bytes_to_staging_bitstream_buffer; + comp_bitstream_offset += tile_group_obu_size; + writtenTileBytes += tile_group_obu_size; + + // Flush batch with upload work and wait on this CPU thread for GPU work completion + // for this case we have to do it within the loop, so the CUP arrays that are reused + // on each loop are completely done being read by the GPU before we modify them. + { + struct pipe_fence_handle *pUploadGPUCompletionFence = NULL; + pD3D12Enc->base.context->flush(pD3D12Enc->base.context, + &pUploadGPUCompletionFence, + PIPE_FLUSH_ASYNC | PIPE_FLUSH_HINT_FINISH); + assert(pUploadGPUCompletionFence); + debug_printf( + "[d3d12_video_encoder] d3d12_video_encoder_build_post_encode_codec_bitstream_av1 - Waiting on GPU " + "completion fence for " + " uploading finalized codec headers to compressed bitstream.\n"); + + pD3D12Enc->m_pD3D12Screen->base.fence_finish(&pD3D12Enc->m_pD3D12Screen->base, + NULL, + pUploadGPUCompletionFence, + OS_TIMEOUT_INFINITE); + pD3D12Enc->m_pD3D12Screen->base.fence_reference(&pD3D12Enc->m_pD3D12Screen->base, + &pUploadGPUCompletionFence, + NULL); + } + } + } + + size_t extra_show_existing_frame_payload_bytes = 0; + if (!picHdr.show_frame) { + + // When writing in the bitstream a frame that will be accessed as reference by a frame we did not + // write to the bitstream yet, let's store them for future show_existing_frame mechanism + pD3D12Enc->m_currentEncodeConfig.m_encoderCodecSpecificStateDescAV1.pendingShowableFrames.push_back( + associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData.PictureIndex); + + } else { // picHdr.show_frame true + + // Check if any of the pending "to be showed later" frames is present in the current frame references + // and if so issue the show_existing_frame header and remove it from the pending list + + for (auto pendingFrameIt = + pD3D12Enc->m_currentEncodeConfig.m_encoderCodecSpecificStateDescAV1.pendingShowableFrames.begin(); + pendingFrameIt != + pD3D12Enc->m_currentEncodeConfig.m_encoderCodecSpecificStateDescAV1.pendingShowableFrames.end();) { + + // Check if current frame references uses this pendingFrameIt + int cur_ref_idx_matching_pending = -1; + for (unsigned i = 0; i < ARRAY_SIZE(picHdr.ref_frame_idx) /*NUM_REF_FRAMES*/; i++) { + if ( + // Is a valid reference + (associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData + .ReferenceFramesReconPictureDescriptors[picHdr.ref_frame_idx[i]] + .ReconstructedPictureResourceIndex != UNUSED_VIRTUAL_DPB_SLOT_PHYSICAL_INDEX) && + // And matches the pending frame PictureIndex + (associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_AV1PicData + .ReferenceFramesReconPictureDescriptors[picHdr.ref_frame_idx[i]] + .PictureIndex == *pendingFrameIt /*PictureIndex*/)) { + + // Store the reference index + cur_ref_idx_matching_pending = picHdr.ref_frame_idx[i]; + break; + } + } + + // If we found a reference of the current frame using pendingFrameIt, + // - Issue the show_existing_frame header + // - Remove it from the pending list + if (cur_ref_idx_matching_pending >= 0) { + size_t staging_buf_offset = pD3D12Enc->m_BitstreamHeadersBuffer.size(); + + + size_t writtenTemporalDelimBytes = 0; + if (D3D12_VIDEO_AV1_INSERT_SHOW_EXISTING_FRAME_HEADER) { + pAV1BitstreamBuilder->write_temporal_delimiter_obu( + pD3D12Enc->m_BitstreamHeadersBuffer, + pD3D12Enc->m_BitstreamHeadersBuffer.begin() + staging_buf_offset, // placingPositionStart + writtenTemporalDelimBytes // Bytes Written AFTER placingPositionStart arg above + ); + } + assert(writtenTemporalDelimBytes == (pD3D12Enc->m_BitstreamHeadersBuffer.size() - staging_buf_offset)); + + // Add current pending frame being processed in the loop + extra_show_existing_frame_payload_bytes += writtenTemporalDelimBytes; + + debug_printf("Written OBU_TEMPORAL_DELIMITER bytes: %" PRIu64 "\n", writtenTemporalDelimBytes); + + size_t writtenShowExistingFrameBytes = 0; + av1_pic_header_t showExistingPicHdr = {}; + showExistingPicHdr.show_existing_frame = 1; + showExistingPicHdr.frame_to_show_map_idx = cur_ref_idx_matching_pending; + + if (D3D12_VIDEO_AV1_INSERT_SHOW_EXISTING_FRAME_HEADER) { + pAV1BitstreamBuilder->write_frame_header( + NULL, // No seq header necessary for show_existing_frame + &showExistingPicHdr, + OBU_FRAME_HEADER, + 0, // No tile info for OBU_FRAME_HEADER + pD3D12Enc->m_BitstreamHeadersBuffer, + pD3D12Enc->m_BitstreamHeadersBuffer.begin() + staging_buf_offset + + writtenTemporalDelimBytes, // placingPositionStart + writtenShowExistingFrameBytes // Bytes Written AFTER placingPositionStart arg above + ); + } + assert(writtenShowExistingFrameBytes == + (pD3D12Enc->m_BitstreamHeadersBuffer.size() - staging_buf_offset - writtenTemporalDelimBytes)); + + // Add current pending frame being processed in the loop + extra_show_existing_frame_payload_bytes += writtenShowExistingFrameBytes; + + assert(pD3D12Enc->m_BitstreamHeadersBuffer.size() == + (staging_buf_offset + writtenShowExistingFrameBytes + writtenTemporalDelimBytes)); + + // Upload headers to the finalized compressed bitstream buffer + // Note: The buffer_subdata is queued in pD3D12Enc->base.context but doesn't execute immediately + pD3D12Enc->base.context->buffer_subdata(pD3D12Enc->base.context, // context + associatedMetadata.comp_bit_destination, // comp. bitstream + PIPE_MAP_WRITE, // usage PIPE_MAP_x + comp_bitstream_offset, // offset + writtenShowExistingFrameBytes + writtenTemporalDelimBytes, + pD3D12Enc->m_BitstreamHeadersBuffer.data() + staging_buf_offset); + + comp_bitstream_offset += writtenShowExistingFrameBytes; + comp_bitstream_offset += writtenTemporalDelimBytes; + + debug_printf("Written show_existing_frame OBU_FRAME header for previous frame with PictureIndex %d (Used " + "in current frame ref_frame_idx[%" PRIu32 "]) bytes: %" PRIu64 "\n", + *pendingFrameIt /*PictureIndex*/, + showExistingPicHdr.frame_to_show_map_idx, + writtenShowExistingFrameBytes); + + // Remove it from the list of pending frames + pendingFrameIt = + pD3D12Enc->m_currentEncodeConfig.m_encoderCodecSpecificStateDescAV1.pendingShowableFrames.erase( + pendingFrameIt); + } else { + pendingFrameIt++; + } + } + + // Flush again if uploaded show_existing_frame header(s) to bitstream. + if (extra_show_existing_frame_payload_bytes) { + struct pipe_fence_handle *pUploadGPUCompletionFence = NULL; + pD3D12Enc->base.context->flush(pD3D12Enc->base.context, + &pUploadGPUCompletionFence, + PIPE_FLUSH_ASYNC | PIPE_FLUSH_HINT_FINISH); + assert(pUploadGPUCompletionFence); + debug_printf("[d3d12_video_encoder] [show_existing_frame extra headers upload] " + "d3d12_video_encoder_build_post_encode_codec_bitstream_av1 - Waiting on GPU " + "completion fence for " + " uploading finalized codec headers to compressed bitstream.\n"); + + pD3D12Enc->m_pD3D12Screen->base.fence_finish(&pD3D12Enc->m_pD3D12Screen->base, + NULL, + pUploadGPUCompletionFence, + OS_TIMEOUT_INFINITE); + pD3D12Enc->m_pD3D12Screen->base.fence_reference(&pD3D12Enc->m_pD3D12Screen->base, + &pUploadGPUCompletionFence, + NULL); + } + } + + // d3d12_resource_from_resource calls AddRef to it so this should only be deleting + // the pipe_resource wrapping object and not the underlying spStagingBitstream + pipe_resource_reference(&src_driver_bitstream, NULL); + assert(associatedMetadata.spStagingBitstream.Get()); + + // Unmap the metadata buffer tmp storage + pipe_buffer_unmap(pD3D12Enc->base.context, mapTransferMetadata); + pipe_resource_reference(&pPipeResolvedMetadataBuffer, NULL); + + assert((writtenSequenceBytes + writtenTemporalDelimBytes + writtenFrameBytes + + extra_show_existing_frame_payload_bytes) == pD3D12Enc->m_BitstreamHeadersBuffer.size()); + return static_cast(writtenSequenceBytes + writtenTemporalDelimBytes + writtenFrameBytes + + writtenTileBytes + extra_show_existing_frame_payload_bytes); +} + +void +upload_tile_group_obu(struct d3d12_video_encoder *pD3D12Enc, + size_t tile_group_obu_size, + size_t decode_tile_elements_size, + std::vector &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) +{ + debug_printf("[Tile group start %d to end %d] Writing to comp_bit_destination %p starts at offset %" PRIu64 "\n", + tileGroup.tg_start, + tileGroup.tg_end, + comp_bit_destination, + comp_bit_destination_offset); + + debug_printf("[Tile group start %d to end %d] Using staging_bitstream_buffer %p at offset %" PRIu64 + " to write the tile_obu_group() prefix syntax: tile_start_and_end_present_flag, tg_start, tg_end and " + "the tile_size_minus1\n", + tileGroup.tg_start, + tileGroup.tg_end, + staging_bitstream_buffer.data(), + staging_bitstream_buffer_offset); + + // Reserve space upfront in the scratch storage + // Do not modify anything before staging_bitstream_buffer_offset + + size_t tile_obu_prefix_size = tile_group_obu_size - decode_tile_elements_size; + if (staging_bitstream_buffer.size() < (staging_bitstream_buffer_offset + tile_obu_prefix_size)) + staging_bitstream_buffer.resize(staging_bitstream_buffer_offset + tile_obu_prefix_size); + + d3d12_video_encoder_bitstream bitstream_tile_group_obu; + bitstream_tile_group_obu.setup_bitstream(staging_bitstream_buffer.size(), + staging_bitstream_buffer.data(), + staging_bitstream_buffer_offset); + + uint8_t NumTiles = TilesPartition.ColCount * TilesPartition.RowCount; + bool tile_start_and_end_present_flag = !(tileGroup.tg_start == 0 && (tileGroup.tg_end == (NumTiles - 1))); + if (NumTiles > 1) + bitstream_tile_group_obu.put_bits(1, + tile_start_and_end_present_flag); // tile_start_and_end_present_flag f(1) + + if (!(NumTiles == 1 || !tile_start_and_end_present_flag)) { + uint8_t tileBits = log2(TilesPartition.ColCount) + log2(TilesPartition.RowCount); + bitstream_tile_group_obu.put_bits(tileBits, tileGroup.tg_start); // tg_start f(tileBits) + bitstream_tile_group_obu.put_bits(tileBits, tileGroup.tg_end); // tg_end f(tileBits) + } + + bitstream_tile_group_obu.put_aligning_bits(); // byte_alignment() + + bitstream_tile_group_obu.flush(); + + size_t bitstream_tile_group_obu_bytes = bitstream_tile_group_obu.get_byte_count() - staging_bitstream_buffer_offset; + + debug_printf("[Tile group start %d to end %d] Written %" PRIu64 + " bitstream_tile_group_obu_bytes at staging_bitstream_buffer %p at offset %" PRIu64 + " for tile_obu_group() prefix syntax: tile_start_and_end_present_flag, tg_start, tg_end\n", + tileGroup.tg_start, + tileGroup.tg_end, + bitstream_tile_group_obu_bytes, + staging_bitstream_buffer.data(), + staging_bitstream_buffer_offset); + + + // Save this to compare the final written destination byte size against the expected tile_group_obu_size + // at the end of the function + ASSERTED size_t comp_bit_destination_offset_before_upload = comp_bit_destination_offset; + + // Upload first part of the header to compressed bitstream destination + // Note: The buffer_subdata is queued in pD3D12Enc->base.context but doesn't execute immediately + if (bitstream_tile_group_obu_bytes > 0) { + pD3D12Enc->base.context->buffer_subdata( + pD3D12Enc->base.context, // context + comp_bit_destination, // comp. bitstream + PIPE_MAP_WRITE, // usage PIPE_MAP_x + comp_bit_destination_offset, // offset + bitstream_tile_group_obu_bytes, // size + staging_bitstream_buffer.data() + staging_bitstream_buffer_offset); // data + + debug_printf("[Tile group start %d to end %d] Uploading %" PRIu64 " bytes" + " for tile_obu_group() prefix syntax: tile_start_and_end_present_flag, tg_start, tg_end" + " from staging_bitstream_buffer %p at offset %" PRIu64 + " to comp_bit_destination %p at offset %" PRIu64 "\n", + tileGroup.tg_start, + tileGroup.tg_end, + bitstream_tile_group_obu_bytes, + staging_bitstream_buffer.data(), + staging_bitstream_buffer_offset, + comp_bit_destination, + comp_bit_destination_offset); + + comp_bit_destination_offset += bitstream_tile_group_obu_bytes; + written_bytes_to_staging_bitstream_buffer += bitstream_tile_group_obu_bytes; + } + + size_t src_offset = 0; + for (UINT64 TileIdx = tileGroup.tg_start; TileIdx <= tileGroup.tg_end; TileIdx++) { + size_t tile_size = pFrameSubregionMetadata[TileIdx].bSize - pFrameSubregionMetadata[TileIdx].bStartOffset; + // The i-th tile is read from the src_buffer[offset] with offset = [sum j = (0, (i-1)){ tile[j].bSize }] + + // tile[i].bStartOffset + size_t src_buf_tile_position = src_offset + pFrameSubregionMetadata[TileIdx].bStartOffset; + src_offset += pFrameSubregionMetadata[TileIdx].bSize; + + // tile_size_minus_1 not coded for last tile + if ((TileIdx != tileGroup.tg_end)) { + bitstream_tile_group_obu.put_le_bytes(TileSizeBytes, // tile_size_minus_1 le(TileSizeBytes) + tile_size - 1 /* convert to ..._minus_1 */); + bitstream_tile_group_obu.flush(); + + debug_printf("[Tile group start %d to end %d] [TileIdx %" PRIu64 "] Written %" PRIu64 + " written_bytes_to_staging_bitstream_buffer at staging_bitstream_buffer %p at offset %" PRIu64 + " for the tile_size_minus1 syntax\n", + tileGroup.tg_start, + tileGroup.tg_end, + TileIdx, + TileSizeBytes, + staging_bitstream_buffer.data(), + (written_bytes_to_staging_bitstream_buffer + staging_bitstream_buffer_offset)); + + // Upload current tile_size_minus_1 + // Note: The buffer_subdata is queued in pD3D12Enc->base.context but doesn't execute immediately + pD3D12Enc->base.context->buffer_subdata(pD3D12Enc->base.context, // context + comp_bit_destination, // comp. bitstream + PIPE_MAP_WRITE, // usage PIPE_MAP_x + comp_bit_destination_offset, // offset + TileSizeBytes, // size + staging_bitstream_buffer.data() + + written_bytes_to_staging_bitstream_buffer + + staging_bitstream_buffer_offset); // data + + debug_printf("[Tile group start %d to end %d] [TileIdx %" PRIu64 "] Uploading %" PRIu64 " bytes" + " for tile_obu_group() prefix syntax: tile_size_minus_1" + " from staging_bitstream_buffer %p at offset %" PRIu64 + " to comp_bit_destination %p at offset %" PRIu64 "\n", + tileGroup.tg_start, + tileGroup.tg_end, + TileIdx, + TileSizeBytes, + staging_bitstream_buffer.data(), + (written_bytes_to_staging_bitstream_buffer + staging_bitstream_buffer_offset), + comp_bit_destination, + comp_bit_destination_offset); + + comp_bit_destination_offset += TileSizeBytes; + written_bytes_to_staging_bitstream_buffer += TileSizeBytes; + } + + // Now copy the decode_tile() element from the driver staging GPU buffer onto the finalized GPU buffer + + struct pipe_box src_box = { + static_cast(src_buf_tile_position), // x + 0, // y + 0, // z + static_cast(tile_size), // width + 1, // height + 1 // depth + }; + + pD3D12Enc->base.context->resource_copy_region(pD3D12Enc->base.context, // ctx + comp_bit_destination, // dst + 0, // dst_level + comp_bit_destination_offset, // dstX + 0, // dstY + 0, // dstZ + src_driver_bitstream, // src + 0, // src level + &src_box); + + debug_printf("[Tile group start %d to end %d] [TileIdx %" PRIu64 "] Written %" PRIu64 + " tile_size bytes data from src_driver_bitstream %p at offset %" PRIu64 + " into comp_bit_destination %p at offset %" PRIu64 "\n", + tileGroup.tg_start, + tileGroup.tg_end, + TileIdx, + tile_size, + src_driver_bitstream, + src_buf_tile_position, + comp_bit_destination, + comp_bit_destination_offset); + + comp_bit_destination_offset += tile_size; + } + + // Make sure we wrote the expected bytes that match the obu_size elements + // in the OBUs wrapping this uploaded tile_group_obu + assert((comp_bit_destination_offset - comp_bit_destination_offset_before_upload) == tile_group_obu_size); +} + +void +d3d12_video_encoder_store_current_picture_references_av1(d3d12_video_encoder *pD3D12Enc, uint64_t current_metadata_slot) +{ + // Update DX12 picparams for post execution (get_feedback) after d3d12_video_encoder_references_manager_av1 + // changes + pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_associatedEncodeConfig.m_encoderPicParamsDesc = + pD3D12Enc->m_currentEncodeConfig.m_encoderPicParamsDesc; +} diff --git a/src/gallium/drivers/d3d12/d3d12_video_enc_av1.h b/src/gallium/drivers/d3d12/d3d12_video_enc_av1.h new file mode 100644 index 00000000000..0aa002356f2 --- /dev/null +++ b/src/gallium/drivers/d3d12/d3d12_video_enc_av1.h @@ -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 &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 diff --git a/src/gallium/drivers/d3d12/d3d12_video_enc_h264.cpp b/src/gallium/drivers/d3d12/d3d12_video_enc_h264.cpp index 06745fc3f82..75b6e1909d3 100644 --- a/src/gallium/drivers/d3d12/d3d12_video_enc_h264.cpp +++ b/src/gallium/drivers/d3d12/d3d12_video_enc_h264.cpp @@ -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: diff --git a/src/gallium/drivers/d3d12/d3d12_video_enc_hevc.cpp b/src/gallium/drivers/d3d12/d3d12_video_enc_hevc.cpp index 6680465e4ce..09a2f348199 100644 --- a/src/gallium/drivers/d3d12/d3d12_video_enc_hevc.cpp +++ b/src/gallium/drivers/d3d12/d3d12_video_enc_hevc.cpp @@ -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; } diff --git a/src/gallium/drivers/d3d12/d3d12_video_encoder_bitstream.cpp b/src/gallium/drivers/d3d12/d3d12_video_encoder_bitstream.cpp index b5a255a3325..7f98270b988 100644 --- a/src/gallium/drivers/d3d12/d3d12_video_encoder_bitstream.cpp +++ b/src/gallium/drivers/d3d12/d3d12_video_encoder_bitstream.cpp @@ -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(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); +} diff --git a/src/gallium/drivers/d3d12/d3d12_video_encoder_bitstream.h b/src/gallium/drivers/d3d12/d3d12_video_encoder_bitstream.h index 73fdc632ab6..60e48a898b1 100644 --- a/src/gallium/drivers/d3d12/d3d12_video_encoder_bitstream.h +++ b/src/gallium/drivers/d3d12/d3d12_video_encoder_bitstream.h @@ -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; }; diff --git a/src/gallium/drivers/d3d12/d3d12_video_encoder_bitstream_builder_av1.cpp b/src/gallium/drivers/d3d12/d3d12_video_encoder_bitstream_builder_av1.cpp new file mode 100644 index 00000000000..25550a2b4fb --- /dev/null +++ b/src/gallium/drivers/d3d12/d3d12_video_encoder_bitstream_builder_av1.cpp @@ -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 &headerBitstream, + std::vector::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 &headerBitstream, + std::vector::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(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(1 << j) & static_cast(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 &headerBitstream, + std::vector::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(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 &headerBitstream, + std::vector::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); +} diff --git a/src/gallium/drivers/d3d12/d3d12_video_encoder_bitstream_builder_av1.h b/src/gallium/drivers/d3d12/d3d12_video_encoder_bitstream_builder_av1.h new file mode 100644 index 00000000000..b0d56a4081e --- /dev/null +++ b/src/gallium/drivers/d3d12/d3d12_video_encoder_bitstream_builder_av1.h @@ -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 &headerBitstream, + std::vector::iterator placingPositionStart, + size_t &writtenBytes); + + void write_sequence_header(const av1_seq_header_t *pSeqHdr, + std::vector &headerBitstream, + std::vector::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 &headerBitstream, + std::vector::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 &headerBitstream, + std::vector::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 diff --git a/src/gallium/drivers/d3d12/d3d12_video_encoder_references_manager_av1.cpp b/src/gallium/drivers/d3d12/d3d12_video_encoder_references_manager_av1.cpp new file mode 100644 index 00000000000..49892338984 --- /dev/null +++ b/src/gallium/drivers/d3d12/d3d12_video_encoder_references_manager_av1.cpp @@ -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 +#include +#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 (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(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)); +} diff --git a/src/gallium/drivers/d3d12/d3d12_video_encoder_references_manager_av1.h b/src/gallium/drivers/d3d12/d3d12_video_encoder_references_manager_av1.h new file mode 100644 index 00000000000..f00d5f80595 --- /dev/null +++ b/src/gallium/drivers/d3d12/d3d12_video_encoder_references_manager_av1.h @@ -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 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 diff --git a/src/gallium/drivers/d3d12/d3d12_video_screen.cpp b/src/gallium/drivers/d3d12/d3d12_video_screen.cpp index 547ae58e7b7..2676fb98d92 100644 --- a/src/gallium/drivers/d3d12/d3d12_video_screen.cpp +++ b/src/gallium/drivers/d3d12/d3d12_video_screen.cpp @@ -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(&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 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(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(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(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"); diff --git a/src/gallium/drivers/d3d12/d3d12_video_types.h b/src/gallium/drivers/d3d12/d3d12_video_types.h index 348c3a9be7f..faafa6d2796 100644 --- a/src/gallium/drivers/d3d12/d3d12_video_types.h +++ b/src/gallium/drivers/d3d12/d3d12_video_types.h @@ -37,6 +37,12 @@ #include #include +#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 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 diff --git a/src/gallium/drivers/d3d12/meson.build b/src/gallium/drivers/d3d12/meson.build index b6c14d16a83..496479d1a0e 100644 --- a/src/gallium/drivers/d3d12/meson.build +++ b/src/gallium/drivers/d3d12/meson.build @@ -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')