From 228a0a46a53e0b6a020e4c8c0452afa3dccac051 Mon Sep 17 00:00:00 2001 From: Sil Vilerino Date: Thu, 22 May 2025 13:33:55 -0400 Subject: [PATCH] d3d12: Implement full/lower two pass video encode Reviewed-by: Pohsiang (John) Hsu Reviewed-by: Jesse Natalie Part-of: --- src/gallium/drivers/d3d12/d3d12_video_enc.cpp | 293 +++++++++++++++- src/gallium/drivers/d3d12/d3d12_video_enc.h | 51 +++ .../drivers/d3d12/d3d12_video_screen.cpp | 320 +++++++++++++----- 3 files changed, 585 insertions(+), 79 deletions(-) diff --git a/src/gallium/drivers/d3d12/d3d12_video_enc.cpp b/src/gallium/drivers/d3d12/d3d12_video_enc.cpp index 0e6f13ae448..14dcdd40f3c 100644 --- a/src/gallium/drivers/d3d12/d3d12_video_enc.cpp +++ b/src/gallium/drivers/d3d12/d3d12_video_enc.cpp @@ -484,6 +484,179 @@ d3d12_video_encoder_update_qpmap_input(struct d3d12_video_encoder *pD3D12Enc, #endif } +/* +* Called on encoder creation with the encoder creation parameters +*/ +void d3d12_video_encoder_initialize_two_pass(struct d3d12_video_encoder *pD3D12Enc, + const struct pipe_enc_two_pass_encoder_config& two_pass) +{ +#if D3D12_VIDEO_USE_NEW_ENCODECMDLIST4_INTERFACE + + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc = {}; + + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.two_pass_support.value = + pD3D12Enc->m_screen->get_video_param(pD3D12Enc->m_screen, + pD3D12Enc->base.profile, + pD3D12Enc->base.entrypoint, + PIPE_VIDEO_CAP_ENC_TWO_PASS); + + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.AppRequested = two_pass.enable; + if (two_pass.pow2_downscale_factor > 0) + { + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.Pow2DownscaleFactor = two_pass.pow2_downscale_factor; + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.bUseExternalDPBScaling = two_pass.skip_1st_dpb_texture; + } + +#endif // D3D12_VIDEO_USE_NEW_ENCODECMDLIST4_INTERFACE +} + +#if D3D12_VIDEO_USE_NEW_ENCODECMDLIST4_INTERFACE +static +struct pipe_enc_two_pass_frame_config +d3d12_video_encoder_get_two_pass_config_from_picparams(struct pipe_picture_desc* picture, + enum pipe_video_format codec) +{ + struct pipe_enc_two_pass_frame_config twopass_frame_config = {}; + switch (codec) { +#if VIDEO_CODEC_H264ENC + case PIPE_VIDEO_FORMAT_MPEG4_AVC: + { + twopass_frame_config = ((struct pipe_h264_enc_picture_desc *)picture)->twopass_frame_config; + } break; +#endif +#if VIDEO_CODEC_H265ENC + case PIPE_VIDEO_FORMAT_HEVC: + { + twopass_frame_config = ((struct pipe_h265_enc_picture_desc *)picture)->twopass_frame_config; + } break; +#endif + default: + { + unreachable("Unsupported pipe_video_format"); + } break; + } +return twopass_frame_config; +} +#endif // D3D12_VIDEO_USE_NEW_ENCODECMDLIST4_INTERFACE + +/* +* Caller once per frame to update the frame specific two pass settings +* such as the skip_1st_pass_on_frame flag. +* +* This is called after the encoder has been created and the +* encoder settings have been initialized in d3d12_video_encoder_initialize_two_pass +* with the encoder creation two pass parameters. +*/ +void +d3d12_video_encoder_update_two_pass_frame_settings(struct d3d12_video_encoder *pD3D12Enc, + enum pipe_video_format codec, + struct pipe_picture_desc* picture) +{ +#if D3D12_VIDEO_USE_NEW_ENCODECMDLIST4_INTERFACE + if (pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.AppRequested) + { + struct pipe_enc_two_pass_frame_config two_pass_frame_cfg = d3d12_video_encoder_get_two_pass_config_from_picparams(picture, codec); + + // Assume two pass enabled for all frames unless supports_dynamic_1st_pass_skip is not supported and skip requested + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.bSkipTwoPassInCurrentFrame = false; + if (pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.two_pass_support.bits.supports_dynamic_1st_pass_skip) + { + // Honor the app's request to skip the 1st pass if supports_dynamic_1st_pass_skip supported + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.bSkipTwoPassInCurrentFrame = two_pass_frame_cfg.skip_1st_pass != 0; + } + + // + // For when two pass is enabled for this frame AND Pow2DownscaleFactor > 0 + // also convert input downscaled texture and input recon pics (in/out) + // + if ((!pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.bSkipTwoPassInCurrentFrame) && + (pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.Pow2DownscaleFactor > 0)) + { + + // + // Convert the input downscaled texture from the pic params + // + struct d3d12_video_buffer *pDownscaledInputBuffer12 = (struct d3d12_video_buffer *) two_pass_frame_cfg.downscaled_source; + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.pDownscaledInputTexture = pDownscaledInputBuffer12 ? d3d12_resource_resource(pDownscaledInputBuffer12->texture) : NULL; + + // + // Convert the DPB input and output params from the picparams DPB array + // + switch (codec) { +#if VIDEO_CODEC_H264ENC + case PIPE_VIDEO_FORMAT_MPEG4_AVC: + { + struct pipe_h264_enc_picture_desc *h264Pic = (struct pipe_h264_enc_picture_desc *) picture; + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pResources.resize(h264Pic->dpb_size); + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pSubresources.resize(h264Pic->dpb_size); + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.FrameAnalysisReconstructedPictureOutput = { NULL, 0u }; + for (uint8_t i = 0; i < h264Pic->dpb_size; i++) { + struct d3d12_video_buffer *vidbuf = (struct d3d12_video_buffer *) h264Pic->dpb[i].downscaled_buffer; + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pResources[i] = d3d12_resource_resource(vidbuf->texture); + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pSubresources[i] = vidbuf->idx_texarray_slots; + if (!pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.bUseExternalDPBScaling && // Pass NULL to the output recon pic 1st pass if bUseExternalDPBScaling set + (h264Pic->dpb[i].pic_order_cnt == h264Pic->pic_order_cnt)) + { + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.FrameAnalysisReconstructedPictureOutput.pReconstructedPicture = + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pResources[i]; + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.FrameAnalysisReconstructedPictureOutput.ReconstructedPictureSubresource = + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pSubresources[i]; + } + } + + // Now that we found the recon pio in the loop above + // only fill the references for frame types as DX12 expects + if ((h264Pic->picture_type == PIPE_H2645_ENC_PICTURE_TYPE_I) || + (h264Pic->picture_type == PIPE_H2645_ENC_PICTURE_TYPE_IDR)) + { + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pResources.clear(); + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pSubresources.clear(); + } + + } break; +#endif +#if VIDEO_CODEC_H265ENC + case PIPE_VIDEO_FORMAT_HEVC: + { + struct pipe_h265_enc_picture_desc *h265Pic = (struct pipe_h265_enc_picture_desc *) picture; + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pResources.resize(h265Pic->dpb_size); + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pSubresources.resize(h265Pic->dpb_size); + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.FrameAnalysisReconstructedPictureOutput = { NULL, 0u }; + for (uint8_t i = 0; i < h265Pic->dpb_size; i++) { + struct d3d12_video_buffer *vidbuf = (struct d3d12_video_buffer *) h265Pic->dpb[i].downscaled_buffer; + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pResources[i] = d3d12_resource_resource(vidbuf->texture); + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pSubresources[i] = vidbuf->idx_texarray_slots; + + if (!pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.bUseExternalDPBScaling && // Pass NULL to the output recon pic 1st pass if bUseExternalDPBScaling set + (h265Pic->dpb[i].pic_order_cnt == h265Pic->pic_order_cnt)) + { + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.FrameAnalysisReconstructedPictureOutput.pReconstructedPicture = + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pResources[i]; + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.FrameAnalysisReconstructedPictureOutput.ReconstructedPictureSubresource = + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pSubresources[i]; + } + } + + // Now that we found the recon pio in the loop above + // only fill the references for frame types as DX12 expects + if (h265Pic->picture_type == PIPE_H2645_ENC_PICTURE_TYPE_IDR) + { + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pResources.clear(); + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pSubresources.clear(); + } + + } break; +#endif + default: + { + unreachable("Unsupported pipe_video_format"); + } break; + } + } + } +#endif // D3D12_VIDEO_USE_NEW_ENCODECMDLIST4_INTERFACE +} + void d3d12_video_encoder_update_dirty_rects(struct d3d12_video_encoder *pD3D12Enc, const struct pipe_enc_dirty_info& rects) @@ -805,6 +978,11 @@ d3d12_video_encoder_reconfigure_encoder_objects(struct d3d12_video_encoder *pD3D heapFlags |= D3D12_VIDEO_ENCODER_HEAP_FLAG_ALLOW_SUBREGION_NOTIFICATION_SINGLE_BUFFER; } + if (pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.AppRequested) + { + heapFlags |= D3D12_VIDEO_ENCODER_HEAP_FLAG_ALLOW_RATE_CONTROL_FRAME_ANALYSIS; + } + D3D12_VIDEO_ENCODER_HEAP_DESC1 heapDesc1 = { pD3D12Enc->m_NodeMask, heapFlags, @@ -816,7 +994,9 @@ d3d12_video_encoder_reconfigure_encoder_objects(struct d3d12_video_encoder *pD3D // resolution list &pD3D12Enc->m_currentEncodeConfig.m_currentResolution, // UINT Pow2DownscaleFactor - 0, + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.AppRequested ? + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.Pow2DownscaleFactor + : 0, }; // Create encoder heap @@ -1703,6 +1883,11 @@ bool d3d12_video_encoder_negotiate_requested_features_and_d3d12_driver_caps(stru 0) { debug_printf("[d3d12_video_encoder] Requested input dirty regions is not supported\n"); } + + if ((capEncoderSupportData1.ValidationFlags & D3D12_VIDEO_ENCODER_VALIDATION_FLAG_FRAME_ANALYSIS_NOT_SUPPORTED ) != + 0) { + debug_printf("[d3d12_video_encoder] Requested two pass encode is not supported\n"); + } #else #endif @@ -1756,6 +1941,12 @@ bool d3d12_video_encoder_query_d3d12_driver_caps(struct d3d12_video_encoder *pD3 capEncoderSupportData1.MotionSearch.BidirectionalRefFrameEnabled = TRUE; } + capEncoderSupportData1.FrameAnalysis.Enabled = pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.AppRequested; + if (capEncoderSupportData1.FrameAnalysis.Enabled) + { + capEncoderSupportData1.FrameAnalysis.Pow2DownscaleFactor = pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.Pow2DownscaleFactor; + } + #endif enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile); @@ -2118,6 +2309,7 @@ d3d12_video_encoder_update_current_encoder_config_state(struct d3d12_video_encod d3d12_video_encoder_update_qpmap_input(pD3D12Enc, ((struct pipe_h264_enc_picture_desc *)picture)->input_gpu_qpmap, ((struct pipe_h264_enc_picture_desc *)picture)->roi, ((struct pipe_h264_enc_picture_desc *)picture)->pic_ctrl.temporal_id); + d3d12_video_encoder_update_two_pass_frame_settings(pD3D12Enc, codec, picture); // ...encoder_config_state_h264 calls encoder support cap, set any state before this call bCodecUpdatesSuccess = d3d12_video_encoder_update_current_encoder_config_state_h264(pD3D12Enc, srcTextureDesc, picture); } break; @@ -2135,6 +2327,7 @@ d3d12_video_encoder_update_current_encoder_config_state(struct d3d12_video_encod d3d12_video_encoder_update_qpmap_input(pD3D12Enc, ((struct pipe_h265_enc_picture_desc *)picture)->input_gpu_qpmap, ((struct pipe_h265_enc_picture_desc *)picture)->roi, ((struct pipe_h265_enc_picture_desc *)picture)->pic.temporal_id); + d3d12_video_encoder_update_two_pass_frame_settings(pD3D12Enc, codec, picture); // ...encoder_config_state_hevc calls encoder support cap, set any state before this call bCodecUpdatesSuccess = d3d12_video_encoder_update_current_encoder_config_state_hevc(pD3D12Enc, srcTextureDesc, picture); } break; @@ -2294,6 +2487,7 @@ d3d12_video_encoder_create_encoder(struct pipe_context *context, const struct pi pD3D12Enc->supports_sliced_fences.value = context->screen->get_video_param(context->screen, codec->profile, codec->entrypoint, PIPE_VIDEO_CAP_ENC_SLICED_NOTIFICATIONS); + d3d12_video_encoder_initialize_two_pass(pD3D12Enc, codec->two_pass); return &pD3D12Enc->base; @@ -3447,6 +3641,93 @@ d3d12_video_encoder_encode_bitstream_impl(struct pipe_video_codec *codec, d12_gpu_stats_rc_bitallocation_map = d3d12_resource_resource(pD3D12Enc->m_currentEncodeConfig.m_GPURCBitAllocationStatsResource); } + D3D12_VIDEO_ENCODER_FRAME_ANALYSIS FrameAnalysis = {}; + D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE FrameAnalysisReconstructedPicture = {}; + std::vector pTwoPassExtraBarriers; + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc[pD3D12Enc->m_currentEncodeConfig.m_activeRateControlIndex].m_Flags &= + ~D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_FRAME_ANALYSIS; + + if ((pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.AppRequested) && + (!pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.bSkipTwoPassInCurrentFrame)) + { + pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc[pD3D12Enc->m_currentEncodeConfig.m_activeRateControlIndex].m_Flags |= + D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_FRAME_ANALYSIS; + + // + // When Pow2DownscaleFactor is zero, is full resolution two pass, which leaves FrameAnalysis empty/zero filled. + // For lower 1st pass resolution, we fill FrameAnalysis appropiately + // + + if (pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.Pow2DownscaleFactor > 0) + { + // + // Schedule barrier transitions (reverse ones are scheduled later by doing swap to pTwoPassExtraBarriers) + // + + if (pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.pDownscaledInputTexture) + { + pTwoPassExtraBarriers.push_back(CD3DX12_RESOURCE_BARRIER::Transition( + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.pDownscaledInputTexture, + D3D12_RESOURCE_STATE_COMMON, + D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ)); + } + + if (pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pResources.size() > 0) + { + if ((pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags & + D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RECONSTRUCTED_FRAMES_REQUIRE_TEXTURE_ARRAYS) != 0) + { + pTwoPassExtraBarriers.push_back(CD3DX12_RESOURCE_BARRIER::Transition( + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pResources[0], + D3D12_RESOURCE_STATE_COMMON, + D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ)); + } + else + { + for (unsigned i = 0; i < pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pResources.size(); i++) + pTwoPassExtraBarriers.push_back(CD3DX12_RESOURCE_BARRIER::Transition( + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pResources[i], + D3D12_RESOURCE_STATE_COMMON, + D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ)); + } + } + + if (pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.FrameAnalysisReconstructedPictureOutput.pReconstructedPicture) // can be NULL if external dpb scaling + { + pTwoPassExtraBarriers.push_back(CD3DX12_RESOURCE_BARRIER::Transition( + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.FrameAnalysisReconstructedPictureOutput.pReconstructedPicture, + D3D12_RESOURCE_STATE_COMMON, + D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE)); + } + + // + // Set EncodeFrame params + // + + FrameAnalysisReconstructedPicture = pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.FrameAnalysisReconstructedPictureOutput; + + FrameAnalysis = + { + // ID3D12Resource *pDownscaledFrame; + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.pDownscaledInputTexture, + // UINT64 Subresource; + 0u, + // D3D12_VIDEO_ENCODE_REFERENCE_FRAMES DownscaledReferences; + { + // UINT NumTexture2Ds; + static_cast(pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pResources.size()), + // _Field_size_full_(NumTexture2Ds) ID3D12Resource **ppTexture2Ds; + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pResources.data(), + // _Field_size_full_(NumTexture2Ds) UINT *pSubresources; + pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pSubresources.data(), + }, + }; + } + + spEncodeCommandList4->ResourceBarrier(static_cast(pTwoPassExtraBarriers.size()), + pTwoPassExtraBarriers.data()); + } + const D3D12_VIDEO_ENCODER_ENCODEFRAME_INPUT_ARGUMENTS1 inputStreamArguments = { // D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_DESC { // D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAGS @@ -3475,7 +3756,7 @@ d3d12_video_encoder_encode_bitstream_impl(struct pipe_video_codec *codec, // D3D12_VIDEO_ENCODER_QUANTIZATION_OPAQUE_MAP QuantizationTextureMap; QuantizationTextureMap, // D3D12_VIDEO_ENCODER_FRAME_ANALYSIS FrameAnalysis; - { }, + FrameAnalysis, }, pInputVideoD3D12Res, inputVideoD3D12Subresource, @@ -3659,7 +3940,7 @@ d3d12_video_encoder_encode_bitstream_impl(struct pipe_video_codec *codec, // D3D12_VIDEO_ENCODER_ENCODE_OPERATION_METADATA_BUFFER { pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_spMetadataOutputBuffer.Get(), 0 }, // D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE FrameAnalysisReconstructedPicture; - {}, + FrameAnalysisReconstructedPicture, }; debug_printf("DX12 EncodeFrame submission fenceValue %" PRIu64 " current_metadata_slot %" PRIu64 " - POC %d picture_type %s LayoutMode %d SlicesCount %d IRMode %d IRIndex %d\n", @@ -3803,6 +4084,12 @@ d3d12_video_encoder_encode_bitstream_impl(struct pipe_video_codec *codec, } spEncodeCommandList4->ResourceBarrier(static_cast(pSlicedEncodingExtraBarriers.size()), pSlicedEncodingExtraBarriers.data()); + + for (auto &BarrierDesc : pTwoPassExtraBarriers) { + std::swap(BarrierDesc.Transition.StateBefore, BarrierDesc.Transition.StateAfter); + } + spEncodeCommandList4->ResourceBarrier(static_cast(pTwoPassExtraBarriers.size()), + pTwoPassExtraBarriers.data()); } else #endif // D3D12_VIDEO_USE_NEW_ENCODECMDLIST4_INTERFACE diff --git a/src/gallium/drivers/d3d12/d3d12_video_enc.h b/src/gallium/drivers/d3d12/d3d12_video_enc.h index dcb932cf74b..8694edc636e 100644 --- a/src/gallium/drivers/d3d12/d3d12_video_enc.h +++ b/src/gallium/drivers/d3d12/d3d12_video_enc.h @@ -378,6 +378,50 @@ struct D3D12EncodeConfiguration struct d3d12_resource *m_GPUQPStatsResource = NULL; struct d3d12_resource *m_GPUSATDStatsResource = NULL; struct d3d12_resource *m_GPURCBitAllocationStatsResource = NULL; + struct + { + // + // Cached caps on encoder creation members + // + union pipe_enc_cap_two_pass two_pass_support; + + // + // Encoder scope members + // + + // Indicates if two pass enabled + bool AppRequested; + + // Indicates, if enabled, the downscale factor for two pass + UINT Pow2DownscaleFactor; + + // If enabled, disable 1st pass recon pic output + // as app will generate that by downscaling the + // 2nd pass dpn recon externally + bool bUseExternalDPBScaling; + + // + // Per frame scope members + // + + // If AppRequested is set, this + // indicates when a specific frame two pass + // will be disabled and its updated per frame + // from the pipe pic params + // + // Note: IHV drivers may not support two pass + // on all frame types, so the ones not supported + // will naturally be always "skipped" anyways + bool bSkipTwoPassInCurrentFrame; + ID3D12Resource* pDownscaledInputTexture; + struct + { + std::vector pResources; + std::vector pSubresources; + } DownscaledReferences; + D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE FrameAnalysisReconstructedPictureOutput; + + } m_TwoPassEncodeDesc = {}; #endif }; @@ -703,6 +747,13 @@ d3d12_video_encoder_update_qpmap_input(struct d3d12_video_encoder *pD3D12Enc, struct pipe_resource* qpmap, struct pipe_enc_roi roi, uint32_t temporal_id); +void +d3d12_video_encoder_initialize_two_pass(struct d3d12_video_encoder *pD3D12Enc, + const struct pipe_enc_two_pass_encoder_config& two_pass); +void +d3d12_video_encoder_update_two_pass_frame_settings(struct d3d12_video_encoder *pD3D12Enc, + enum pipe_video_format codec, + struct pipe_picture_desc* picture); /// /// d3d12_video_encoder functions ends /// diff --git a/src/gallium/drivers/d3d12/d3d12_video_screen.cpp b/src/gallium/drivers/d3d12/d3d12_video_screen.cpp index 71aa6d37410..973f53f6b90 100644 --- a/src/gallium/drivers/d3d12/d3d12_video_screen.cpp +++ b/src/gallium/drivers/d3d12/d3d12_video_screen.cpp @@ -907,8 +907,235 @@ static d3d12_video_encode_get_hevc_codec_support ( const D3D12_VIDEO_ENCODER_COD } #endif // VIDEO_CODEC_H265ENC +static bool +d3d12_has_video_process_support(struct pipe_screen *pscreen, + D3D12_FEATURE_DATA_VIDEO_PROCESS_SUPPORT &supportCaps, + D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC& outMinSupportedInput, + D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC& outMaxSupportedInput) +{ + ComPtr spD3D12VideoDevice; + struct d3d12_screen *pD3D12Screen = (struct d3d12_screen *) pscreen; + if (FAILED(pD3D12Screen->dev->QueryInterface(IID_PPV_ARGS(spD3D12VideoDevice.GetAddressOf())))) { + // No video process support in underlying d3d12 device (needs ID3D12VideoDevice) + return false; + } + + D3D12_FEATURE_DATA_VIDEO_FEATURE_AREA_SUPPORT VideoFeatureAreaSupport = {}; + if (FAILED(spD3D12VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_FEATURE_AREA_SUPPORT, + &VideoFeatureAreaSupport, + sizeof(VideoFeatureAreaSupport)))) { + return false; + } + + D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC resolutionsList[] = { + { 8192, 8192 }, // 8k + { 8192, 4320 }, // 8k - alternative + { 7680, 4800 }, // 8k - alternative + { 7680, 4320 }, // 8k - alternative + { 4096, 2304 }, // 2160p (4K) + { 4096, 2160 }, // 2160p (4K) - alternative + { 2560, 1440 }, // 1440p + { 1920, 1200 }, // 1200p + { 1920, 1080 }, // 1080p + { 1280, 720 }, // 720p + { 800, 600 }, + { 352, 480 }, + { 352, 240 }, + { 176, 144 }, + { 128, 128 }, + { 96, 96 }, + { 64, 64 }, + { 32, 32 }, + { 16, 16 }, + { 8, 8 }, + { 4, 4 }, + { 2, 2 }, + { 1, 1 }, + }; + + outMinSupportedInput = {}; + outMaxSupportedInput = {}; + uint32_t idxResol = 0; + bool bSupportsAny = false; + while (idxResol < ARRAY_SIZE(resolutionsList)) { + supportCaps.InputSample.Width = resolutionsList[idxResol].Width; + supportCaps.InputSample.Height = resolutionsList[idxResol].Height; + if (SUCCEEDED(spD3D12VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_PROCESS_SUPPORT, &supportCaps, sizeof(supportCaps)))) { + if ((supportCaps.SupportFlags & D3D12_VIDEO_PROCESS_SUPPORT_FLAG_SUPPORTED) != 0) + { + // Save the first (maximum) + if(!bSupportsAny) { + outMaxSupportedInput = resolutionsList[idxResol]; + bSupportsAny = true; + } + + // Keep saving the other supported values to get the minimum + outMinSupportedInput = resolutionsList[idxResol]; + } + } + idxResol++; + } + + return VideoFeatureAreaSupport.VideoProcessSupport && bSupportsAny; +} + #if D3D12_VIDEO_USE_NEW_ENCODECMDLIST4_INTERFACE +static +union pipe_enc_cap_two_pass +query_two_pass_support(struct pipe_screen *pscreen, + D3D12_VIDEO_ENCODER_INPUT_MAP_SESSION_INFO sessionInfo, + ID3D12VideoDevice3* pD3D12VideoDevice, + bool bDriverSupportsReadableDPBReconPic) +{ + union pipe_enc_cap_two_pass two_pass_support = {}; + D3D12_FEATURE_DATA_VIDEO_ENCODER_RATE_CONTROL_FRAME_ANALYSIS twoPassCapData = + { + // UINT NodeIndex; + 0u, + //D3D12_VIDEO_ENCODER_CODEC Codec; + sessionInfo.Codec, + //D3D12_VIDEO_ENCODER_PROFILE_DESC Profile; + sessionInfo.Profile, + //D3D12_VIDEO_ENCODER_LEVEL_SETTING Level; + sessionInfo.Level, + //DXGI_FORMAT InputFormat; + sessionInfo.InputFormat, + //D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC InputResolution; + sessionInfo.InputResolution, + //D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION CodecConfiguration; + sessionInfo.CodecConfiguration, + //D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE SubregionFrameEncoding; + sessionInfo.SubregionFrameEncoding, + //D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA SubregionFrameEncodingData; + sessionInfo.SubregionFrameEncodingData, + //D3D12_VIDEO_ENCODER_QPMAP_CONFIGURATION QPMap; + { + //BOOL Enabled; + FALSE, + //D3D12_VIDEO_ENCODER_INPUT_MAP_SOURCE MapSource; + D3D12_VIDEO_ENCODER_INPUT_MAP_SOURCE_CPU_BUFFER, + }, + //D3D12_VIDEO_ENCODER_DIRTY_REGIONS_CONFIGURATION DirtyRegions; + { + //BOOL Enabled; + FALSE, + //D3D12_VIDEO_ENCODER_INPUT_MAP_SOURCE MapSource; + D3D12_VIDEO_ENCODER_INPUT_MAP_SOURCE_CPU_BUFFER, + //D3D12_VIDEO_ENCODER_DIRTY_REGIONS_MAP_VALUES_MODE MapValuesType; + D3D12_VIDEO_ENCODER_DIRTY_REGIONS_MAP_VALUES_MODE_DIRTY, + }, + //D3D12_VIDEO_ENCODER_MOTION_SEARCH_CONFIGURATION MotionSearch; + { + //BOOL Enabled; + FALSE, + //D3D12_VIDEO_ENCODER_INPUT_MAP_SOURCE MapSource; + D3D12_VIDEO_ENCODER_INPUT_MAP_SOURCE_CPU_BUFFER, + //D3D12_VIDEO_ENCODER_FRAME_MOTION_SEARCH_MODE MotionSearchMode; + D3D12_VIDEO_ENCODER_FRAME_MOTION_SEARCH_MODE_FULL_SEARCH, + //BOOL BidirectionalRefFrameEnabled; + FALSE, + }, + //UINT Pow2DownscaleFactor; + 0u, + //D3D12_VIDEO_ENCODER_RATE_CONTROL_FRAME_ANALYSIS_SUPPORT_FLAGS SupportFlags; + D3D12_VIDEO_ENCODER_RATE_CONTROL_FRAME_ANALYSIS_SUPPORT_FLAG_NONE, + }; + + // + // Find out if the driver supports D3D12_VIDEO_SCALE_SUPPORT_FLAG_DPB_ENCODER_RESOURCES + // + + D3D12_FEATURE_DATA_VIDEO_PROCESS_SUPPORT vpblitCapsData = + { + 0, // NodeIndex + { + sessionInfo.InputResolution.Width, + sessionInfo.InputResolution.Height, + { + sessionInfo.InputFormat, + DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709 + } + }, + D3D12_VIDEO_FIELD_TYPE_NONE, + D3D12_VIDEO_FRAME_STEREO_FORMAT_NONE, + { + 30, + 1 + }, + { + sessionInfo.InputFormat, + DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709 + }, + D3D12_VIDEO_FRAME_STEREO_FORMAT_NONE, + { + 30, + 1 + }, + }; + + bool bDriverSupportsOpaqueDPBReconScaling = false; + D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC minSupportedInput = {}; + D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC maxSupportedInput = {}; + if (d3d12_has_video_process_support(pscreen, vpblitCapsData, minSupportedInput, maxSupportedInput) && + (sessionInfo.InputResolution.Width <= maxSupportedInput.Width) && + (sessionInfo.InputResolution.Height <= maxSupportedInput.Height)) + { + bDriverSupportsOpaqueDPBReconScaling = (vpblitCapsData.ScaleSupport.Flags & D3D12_VIDEO_SCALE_SUPPORT_FLAG_DPB_ENCODER_RESOURCES) != 0; + } + + // + // Find the min/max supported Pow2DownscaleFactor values. + // + + constexpr unsigned c_MaxPow2DownscaleToTry = 3u; + // set a value bigger than c_MaxPow2DownscaleToTry and find the min in the loop below using std::min + two_pass_support.bits.min_pow2_downscale_factor = c_MaxPow2DownscaleToTry + 1u; + + for (unsigned CurPow2DownscaleFactor = 0; CurPow2DownscaleFactor <= c_MaxPow2DownscaleToTry; CurPow2DownscaleFactor++) + { + // + // Query the cap with CurPow2DownscaleFactor + // + + twoPassCapData.Pow2DownscaleFactor = CurPow2DownscaleFactor; + + if ((SUCCEEDED(pD3D12VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_ENCODER_RATE_CONTROL_FRAME_ANALYSIS, &twoPassCapData, sizeof(twoPassCapData)))) && + (twoPassCapData.SupportFlags & D3D12_VIDEO_ENCODER_RATE_CONTROL_FRAME_ANALYSIS_SUPPORT_FLAG_SUPPORTED)) + { + two_pass_support.bits.supports_two_pass = 1u; + two_pass_support.bits.min_pow2_downscale_factor = std::min(two_pass_support.bits.min_pow2_downscale_factor, twoPassCapData.Pow2DownscaleFactor); + two_pass_support.bits.max_pow2_downscale_factor = std::max(two_pass_support.bits.min_pow2_downscale_factor, twoPassCapData.Pow2DownscaleFactor); + two_pass_support.bits.supports_dynamic_1st_pass_skip = (twoPassCapData.SupportFlags & D3D12_VIDEO_ENCODER_RATE_CONTROL_FRAME_ANALYSIS_SUPPORT_FLAG_DYNAMIC_1ST_PASS_SKIP) ? 1u : 0u; + + if (twoPassCapData.SupportFlags & D3D12_VIDEO_ENCODER_RATE_CONTROL_FRAME_ANALYSIS_SUPPORT_FLAG_EXTERNAL_DPB_DOWNSCALING) + { + // + // For external DPB recon downscaling, one of these must be true: + // 1. The DPB pictures are non-opaque layout (e.g without the reference only resource flag) + // 2. The DPB pictures are opaque layout, but the driver also supports + // D3D12_VIDEO_SCALE_SUPPORT_FLAG_DPB_ENCODER_RESOURCES (in the VPblit caps) + // + if (bDriverSupportsReadableDPBReconPic) + { + two_pass_support.bits.supports_1pass_recon_writing_skip = 1u; + } + else + { + two_pass_support.bits.supports_1pass_recon_writing_skip = bDriverSupportsOpaqueDPBReconScaling ? 1u : 0u; + } + } + } + } + + if (!two_pass_support.bits.supports_two_pass) + { + two_pass_support = {}; // Clean min_pow2_downscale_factor + } + + return two_pass_support; +} + static union pipe_enc_cap_dirty_info query_dirty_rects_support(D3D12_VIDEO_ENCODER_INPUT_MAP_SESSION_INFO sessionInfo, @@ -1251,7 +1478,8 @@ d3d12_has_video_encode_support(struct pipe_screen *pscreen, union pipe_enc_cap_sliced_notifications &sliced_encode_support, union pipe_enc_cap_dirty_info &dirty_rects_support_gpu, union pipe_enc_cap_qpmap &qpmap_support, - union pipe_enc_cap_motion_vector_map &gpu_motion_input_support) + union pipe_enc_cap_motion_vector_map &gpu_motion_input_support, + union pipe_enc_cap_two_pass &two_pass_support) { ComPtr spD3D12VideoDevice; struct d3d12_screen *pD3D12Screen = (struct d3d12_screen *) pscreen; @@ -1389,6 +1617,9 @@ d3d12_has_video_encode_support(struct pipe_screen *pscreen, sliced_encode_support = get_sliced_encode_support(capEncoderSupportData1.SupportFlags); qpmap_support = get_qpmap_gpuinput_support(sessionInfo, spD3D12VideoDevice.Get()); gpu_motion_input_support = get_motion_gpuinput_support(sessionInfo, spD3D12VideoDevice.Get()); + two_pass_support = query_two_pass_support(pscreen, sessionInfo, spD3D12VideoDevice.Get(), + ((capEncoderSupportData1.SupportFlags & + D3D12_VIDEO_ENCODER_SUPPORT_FLAG_READABLE_RECONSTRUCTED_PICTURE_LAYOUT_AVAILABLE) != 0)); #endif } } break; @@ -1715,6 +1946,9 @@ d3d12_has_video_encode_support(struct pipe_screen *pscreen, get_gpu_output_stats_support(sessionInfo, capEncoderSupportData1.SupportFlags, spD3D12VideoDevice.Get(), gpu_stats_qp, gpu_stats_satd, gpu_stats_rcbits); sliced_encode_support = get_sliced_encode_support(capEncoderSupportData1.SupportFlags); gpu_motion_input_support = get_motion_gpuinput_support(sessionInfo, spD3D12VideoDevice.Get()); + two_pass_support = query_two_pass_support(pscreen, sessionInfo, spD3D12VideoDevice.Get(), + ((capEncoderSupportData1.SupportFlags & + D3D12_VIDEO_ENCODER_SUPPORT_FLAG_READABLE_RECONSTRUCTED_PICTURE_LAYOUT_AVAILABLE) != 0)); #endif } } @@ -2080,79 +2314,6 @@ d3d12_screen_get_video_param_decode(struct pipe_screen *pscreen, } } - -static bool -d3d12_has_video_process_support(struct pipe_screen *pscreen, - D3D12_FEATURE_DATA_VIDEO_PROCESS_SUPPORT &supportCaps, - D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC& outMinSupportedInput, - D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC& outMaxSupportedInput) -{ - ComPtr spD3D12VideoDevice; - struct d3d12_screen *pD3D12Screen = (struct d3d12_screen *) pscreen; - if (FAILED(pD3D12Screen->dev->QueryInterface(IID_PPV_ARGS(spD3D12VideoDevice.GetAddressOf())))) { - // No video process support in underlying d3d12 device (needs ID3D12VideoDevice) - return false; - } - - D3D12_FEATURE_DATA_VIDEO_FEATURE_AREA_SUPPORT VideoFeatureAreaSupport = {}; - if (FAILED(spD3D12VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_FEATURE_AREA_SUPPORT, - &VideoFeatureAreaSupport, - sizeof(VideoFeatureAreaSupport)))) { - return false; - } - - D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC resolutionsList[] = { - { 8192, 8192 }, // 8k - { 8192, 4320 }, // 8k - alternative - { 7680, 4800 }, // 8k - alternative - { 7680, 4320 }, // 8k - alternative - { 4096, 2304 }, // 2160p (4K) - { 4096, 2160 }, // 2160p (4K) - alternative - { 2560, 1440 }, // 1440p - { 1920, 1200 }, // 1200p - { 1920, 1080 }, // 1080p - { 1280, 720 }, // 720p - { 800, 600 }, - { 352, 480 }, - { 352, 240 }, - { 176, 144 }, - { 128, 128 }, - { 96, 96 }, - { 64, 64 }, - { 32, 32 }, - { 16, 16 }, - { 8, 8 }, - { 4, 4 }, - { 2, 2 }, - { 1, 1 }, - }; - - outMinSupportedInput = {}; - outMaxSupportedInput = {}; - uint32_t idxResol = 0; - bool bSupportsAny = false; - while (idxResol < ARRAY_SIZE(resolutionsList)) { - supportCaps.InputSample.Width = resolutionsList[idxResol].Width; - supportCaps.InputSample.Height = resolutionsList[idxResol].Height; - if (SUCCEEDED(spD3D12VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_PROCESS_SUPPORT, &supportCaps, sizeof(supportCaps)))) { - if ((supportCaps.SupportFlags & D3D12_VIDEO_PROCESS_SUPPORT_FLAG_SUPPORTED) != 0) - { - // Save the first (maximum) - if(!bSupportsAny) { - outMaxSupportedInput = resolutionsList[idxResol]; - bSupportsAny = true; - } - - // Keep saving the other supported values to get the minimum - outMinSupportedInput = resolutionsList[idxResol]; - } - } - idxResol++; - } - - return VideoFeatureAreaSupport.VideoProcessSupport && bSupportsAny; -} - static int d3d12_screen_get_video_param_postproc(struct pipe_screen *pscreen, enum pipe_video_profile profile, @@ -2305,6 +2466,7 @@ d3d12_screen_get_video_param_encode(struct pipe_screen *pscreen, union pipe_enc_cap_sliced_notifications sliced_encode_support = {}; union pipe_enc_cap_qpmap gpu_qpmap_input = {}; union pipe_enc_cap_motion_vector_map gpu_motion_input = {}; + union pipe_enc_cap_two_pass two_pass_support = {}; memset(&codec_specific_support, 0, sizeof(codec_specific_support)); switch (param) { case PIPE_VIDEO_CAP_REQUIRES_FLUSH_ON_END_FRAME: @@ -2376,6 +2538,7 @@ d3d12_screen_get_video_param_encode(struct pipe_screen *pscreen, case PIPE_VIDEO_CAP_ENC_DIRTY_MAPS: case PIPE_VIDEO_CAP_ENC_QP_MAPS: case PIPE_VIDEO_CAP_ENC_MOTION_VECTOR_MAPS: + case PIPE_VIDEO_CAP_ENC_TWO_PASS: { if (d3d12_has_video_encode_support(pscreen, profile, @@ -2404,7 +2567,8 @@ d3d12_screen_get_video_param_encode(struct pipe_screen *pscreen, sliced_encode_support, dirty_rects_support_gpu, gpu_qpmap_input, - gpu_motion_input)) { + gpu_motion_input, + two_pass_support)) { DXGI_FORMAT format = d3d12_convert_pipe_video_profile_to_dxgi_format(profile); auto pipeFmt = d3d12_get_pipe_format(format); @@ -2507,6 +2671,8 @@ d3d12_screen_get_video_param_encode(struct pipe_screen *pscreen, return gpu_qpmap_input.value; } else if (param == PIPE_VIDEO_CAP_ENC_MOTION_VECTOR_MAPS) { return gpu_motion_input.value; + } else if (param == PIPE_VIDEO_CAP_ENC_TWO_PASS ) { + return two_pass_support.value; } } } else if (param == PIPE_VIDEO_CAP_ENC_QUALITY_LEVEL) { @@ -2584,6 +2750,7 @@ d3d12_video_encode_requires_texture_array_dpb(struct d3d12_screen* pScreen, enum union pipe_enc_cap_sliced_notifications sliced_encode_support = {}; union pipe_enc_cap_qpmap gpu_qpmap_input = {}; union pipe_enc_cap_motion_vector_map gpu_motion_input = {}; + union pipe_enc_cap_two_pass two_pass_support = {}; if (d3d12_has_video_encode_support(&pScreen->base, profile, maxLvlEncode, @@ -2611,7 +2778,8 @@ d3d12_video_encode_requires_texture_array_dpb(struct d3d12_screen* pScreen, enum sliced_encode_support, dirty_rects_support_gpu, gpu_qpmap_input, - gpu_motion_input)) + gpu_motion_input, + two_pass_support)) { return bVideoEncodeRequiresTextureArray; }