From 98f5fa618b65ff59f3afad6525f11ac4ee762aad Mon Sep 17 00:00:00 2001 From: Wenfeng Gao Date: Tue, 20 Jan 2026 08:05:41 -0800 Subject: [PATCH] mediafoundation: Support externally provided motion hints Added support for externally provided motion hints by reading the MFSampleExtension_MoveRegions sample attribute. The motion hint data is converted into pipe_enc_move_info and passed down to the driver for use during encoding. Reviewed-by: Yubo Xie Part-of: --- .../frontends/mediafoundation/encode.cpp | 24 +++++++- .../frontends/mediafoundation/encode_av1.cpp | 2 +- .../frontends/mediafoundation/encode_h264.cpp | 61 ++++++++++++++++++- .../frontends/mediafoundation/encode_hevc.cpp | 61 ++++++++++++++++++- .../mediafoundation/hmft_entrypoints.h | 3 +- 5 files changed, 146 insertions(+), 5 deletions(-) diff --git a/src/gallium/frontends/mediafoundation/encode.cpp b/src/gallium/frontends/mediafoundation/encode.cpp index 420d70abac1..1f2180ec7be 100644 --- a/src/gallium/frontends/mediafoundation/encode.cpp +++ b/src/gallium/frontends/mediafoundation/encode.cpp @@ -44,6 +44,8 @@ CDX12EncHMFT::PrepareForEncode( IMFSample *pSample, LPDX12EncodeContext *ppDX12E UINT textureHeight = 0u; bool bReceivedDirtyRectBlob = false; uint32_t dirtyRectFrameNum = UINT32_MAX; + bool bReceivedMoveRegionBlob = false; + uint32_t moveRegionFrameNum = UINT32_MAX; LONGLONG inputSampleTime = 0; LONGLONG inputSampleDuration = 0; @@ -323,6 +325,26 @@ CDX12EncHMFT::PrepareForEncode( IMFSample *pSample, LPDX12EncodeContext *ppDX12E } } + // Try to get MFSampleExtension_MoveRegions blob only when the HW supports it. + if( m_EncoderCapabilities.m_HWSupportMoveRects.bits.supports_precision_full_pixel && m_EncoderCapabilities.m_HWSupportMoveRects.bits.max_motion_hints > 0) + { + UINT32 cMoveRegionBlob = 0; + pSample->GetBlobSize( MFSampleExtension_MoveRegions, &cMoveRegionBlob ); + if( cMoveRegionBlob >= sizeof( MOVEREGION_INFO ) ) + { + if( m_pMoveRegionBlob.size() < cMoveRegionBlob ) + { + m_pMoveRegionBlob.resize( cMoveRegionBlob ); + } + if( S_OK == pSample->GetBlob( MFSampleExtension_MoveRegions, m_pMoveRegionBlob.data(), cMoveRegionBlob, &cMoveRegionBlob ) ) + { + MOVEREGION_INFO *pMoveRegionInfo = (MOVEREGION_INFO *) m_pMoveRegionBlob.data(); + moveRegionFrameNum = pMoveRegionInfo->FrameNumber; + bReceivedMoveRegionBlob = true; + } + } + } + if( m_pGOPTracker == nullptr ) { CHECKHR_GOTO( CreateGOPTracker( textureWidth, textureHeight ), done ); @@ -537,7 +559,7 @@ CDX12EncHMFT::PrepareForEncode( IMFSample *pSample, LPDX12EncodeContext *ppDX12E // Call the helper for encoder specific work pDX12EncodeContext->encoderPicInfo.base.in_fence = pPipeEncoderInputFenceHandle; pDX12EncodeContext->encoderPicInfo.base.in_fence_value = pipeEncoderInputFenceHandleValue; - CHECKHR_GOTO( PrepareForEncodeHelper( pDX12EncodeContext, bReceivedDirtyRectBlob, dirtyRectFrameNum ), done ); + CHECKHR_GOTO( PrepareForEncodeHelper( pDX12EncodeContext, bReceivedDirtyRectBlob, dirtyRectFrameNum, bReceivedMoveRegionBlob, moveRegionFrameNum ), done ); // Needs to be run after PrepareForEncodeHelper to know if current frame is used as reference // Only allocate reconstructed picture copy buffer if feature is enabled and supported diff --git a/src/gallium/frontends/mediafoundation/encode_av1.cpp b/src/gallium/frontends/mediafoundation/encode_av1.cpp index 489dc11ede6..9109c72a6bc 100644 --- a/src/gallium/frontends/mediafoundation/encode_av1.cpp +++ b/src/gallium/frontends/mediafoundation/encode_av1.cpp @@ -29,7 +29,7 @@ extern DWORD CalculateQualityFromQP( DWORD QP ); HRESULT -CDX12EncHMFT::PrepareForEncodeHelper( LPDX12EncodeContext pDX12EncodeContext, bool dirtyRectFrameNumSet, uint32_t dirtyRectFrameNum ) +CDX12EncHMFT::PrepareForEncodeHelper( LPDX12EncodeContext pDX12EncodeContext, bool dirtyRectFrameNumSet, uint32_t dirtyRectFrameNum, bool moveRegionFrameNumSet, uint32_t moveRegionFrameNum ) { HRESULT hr = S_OK; // done: diff --git a/src/gallium/frontends/mediafoundation/encode_h264.cpp b/src/gallium/frontends/mediafoundation/encode_h264.cpp index 9dace220e5d..bf91c6e8c3f 100644 --- a/src/gallium/frontends/mediafoundation/encode_h264.cpp +++ b/src/gallium/frontends/mediafoundation/encode_h264.cpp @@ -192,7 +192,7 @@ UpdateH264EncPictureDesc( pipe_h264_enc_picture_desc *pPicInfo, // internal function which contains the codec specific portion of PrepareForEncode HRESULT -CDX12EncHMFT::PrepareForEncodeHelper( LPDX12EncodeContext pDX12EncodeContext, bool dirtyRectFrameNumSet, uint32_t dirtyRectFrameNum ) +CDX12EncHMFT::PrepareForEncodeHelper( LPDX12EncodeContext pDX12EncodeContext, bool dirtyRectFrameNumSet, uint32_t dirtyRectFrameNum, bool moveRegionFrameNumSet, uint32_t moveRegionFrameNum ) { HRESULT hr = S_OK; const reference_frames_tracker_frame_descriptor_h264 *cur_frame_desc = nullptr; @@ -403,6 +403,65 @@ CDX12EncHMFT::PrepareForEncodeHelper( LPDX12EncodeContext pDX12EncodeContext, bo } } + // ---------------------------- + // Move Regions (motion hints) + // ---------------------------- + + // Always reset per-frame optional hint structures + memset( &pPicInfo->move_info, 0, sizeof( pPicInfo->move_info ) ); + + if( moveRegionFrameNumSet && m_EncoderCapabilities.m_HWSupportMoveRects.bits.max_motion_hints > 0 && + m_EncoderCapabilities.m_HWSupportMoveRects.bits.supports_precision_full_pixel && + pPicInfo->picture_type == PIPE_H2645_ENC_PICTURE_TYPE_P ) // P-frames only + { + uint32_t current_poc = pPicInfo->pic_order_cnt; + + // Validate frame number first + if( moveRegionFrameNum != current_poc ) + { + debug_printf( "[dx12 hmft 0x%p] MoveRegions frame mismatch (MRFN=%u, cur POC=%u), ignoring\n", + this, + moveRegionFrameNum, + current_poc ); + } + else + { + MOVEREGION_INFO *pMoveInfo = reinterpret_cast( m_pMoveRegionBlob.data() ); + uint32_t maxRects = m_EncoderCapabilities.m_HWSupportMoveRects.bits.max_motion_hints; + uint32_t numRects = std::min( pMoveInfo->NumMoveRegions, maxRects ); + + if( numRects > 0 ) + { + uint8_t surfaceIndex = pPicInfo->ref_list0[0]; + + if( surfaceIndex == PIPE_H2645_LIST_REF_INVALID_ENTRY ) + { + debug_printf( "[dx12 hmft 0x%p] MoveRegions: invalid L0 reference, ignoring\n", this ); + } + else + { + pPicInfo->move_info.input_mode = PIPE_ENC_MOVE_INFO_INPUT_MODE_RECTS; + pPicInfo->move_info.num_rects = numRects; + pPicInfo->move_info.dpb_reference_index = surfaceIndex; + pPicInfo->move_info.precision = PIPE_ENC_MOVE_INFO_PRECISION_UNIT_FULL_PIXEL; + + for( uint32_t i = 0; i < numRects; i++ ) + { + const MOVE_RECT &mr = pMoveInfo->MoveRegions[i]; + + pPicInfo->move_info.rects[i].source_point.x = mr.SourcePoint.x; + pPicInfo->move_info.rects[i].source_point.y = mr.SourcePoint.y; + + pPicInfo->move_info.rects[i].dest_rect.left = mr.DestRect.left; + pPicInfo->move_info.rects[i].dest_rect.top = mr.DestRect.top; + pPicInfo->move_info.rects[i].dest_rect.right = mr.DestRect.right; + pPicInfo->move_info.rects[i].dest_rect.bottom = mr.DestRect.bottom; + } + } + } + } + } + pPicInfo->gpu_stats_qp_map = pDX12EncodeContext->pPipeResourceQPMapStats; pPicInfo->gpu_stats_satd_map = pDX12EncodeContext->pPipeResourceSATDMapStats; pPicInfo->gpu_stats_rc_bitallocation_map = pDX12EncodeContext->pPipeResourceRCBitAllocMapStats; diff --git a/src/gallium/frontends/mediafoundation/encode_hevc.cpp b/src/gallium/frontends/mediafoundation/encode_hevc.cpp index 357287464d0..6eb966509ed 100644 --- a/src/gallium/frontends/mediafoundation/encode_hevc.cpp +++ b/src/gallium/frontends/mediafoundation/encode_hevc.cpp @@ -233,7 +233,7 @@ UpdateH265EncPictureDesc( pipe_h265_enc_picture_desc *pPicInfo, // internal function which contains the codec specific portion of PrepareForEncode HRESULT -CDX12EncHMFT::PrepareForEncodeHelper( LPDX12EncodeContext pDX12EncodeContext, bool dirtyRectFrameNumSet, uint32_t dirtyRectFrameNum ) +CDX12EncHMFT::PrepareForEncodeHelper( LPDX12EncodeContext pDX12EncodeContext, bool dirtyRectFrameNumSet, uint32_t dirtyRectFrameNum, bool moveRegionFrameNumSet, uint32_t moveRegionFrameNum ) { HRESULT hr = S_OK; pipe_h265_enc_picture_desc *pPicInfo = &pDX12EncodeContext->encoderPicInfo.h265enc; @@ -384,6 +384,65 @@ CDX12EncHMFT::PrepareForEncodeHelper( LPDX12EncodeContext pDX12EncodeContext, bo } } + // ---------------------------- + // Move Regions (motion hints) + // ---------------------------- + + // Always reset per-frame optional hint structures + memset( &pPicInfo->move_info, 0, sizeof( pPicInfo->move_info ) ); + + if( moveRegionFrameNumSet && m_EncoderCapabilities.m_HWSupportMoveRects.bits.max_motion_hints > 0 && + m_EncoderCapabilities.m_HWSupportMoveRects.bits.supports_precision_full_pixel && + pPicInfo->picture_type == PIPE_H2645_ENC_PICTURE_TYPE_P ) // P-frames only + { + uint32_t current_poc = pPicInfo->pic_order_cnt; + + // Validate frame number first + if( moveRegionFrameNum != current_poc ) + { + debug_printf( "[dx12 hmft 0x%p] MoveRegions frame mismatch (MRFN=%u, cur POC=%u), ignoring\n", + this, + moveRegionFrameNum, + current_poc ); + } + else + { + MOVEREGION_INFO *pMoveInfo = reinterpret_cast( m_pMoveRegionBlob.data() ); + uint32_t maxRects = m_EncoderCapabilities.m_HWSupportMoveRects.bits.max_motion_hints; + uint32_t numRects = std::min( pMoveInfo->NumMoveRegions, maxRects ); + + if( numRects > 0 ) + { + uint8_t surfaceIndex = pPicInfo->ref_list0[0]; + + if( surfaceIndex == PIPE_H2645_LIST_REF_INVALID_ENTRY ) + { + debug_printf( "[dx12 hmft 0x%p] MoveRegions: invalid L0 reference, ignoring\n", this ); + } + else + { + pPicInfo->move_info.input_mode = PIPE_ENC_MOVE_INFO_INPUT_MODE_RECTS; + pPicInfo->move_info.num_rects = numRects; + pPicInfo->move_info.dpb_reference_index = surfaceIndex; + pPicInfo->move_info.precision = PIPE_ENC_MOVE_INFO_PRECISION_UNIT_FULL_PIXEL; + + for( uint32_t i = 0; i < numRects; i++ ) + { + const MOVE_RECT &mr = pMoveInfo->MoveRegions[i]; + + pPicInfo->move_info.rects[i].source_point.x = mr.SourcePoint.x; + pPicInfo->move_info.rects[i].source_point.y = mr.SourcePoint.y; + + pPicInfo->move_info.rects[i].dest_rect.left = mr.DestRect.left; + pPicInfo->move_info.rects[i].dest_rect.top = mr.DestRect.top; + pPicInfo->move_info.rects[i].dest_rect.right = mr.DestRect.right; + pPicInfo->move_info.rects[i].dest_rect.bottom = mr.DestRect.bottom; + } + } + } + } + } + pPicInfo->gpu_stats_qp_map = pDX12EncodeContext->pPipeResourceQPMapStats; pPicInfo->gpu_stats_satd_map = pDX12EncodeContext->pPipeResourceSATDMapStats; pPicInfo->gpu_stats_rc_bitallocation_map = pDX12EncodeContext->pPipeResourceRCBitAllocMapStats; diff --git a/src/gallium/frontends/mediafoundation/hmft_entrypoints.h b/src/gallium/frontends/mediafoundation/hmft_entrypoints.h index 5c5100ef9c2..7110717cd86 100644 --- a/src/gallium/frontends/mediafoundation/hmft_entrypoints.h +++ b/src/gallium/frontends/mediafoundation/hmft_entrypoints.h @@ -841,10 +841,11 @@ class __declspec( uuid( HMFT_GUID ) ) CDX12EncHMFT : CMFD3DManager, bool m_bUnlocked = false; HRESULT IsUnlocked( void ); - HRESULT PrepareForEncodeHelper( LPDX12EncodeContext pDX12EncodeContext, bool dirtyRectFrameNumSet, uint32_t dirtyRectFrameNum ); + HRESULT PrepareForEncodeHelper( LPDX12EncodeContext pDX12EncodeContext, bool dirtyRectFrameNumSet, uint32_t dirtyRectFrameNum, bool moveRegionFrameNumSet, uint32_t moveRegionFrameNum ); HRESULT PrepareForEncode( IMFSample *pSample, LPDX12EncodeContext *ppDX12EncodeContext ); std::vector m_pDirtyRectBlob = std::vector( sizeof( DIRTYRECT_INFO ) ); + std::vector m_pMoveRegionBlob = std::vector( sizeof( MOVEREGION_INFO ) ); public: CDX12EncHMFT();