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 <yuboxie@microsoft.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/39515>
This commit is contained in:
Wenfeng Gao 2026-01-20 08:05:41 -08:00 committed by Marge Bot
parent afcead9158
commit 98f5fa618b
5 changed files with 146 additions and 5 deletions

View file

@ -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

View file

@ -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:

View file

@ -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<MOVEREGION_INFO *>( 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;

View file

@ -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<MOVEREGION_INFO *>( 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;

View file

@ -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<BYTE> m_pDirtyRectBlob = std::vector<BYTE>( sizeof( DIRTYRECT_INFO ) );
std::vector<BYTE> m_pMoveRegionBlob = std::vector<BYTE>( sizeof( MOVEREGION_INFO ) );
public:
CDX12EncHMFT();