mediafoundation: add support for GPU priority setting via IMFDXGIScheduler

Reviewed-by: Sil Vilerino <sivileri@microsoft.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/40895>
This commit is contained in:
Pohsiang (John) Hsu 2026-04-10 08:30:46 -07:00 committed by Marge Bot
parent 06c9c08c48
commit 2af4938328
5 changed files with 128 additions and 163 deletions

View file

@ -290,14 +290,6 @@ StringFromCodecAPI( const GUID *Api )
{
return "CODECAPI_AVEncVideoInputAbsoluteQPBlockSettings";
}
else if( *Api == CODECAPI_AVEncWorkGlobalPriority )
{
return "CODECAPI_AVEncWorkGlobalPriority";
}
else if( *Api == CODECAPI_AVEncWorkProcessPriority )
{
return "CODECAPI_AVEncWorkProcessPriority";
}
return "Unknown CodecAPI";
}
@ -430,15 +422,6 @@ CDX12EncHMFT::IsSupported( const GUID *Api )
}
}
if( ( *Api == CODECAPI_AVEncWorkGlobalPriority ) || ( *Api == CODECAPI_AVEncWorkProcessPriority ) )
{
if( m_EncoderCapabilities.m_bHWSupportsQueuePriorityManagement )
{
hr = S_OK;
return hr;
}
}
if( m_EncoderCapabilities.m_TwoPassSupport.bits.supports_1pass_recon_writing_skip )
{
if( *Api == CODECAPI_AVEncVideoRateControlFramePreAnalysisExternalReconDownscale )
@ -966,16 +949,6 @@ CDX12EncHMFT::GetValue( const GUID *Api, VARIANT *Value )
Value->vt = VT_UI4;
Value->ulVal = m_uiSliceGenerationMode;
}
else if( *Api == CODECAPI_AVEncWorkGlobalPriority )
{
Value->vt = VT_UI4;
Value->ulVal = (UINT32) m_WorkGlobalPriority;
}
else if( *Api == CODECAPI_AVEncWorkProcessPriority )
{
Value->vt = VT_UI4;
Value->ulVal = (UINT32) m_WorkProcessPriority;
}
else if( *Api == CODECAPI_AVEncVideoInputDeltaQPBlockSettings )
{
InputQPSettings hevcDeltaQPSettings;
@ -1731,36 +1704,6 @@ CDX12EncHMFT::SetValue( const GUID *Api, VARIANT *Value )
}
m_bVideoEnableSpatialAdaptiveQuantization = Value->ulVal ? TRUE : FALSE;
}
else if( *Api == CODECAPI_AVEncWorkProcessPriority )
{
debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncWorkProcessPriority - %u\n", this, Value->ulVal );
if( Value->vt != VT_UI4 )
{
CHECKHR_GOTO( E_INVALIDARG, done );
}
if( !m_EncoderCapabilities.m_bHWSupportsQueuePriorityManagement )
{
CHECKHR_GOTO( E_INVALIDARG, done );
}
m_WorkProcessPriority = (D3D12_COMMAND_QUEUE_PROCESS_PRIORITY) ( Value->ulVal );
m_bWorkProcessPrioritySet = TRUE;
}
else if( *Api == CODECAPI_AVEncWorkGlobalPriority )
{
debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncWorkGlobalPriority - %u\n", this, Value->ulVal );
if( Value->vt != VT_UI4 )
{
CHECKHR_GOTO( E_INVALIDARG, done );
}
if( !m_EncoderCapabilities.m_bHWSupportsQueuePriorityManagement )
{
CHECKHR_GOTO( E_INVALIDARG, done );
}
m_WorkGlobalPriority = (D3D12_COMMAND_QUEUE_GLOBAL_PRIORITY) Value->ulVal;
m_bWorkGlobalPrioritySet = TRUE;
}
else if( *Api == CODECAPI_AVEncVideoOutputQPMapBlockSize )
{
debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncVideoOutputQPMapBlockSize - %u\n", this, Value->ulVal );

View file

@ -522,39 +522,6 @@ CDX12EncHMFT::PrepareForEncode( IMFSample *pSample, LPDX12EncodeContext *ppDX12E
pDX12EncodeContext->pVlScreen = m_pVlScreen; // weakref
//
// Update the encoder priorities (if any set)
//
#if 0 // For testing priorities
{
m_bWorkProcessPrioritySet = ((pipeEncoderInputFenceHandleValue % 2) == 0) ? TRUE : FALSE;
m_WorkGlobalPriority = static_cast<D3D12_COMMAND_QUEUE_GLOBAL_PRIORITY>((pipeEncoderInputFenceHandleValue % 14) + 18); // 18-31 range (no hard realtime privileges)
m_bWorkGlobalPrioritySet = ((pipeEncoderInputFenceHandleValue % 2) == 0) ? TRUE : FALSE;
m_WorkProcessPriority = static_cast<D3D12_COMMAND_QUEUE_PROCESS_PRIORITY>(pipeEncoderInputFenceHandleValue % 2); // 0-1 range
}
#endif // For testing priorities
if( m_bWorkProcessPrioritySet || m_bWorkGlobalPrioritySet )
{
mtx_lock( &m_ContextPriorityMgr.m_lock );
int result = 0;
for( ID3D12CommandQueue *queue : m_ContextPriorityMgr.m_registeredQueues )
{
result = m_ContextPriorityMgr.base.set_queue_priority( &m_ContextPriorityMgr.base,
queue,
reinterpret_cast<uint32_t *>( &m_WorkGlobalPriority ),
reinterpret_cast<uint32_t *>( &m_WorkProcessPriority ) );
}
mtx_unlock( &m_ContextPriorityMgr.m_lock );
CHECKBOOL_GOTO( result == 0, MF_E_UNEXPECTED, done );
// Once set in the underlying pipe context, don't set them again
// until they're modified by the CodecAPI SetValue function.
m_bWorkProcessPrioritySet = FALSE;
m_bWorkGlobalPrioritySet = FALSE;
}
// Call the helper for encoder specific work
pDX12EncodeContext->encoderPicInfo.base.in_fence = pPipeEncoderInputFenceHandle;
pDX12EncodeContext->encoderPicInfo.base.in_fence_value = pipeEncoderInputFenceHandleValue;

View file

@ -439,46 +439,6 @@ DEFINE_CODECAPI_GUID( AVEncVideoRateControlFramePreAnalysisExternalReconDownscal
DEFINE_CODECAPI_GUIDNAMED( AVEncVideoRateControlFramePreAnalysisExternalReconDownscale )
#endif
#ifndef CODECAPI_AVEncWorkGlobalPriority
// AVEncWorkGlobalPriority (VT_UI4) (Experimental, Testing only)
// Indicates global priority for all work submitted by the encoder to the GPU
// VARIANT_FALSE: disable; VARIANT_TRUE: enable
DEFINE_CODECAPI_GUID( AVEncWorkGlobalPriority,
"CA123CAA-A17B-4BBA-9E08-269F3CF5D636",
0xca123caa,
0xa17b,
0x4bba,
0x9e,
0x8,
0x26,
0x9f,
0x3c,
0xf5,
0xd6,
0x36 )
#define CODECAPI_AVEncWorkGlobalPriority DEFINE_CODECAPI_GUIDNAMED( AVEncWorkGlobalPriority )
#endif
#ifndef CODECAPI_AVEncWorkProcessPriority
// AVEncWorkProcessPriority (VT_UI4) (Experimental, Testing only)
// Indicates global priority for all work submitted by the encoder to the GPU
// VARIANT_FALSE: disable; VARIANT_TRUE: enable
DEFINE_CODECAPI_GUID( AVEncWorkProcessPriority,
"FB123CAA-B778-4BBA-9E08-269F3CF5D125",
0xfb123caa,
0xb778,
0x4bba,
0x9e,
0x8,
0x26,
0x9f,
0x3c,
0xf5,
0xd1,
0x25 )
#define CODECAPI_AVEncWorkProcessPriority DEFINE_CODECAPI_GUIDNAMED( AVEncWorkProcessPriority )
#endif
#if MFT_CODEC_H264ENC
#define HMFT_GUID "8994db7c-288a-4c62-a136-a3c3c2a208a8"
#elif MFT_CODEC_H265ENC
@ -720,11 +680,6 @@ class __declspec( uuid( HMFT_GUID ) ) CDX12EncHMFT : CMFD3DManager,
BOOL m_bRateControlFramePreAnalysis = FALSE;
BOOL m_bRateControlFramePreAnalysisExternalReconDownscale = FALSE;
D3D12_COMMAND_QUEUE_PROCESS_PRIORITY m_WorkProcessPriority = {};
BOOL m_bWorkProcessPrioritySet = FALSE;
D3D12_COMMAND_QUEUE_GLOBAL_PRIORITY m_WorkGlobalPriority = {};
BOOL m_bWorkGlobalPrioritySet = FALSE;
UINT m_uiMaxOutputBitstreamSize = 0u;
struct pipe_video_codec *m_pPipeVideoCodec = nullptr;
struct pipe_video_codec *m_pPipeVideoBlitter = nullptr;

View file

@ -92,6 +92,11 @@ CMFD3DManager::Shutdown( bool bReleaseDeviceManager )
m_spReconstructedPictureBufferPool.Reset();
}
// Release scheduler registrations before closing the device handle
m_ContextPriorityMgr.m_registeredQueues.clear();
m_ContextPriorityMgr.m_spSchedulerClient.Reset();
m_ContextPriorityMgr.m_hDevice = NULL;
if( m_spDeviceManager != nullptr )
{
if( m_hDevice != NULL )
@ -277,7 +282,7 @@ CMFD3DManager::UpdateGPUFeatureFlags()
m_deviceDriverVersion.part4 >= 9002 )
{
m_gpuFeatureFlags.m_bH264SendUnwrappedPOC = true;
MFE_INFO( "[dx12 hmft 0x%p] D3DManager: GPUFeature m_bH264SendUnwrappedPOC is set to true\n", m_logId );
MFE_INFO( "[dx12 hmft 0x%p] D3DManager: GPUFeature m_bH264SendUnwrappedPOC is set to true", m_logId );
}
}
*/
@ -287,27 +292,38 @@ int
MFTRegisterWorkQueue( struct d3d12_context_queue_priority_manager *manager, ID3D12CommandQueue *queue )
{
mft_context_queue_priority_manager *mft_mgr = (mft_context_queue_priority_manager *) manager;
mtx_lock( &mft_mgr->m_lock );
ComPtr<IUnknown> queue_unknown;
if( !queue ||
FAILED( queue->QueryInterface( IID_PPV_ARGS( &queue_unknown ) ) ) )
if( !queue )
{
mtx_unlock( &mft_mgr->m_lock );
return -1;
}
// Only register the queue if not already registered
auto it = std::find( mft_mgr->m_registeredQueues.begin(), mft_mgr->m_registeredQueues.end(), queue );
if( it == mft_mgr->m_registeredQueues.end() )
mtx_lock( &mft_mgr->m_lock );
if( mft_mgr->m_spSchedulerClient )
{
//
// Register the queue_unknown with the MFT.
//
if( mft_mgr->m_registeredQueues.find( queue ) == mft_mgr->m_registeredQueues.end() )
{
HRESULT hr = S_OK;
ComPtr<IMFDXGISchedulerRegistration> spRegistration;
mft_mgr->m_registeredQueues.push_back( queue );
hr = mft_mgr->m_spSchedulerClient->RegisterObject( mft_mgr->m_hDevice, queue, &spRegistration );
if( FAILED( hr ) )
{
MFE_ERROR( "[dx12 hmft 0x%p] D3DManager: RegisterObject failed for Queue 0x%p", mft_mgr->m_logId, queue );
mtx_unlock( &mft_mgr->m_lock );
return -1;
}
MFE_INFO( "[dx12 hmft 0x%p] D3DManager: RegisterObject succeeded for Queue 0x%p, spRegistration 0x%p",
mft_mgr->m_logId,
queue,
spRegistration.Get() );
mft_mgr->m_registeredQueues[queue] = spRegistration;
}
}
else
{
MFE_INFO( "[dx12 hmft 0x%p] D3DManager: m_spSchedulerClient is not supported -> no opt", mft_mgr->m_logId );
}
mtx_unlock( &mft_mgr->m_lock );
return 0;
}
@ -316,24 +332,47 @@ int
MFTUnregisterWorkQueue( struct d3d12_context_queue_priority_manager *manager, ID3D12CommandQueue *queue )
{
mft_context_queue_priority_manager *mft_mgr = (mft_context_queue_priority_manager *) manager;
mtx_lock( &mft_mgr->m_lock );
ComPtr<IUnknown> queue_unknown;
if( !queue ||
FAILED( queue->QueryInterface( IID_PPV_ARGS( &queue_unknown ) ) ) )
if( !queue )
{
mtx_unlock( &mft_mgr->m_lock );
return -1;
}
//
// Unregister the queue_unknown with the MFT.
//
mtx_lock( &mft_mgr->m_lock );
if( mft_mgr->m_spSchedulerClient )
{
auto item = mft_mgr->m_registeredQueues.find( queue );
if( item != mft_mgr->m_registeredQueues.end() )
{
MFE_INFO( "[dx12 hmft 0x%p] D3DManager: Releases spRegistration for Queue 0x%p, spRegistration 0x%p",
mft_mgr->m_logId,
queue,
item->second.Get() );
auto it = std::find( mft_mgr->m_registeredQueues.begin(), mft_mgr->m_registeredQueues.end(), queue );
if( it != mft_mgr->m_registeredQueues.end() )
mft_mgr->m_registeredQueues.erase( it );
#if MESA_DEBUG
{
uint32_t global_priority, local_priority;
mft_mgr->base.get_queue_priority( &mft_mgr->base, queue, &global_priority, &local_priority );
debug_printf( "[dx12 hmft 0x%p] D3DManager: ending queue = 0x%p, local_priority = %d, global_priority = %d\n",
mft_mgr->m_logId,
queue,
local_priority,
global_priority );
}
#endif
mft_mgr->m_registeredQueues.erase( item );
}
else
{
MFE_WARNING( "[dx12 hmft 0x%p] D3DManager: Queue 0x%p is unexpectedly not found in m_registeredQueues",
mft_mgr->m_logId,
queue );
}
}
else
{
MFE_INFO( "[dx12 hmft 0x%p] D3DManager: m_spSchedulerClient is not supported -> no opt", mft_mgr->m_logId );
}
mtx_unlock( &mft_mgr->m_lock );
return 0;
}
@ -372,9 +411,18 @@ CMFD3DManager::xOnSetD3DManager( ULONG_PTR ulParam )
{
CHECKBOOL_GOTO( thrd_success == mtx_init( &m_ContextPriorityMgr.m_lock, mtx_plain ), MF_E_DXGI_DEVICE_NOT_INITIALIZED, done );
m_ContextPriorityMgr.m_logId = m_logId;
m_ContextPriorityMgr.base.register_work_queue = MFTRegisterWorkQueue;
m_ContextPriorityMgr.base.unregister_work_queue = MFTUnregisterWorkQueue;
// Query for scheduler client to register driver work queues with the MF scheduler
ComPtr<IMFDXGISchedulerClient> spScheduler;
if( SUCCEEDED( m_spDeviceManager->GetVideoService( m_hDevice, IID_PPV_ARGS( &spScheduler ) ) ) )
{
m_ContextPriorityMgr.m_spSchedulerClient = spScheduler;
m_ContextPriorityMgr.m_hDevice = m_hDevice;
}
CHECKBOOL_GOTO( m_ScreenInteropInfo.set_context_queue_priority_manager( m_pPipeContext, &m_ContextPriorityMgr.base ) == 0,
MF_E_DXGI_DEVICE_NOT_INITIALIZED,
done );

View file

@ -45,6 +45,7 @@
#include <c11/threads.h>
#include <wrl/client.h>
#include <wrl/implements.h>
#include <unordered_map>
#include "macros.h"
@ -53,6 +54,54 @@
// Use the Windows SDK dxcore include (e.g directx/dxcore uses DirectX-Headers)
#include <dxcore.h>
// Forward declare experimental MF DXGI Scheduler interfaces (not yet in all SDK versions)
#ifndef __IMFDXGISchedulerRegistration_INTERFACE_DEFINED__
#define __IMFDXGISchedulerRegistration_INTERFACE_DEFINED__
typedef enum MF_DXGI_SCHEDULING_PRIORITY
{
MF_DXGI_SCHEDULING_PRIORITY_IDLE = 0,
MF_DXGI_SCHEDULING_PRIORITY_DEFAULT = 0x1,
MF_DXGI_SCHEDULING_PRIORITY_HIGH = 0x2
} MF_DXGI_SCHEDULING_PRIORITY;
/* interface IMFDXGISchedulerRegistration */
/* [uuid][local][object] */
EXTERN_C const IID IID_IMFDXGISchedulerRegistration;
MIDL_INTERFACE( "396ACD9A-C9EF-41e5-8009-6735C0528875" )
IMFDXGISchedulerRegistration : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE GetPriority(
/* [annotation][out] */
_Out_ MF_DXGI_SCHEDULING_PRIORITY * priority ) = 0;
virtual HRESULT STDMETHODCALLTYPE SetPriority(
/* [in] */ MF_DXGI_SCHEDULING_PRIORITY priority ) = 0;
};
/* interface IMFDXGISchedulerClient */
/* [uuid][local][object] */
EXTERN_C const IID IID_IMFDXGISchedulerClient;
MIDL_INTERFACE( "DF681668-70EC-457B-9AA3-CAA60910A710" )
IMFDXGISchedulerClient : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE RegisterObject(
/* [annotation][in] */
_In_ HANDLE hDevice,
/* [annotation][in] */
_In_ IUnknown * pObject,
/* [annotation][out] */
_COM_Outptr_ IMFDXGISchedulerRegistration * *ppRegistration ) = 0;
};
#endif
// end: Forward declare experimental MF DXGI Scheduler interfaces (not yet in all SDK versions)
using namespace Microsoft::WRL;
using Microsoft::WRL::ComPtr;
using namespace std;
@ -73,8 +122,11 @@ typedef union
struct mft_context_queue_priority_manager
{
struct d3d12_context_queue_priority_manager base;
std::vector<ID3D12CommandQueue *> m_registeredQueues;
std::unordered_map<ID3D12CommandQueue *, ComPtr<IMFDXGISchedulerRegistration>> m_registeredQueues;
ComPtr<IMFDXGISchedulerClient> m_spSchedulerClient;
HANDLE m_hDevice = NULL;
mtx_t m_lock;
const void *m_logId = {};
};
class CMFD3DManager