mediafoundation: add stats resource pool so we can use pool for QP map as well

Reviewed-by: Yubo Xie <yuboxie@microsoft.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/37982>
This commit is contained in:
Pohsiang (John) Hsu
2025-10-14 21:32:18 -07:00
parent daa3ed7fea
commit 932401ddf0
9 changed files with 620 additions and 127 deletions
@@ -45,8 +45,6 @@ typedef class DX12EncodeContext
pipe_resource *pPipeResourceSATDMapStats = nullptr;
pipe_resource *pPipeResourceRCBitAllocMapStats = nullptr;
pipe_resource *pPipeResourcePSNRStats = nullptr;
BOOL bUseSATDMapAllocator = FALSE;
BOOL bUseBitsusedMapAllocator = FALSE;
// Keep all the media and sync objects until encode is done
// and then signal EnqueueResourceRelease so the media
@@ -206,29 +204,6 @@ typedef class DX12EncodeContext
if( ( ( slice_idx == 0 ) || pOutputBitRes[slice_idx] != pOutputBitRes[slice_idx - 1] ) && pOutputBitRes[slice_idx] )
pVlScreen->pscreen->resource_destroy( pVlScreen->pscreen, pOutputBitRes[slice_idx] );
if( pPipeResourceQPMapStats )
pVlScreen->pscreen->resource_destroy( pVlScreen->pscreen, pPipeResourceQPMapStats );
if( bUseSATDMapAllocator )
{
pPipeResourceSATDMapStats = nullptr;
}
else
{
if( pPipeResourceSATDMapStats )
pVlScreen->pscreen->resource_destroy( pVlScreen->pscreen, pPipeResourceSATDMapStats );
}
if( bUseBitsusedMapAllocator )
{
pPipeResourceRCBitAllocMapStats = nullptr;
}
else
{
if( pPipeResourceRCBitAllocMapStats )
pVlScreen->pscreen->resource_destroy( pVlScreen->pscreen, pPipeResourceRCBitAllocMapStats );
}
if( pPipeVideoBuffer )
pPipeVideoBuffer->destroy( pPipeVideoBuffer );
if( pDownscaledTwoPassPipeVideoBuffer )
@@ -373,61 +373,75 @@ CDX12EncHMFT::PrepareForEncode( IMFSample *pSample, LPDX12EncodeContext *ppDX12E
templ.depth0 = 1;
templ.array_size = 1;
if( m_EncoderCapabilities.m_HWSupportStatsQPMapOutput.bits.supported && m_uiVideoOutputQPMapBlockSize > 0 )
{
uint32_t block_size = ( 1 << m_EncoderCapabilities.m_HWSupportStatsQPMapOutput.bits.log2_values_block_size );
templ.format = (enum pipe_format) m_EncoderCapabilities.m_HWSupportStatsQPMapOutput.bits.pipe_pixel_format;
templ.width0 = static_cast<uint32_t>( std::ceil( m_uiOutputWidth / static_cast<float>( block_size ) ) );
templ.height0 = static_cast<uint16_t>( std::ceil( m_uiOutputHeight / static_cast<float>( block_size ) ) );
CHECKNULL_GOTO(
pDX12EncodeContext->pPipeResourceQPMapStats = m_pVlScreen->pscreen->resource_create( m_pVlScreen->pscreen, &templ ),
E_OUTOFMEMORY,
done );
}
if( m_EncoderCapabilities.m_HWSupportStatsSATDMapOutput.bits.supported && m_uiVideoSatdMapBlockSize > 0 )
{
uint32_t block_size = ( 1 << m_EncoderCapabilities.m_HWSupportStatsSATDMapOutput.bits.log2_values_block_size );
templ.format = (enum pipe_format) m_EncoderCapabilities.m_HWSupportStatsSATDMapOutput.bits.pipe_pixel_format;
templ.width0 = static_cast<uint32_t>( std::ceil( m_uiOutputWidth / static_cast<float>( block_size ) ) );
templ.height0 = static_cast<uint16_t>( std::ceil( m_uiOutputHeight / static_cast<float>( block_size ) ) );
pDX12EncodeContext->bUseSATDMapAllocator = m_bUseSATDMapAllocator;
if( m_bUseSATDMapAllocator )
if( !m_spSatdStatsBufferPool )
{
pDX12EncodeContext->pPipeResourceSATDMapStats =
AllocatePipeResourceFromAllocator( m_spSATDMapAllocator.Get(), m_pVlScreen->pscreen, &templ );
CHECKNULL_GOTO( pDX12EncodeContext->pPipeResourceSATDMapStats, E_OUTOFMEMORY, done );
}
else
{
CHECKNULL_GOTO( pDX12EncodeContext->pPipeResourceSATDMapStats =
m_pVlScreen->pscreen->resource_create( m_pVlScreen->pscreen, &templ ),
E_OUTOFMEMORY,
done );
uint32_t block_size = ( 1 << m_EncoderCapabilities.m_HWSupportStatsSATDMapOutput.bits.log2_values_block_size );
pipe_format format = (enum pipe_format) m_EncoderCapabilities.m_HWSupportStatsSATDMapOutput.bits.pipe_pixel_format;
uint32_t width0 = static_cast<uint32_t>( std::ceil( m_uiOutputWidth / static_cast<float>( block_size ) ) );
uint16_t height0 = static_cast<uint16_t>( std::ceil( m_uiOutputHeight / static_cast<float>( block_size ) ) );
CHECKHR_GOTO( stats_buffer_manager::Create( m_pVlScreen,
m_pPipeContext,
MFSampleExtension_VideoEncodeSatdMap,
width0,
height0,
format,
( m_bLowLatency ? 1 : MFT_INPUT_QUEUE_DEPTH ),
m_spSatdStatsBufferPool.GetAddressOf() ),
done );
}
pDX12EncodeContext->pPipeResourceSATDMapStats = m_spSatdStatsBufferPool->get_new_tracked_buffer();
CHECKNULL_GOTO( pDX12EncodeContext->pPipeResourceSATDMapStats, E_OUTOFMEMORY, done );
}
if( m_EncoderCapabilities.m_HWSupportStatsRCBitAllocationMapOutput.bits.supported && m_uiVideoOutputBitsUsedMapBlockSize > 0 )
{
uint32_t block_size = ( 1 << m_EncoderCapabilities.m_HWSupportStatsRCBitAllocationMapOutput.bits.log2_values_block_size );
templ.format = (enum pipe_format) m_EncoderCapabilities.m_HWSupportStatsRCBitAllocationMapOutput.bits.pipe_pixel_format;
templ.width0 = static_cast<uint32_t>( std::ceil( m_uiOutputWidth / static_cast<float>( block_size ) ) );
templ.height0 = static_cast<uint16_t>( std::ceil( m_uiOutputHeight / static_cast<float>( block_size ) ) );
if( !m_spBitsUsedStatsBufferPool )
{
uint32_t block_size =
( 1 << m_EncoderCapabilities.m_HWSupportStatsRCBitAllocationMapOutput.bits.log2_values_block_size );
pipe_format format =
(enum pipe_format) m_EncoderCapabilities.m_HWSupportStatsRCBitAllocationMapOutput.bits.pipe_pixel_format;
uint32_t width0 = static_cast<uint32_t>( std::ceil( m_uiOutputWidth / static_cast<float>( block_size ) ) );
uint16_t height0 = static_cast<uint16_t>( std::ceil( m_uiOutputHeight / static_cast<float>( block_size ) ) );
pDX12EncodeContext->bUseBitsusedMapAllocator = m_bUseBitsusedMapAllocator;
if( m_bUseBitsusedMapAllocator )
{
pDX12EncodeContext->pPipeResourceRCBitAllocMapStats =
AllocatePipeResourceFromAllocator( m_spBitsusedMapAllocator.Get(), m_pVlScreen->pscreen, &templ );
CHECKNULL_GOTO( pDX12EncodeContext->pPipeResourceRCBitAllocMapStats, E_OUTOFMEMORY, done );
CHECKHR_GOTO( stats_buffer_manager::Create( m_pVlScreen,
m_pPipeContext,
MFSampleExtension_VideoEncodeBitsUsedMap,
width0,
height0,
format,
( m_bLowLatency ? 1 : MFT_INPUT_QUEUE_DEPTH ),
m_spBitsUsedStatsBufferPool.GetAddressOf() ),
done );
}
else
pDX12EncodeContext->pPipeResourceRCBitAllocMapStats = m_spBitsUsedStatsBufferPool->get_new_tracked_buffer();
CHECKNULL_GOTO( pDX12EncodeContext->pPipeResourceRCBitAllocMapStats, E_OUTOFMEMORY, done );
}
if( m_EncoderCapabilities.m_HWSupportStatsQPMapOutput.bits.supported && m_uiVideoOutputQPMapBlockSize > 0 )
{
if( !m_spQPMapStatsBufferPool )
{
CHECKNULL_GOTO( pDX12EncodeContext->pPipeResourceRCBitAllocMapStats =
m_pVlScreen->pscreen->resource_create( m_pVlScreen->pscreen, &templ ),
E_OUTOFMEMORY,
done );
uint32_t block_size = ( 1 << m_EncoderCapabilities.m_HWSupportStatsQPMapOutput.bits.log2_values_block_size );
pipe_format format = (enum pipe_format) m_EncoderCapabilities.m_HWSupportStatsQPMapOutput.bits.pipe_pixel_format;
uint32_t width0 = static_cast<uint32_t>( std::ceil( m_uiOutputWidth / static_cast<float>( block_size ) ) );
uint16_t height0 = static_cast<uint16_t>( std::ceil( m_uiOutputHeight / static_cast<float>( block_size ) ) );
CHECKHR_GOTO( stats_buffer_manager::Create( m_pVlScreen,
m_pPipeContext,
MFSampleExtension_VideoEncodeQPMap,
width0,
height0,
format,
( m_bLowLatency ? 1 : MFT_INPUT_QUEUE_DEPTH ),
m_spQPMapStatsBufferPool.GetAddressOf() ),
done );
}
pDX12EncodeContext->pPipeResourceQPMapStats = m_spQPMapStatsBufferPool->get_new_tracked_buffer();
CHECKNULL_GOTO( pDX12EncodeContext->pPipeResourceQPMapStats, E_OUTOFMEMORY, done );
}
if( m_EncoderCapabilities.m_PSNRStatsSupport.bits.supports_y_channel && m_bVideoEnableFramePsnrYuv )
@@ -66,6 +66,7 @@ mediafoundation_files = files(
'reference_frames_tracker_av1.cpp',
'reference_frames_tracker_h264.cpp',
'reference_frames_tracker_hevc.cpp',
'stats_buffer_manager.cpp',
'videobufferlock.cpp',
'wpptrace.cpp',
)
@@ -72,16 +72,19 @@ CMFD3DManager::Shutdown( bool bReleaseDeviceManager )
m_spVideoSampleAllocator = nullptr;
}
if( m_spSATDMapAllocator )
if( m_spSatdStatsBufferPool )
{
m_spSATDMapAllocator->UninitializeSampleAllocator();
m_spSATDMapAllocator = nullptr;
m_spSatdStatsBufferPool.Reset();
}
if( m_spBitsusedMapAllocator )
if( m_spBitsUsedStatsBufferPool )
{
m_spBitsusedMapAllocator->UninitializeSampleAllocator();
m_spBitsusedMapAllocator = nullptr;
m_spBitsUsedStatsBufferPool.Reset();
}
if( m_spQPMapStatsBufferPool )
{
m_spQPMapStatsBufferPool.Reset();
}
if( m_spDeviceManager != nullptr )
@@ -368,9 +371,6 @@ CMFD3DManager::xOnSetD3DManager( ULONG_PTR ulParam )
CHECKNULL_GOTO( m_ContextPriorityMgr.base.get_queue_priority, MF_E_DXGI_DEVICE_NOT_INITIALIZED, done );
}
CHECKHR_GOTO( MFCreateVideoSampleAllocatorEx( IID_PPV_ARGS( &m_spSATDMapAllocator ) ), done );
CHECKHR_GOTO( MFCreateVideoSampleAllocatorEx( IID_PPV_ARGS( &m_spBitsusedMapAllocator ) ), done );
CHECKHR_GOTO( GetDeviceInfo(), done );
UpdateGPUFeatureFlags();
@@ -48,6 +48,8 @@
#include "macros.h"
#include "stats_buffer_manager.h"
// Use the Windows SDK dxcore include (e.g directx/dxcore uses DirectX-Headers)
#include <dxcore.h>
@@ -98,10 +100,11 @@ class CMFD3DManager
ComPtr<ID3D12VideoDevice> m_spVideoDevice;
ComPtr<ID3D12CommandQueue> m_spStagingQueue;
ComPtr<IMFVideoSampleAllocatorEx> m_spVideoSampleAllocator; // Used for software input samples that need to be copied
ComPtr<IMFVideoSampleAllocatorEx> m_spSATDMapAllocator;
ComPtr<IMFVideoSampleAllocatorEx> m_spBitsusedMapAllocator;
BOOL m_bUseSATDMapAllocator = FALSE;
BOOL m_bUseBitsusedMapAllocator = FALSE;
ComPtr<stats_buffer_manager> m_spSatdStatsBufferPool;
ComPtr<stats_buffer_manager> m_spBitsUsedStatsBufferPool;
ComPtr<stats_buffer_manager> m_spQPMapStatsBufferPool;
UINT32 m_uiResetToken = 0;
HANDLE m_hDevice = NULL;
struct vl_screen *m_pVlScreen = nullptr;
@@ -565,15 +565,9 @@ CDX12EncHMFT::OnOutputTypeChanged()
if( bResolutionChange )
{
ConfigureMapSampleAllocatorHelper( m_spSATDMapAllocator,
m_EncoderCapabilities.m_HWSupportStatsSATDMapOutput,
m_uiVideoSatdMapBlockSize,
m_bUseSATDMapAllocator );
ConfigureMapSampleAllocatorHelper( m_spBitsusedMapAllocator,
m_EncoderCapabilities.m_HWSupportStatsRCBitAllocationMapOutput,
m_uiVideoOutputBitsUsedMapBlockSize,
m_bUseBitsusedMapAllocator );
m_spSatdStatsBufferPool.Reset();
m_spBitsUsedStatsBufferPool.Reset();
m_spQPMapStatsBufferPool.Reset();
}
// Indicate that we'll be adding MF_NALU_LENGTH_INFORMATION on each output sample that comes
@@ -1469,48 +1463,48 @@ CDX12EncHMFT::xThreadProc( void *pCtx )
}
}
// Conditionally attach output QP map
if( pThis->m_uiVideoOutputQPMapBlockSize && pDX12EncodeContext->pPipeResourceQPMapStats != nullptr )
// Conditionally attach output QP map (d3d12resource), tracking will be added to the d3d12resource and when the app
// releases the MF sample, the d3d12resource will be returned back to the pool
if( pThis->m_uiVideoOutputQPMapBlockSize && pDX12EncodeContext->pPipeResourceQPMapStats != nullptr &&
pThis->m_spQPMapStatsBufferPool )
{
HRESULT hr = MFAttachPipeResourceAsSampleExtension( pThis->m_pPipeContext,
pDX12EncodeContext->pPipeResourceQPMapStats,
pDX12EncodeContext->pSyncObjectQueue,
MFSampleExtension_VideoEncodeQPMap,
spOutputSample.Get() );
HRESULT hr = pThis->m_spQPMapStatsBufferPool->AttachPipeResourceAsSampleExtension(
pDX12EncodeContext->pPipeResourceQPMapStats,
pDX12EncodeContext->pSyncObjectQueue,
spOutputSample.Get() );
if( FAILED( hr ) )
{
MFE_INFO( "[dx12 hmft 0x%p] QPMap: MFAttachPipeResourceAsSampleExtension failed - hr=0x%08x", pThis, hr );
MFE_INFO( "[dx12 hmft 0x%p] QPMap: AttachPipeResourceAsSampleExtension failed - hr=0x%08x", pThis, hr );
}
}
// Conditionally attach output bits used map
if( pThis->m_uiVideoOutputBitsUsedMapBlockSize && pDX12EncodeContext->pPipeResourceRCBitAllocMapStats != nullptr )
// Conditionally attach output bits used map (d3d12resource), tracking will be added to the d3d12resource and when
// the app releases the MF sample, the d3d12resource will be returned back to the pool
if( pThis->m_uiVideoOutputBitsUsedMapBlockSize && pDX12EncodeContext->pPipeResourceRCBitAllocMapStats != nullptr &&
pThis->m_spBitsUsedStatsBufferPool )
{
HRESULT hr = MFAttachPipeResourceAsSampleExtension( pThis->m_pPipeContext,
pDX12EncodeContext->pPipeResourceRCBitAllocMapStats,
pDX12EncodeContext->pSyncObjectQueue,
MFSampleExtension_VideoEncodeBitsUsedMap,
spOutputSample.Get() );
HRESULT hr = pThis->m_spBitsUsedStatsBufferPool->AttachPipeResourceAsSampleExtension(
pDX12EncodeContext->pPipeResourceRCBitAllocMapStats,
pDX12EncodeContext->pSyncObjectQueue,
spOutputSample.Get() );
if( FAILED( hr ) )
{
MFE_INFO( "[dx12 hmft 0x%p] BitsUsed: MFAttachPipeResourceAsSampleExtension failed - hr=0x%08x", pThis, hr );
MFE_INFO( "[dx12 hmft 0x%p] BitsUsed: AttachPipeResourceAsSampleExtension failed - hr=0x%08x", pThis, hr );
}
}
// Conditionally attach SATD map
if( pThis->m_uiVideoSatdMapBlockSize && pDX12EncodeContext->pPipeResourceSATDMapStats != nullptr )
// Conditionally attach SATD map (d3d12resource), tracking will be added to the d3d12resource and when the app
// releases the MF sample, the d3d12resource will be returned back to the pool
if( pThis->m_uiVideoSatdMapBlockSize && pDX12EncodeContext->pPipeResourceSATDMapStats != nullptr &&
pThis->m_spSatdStatsBufferPool )
{
HRESULT hr = MFAttachPipeResourceAsSampleExtension( pThis->m_pPipeContext,
pDX12EncodeContext->pPipeResourceSATDMapStats,
pDX12EncodeContext->pSyncObjectQueue,
MFSampleExtension_VideoEncodeSatdMap,
spOutputSample.Get() );
HRESULT hr = pThis->m_spSatdStatsBufferPool->AttachPipeResourceAsSampleExtension(
pDX12EncodeContext->pPipeResourceSATDMapStats,
pDX12EncodeContext->pSyncObjectQueue,
spOutputSample.Get() );
if( FAILED( hr ) )
{
MFE_INFO( "[dx12 hmft 0x%p] SATDMap: MFAttachPipeResourceAsSampleExtension failed - hr=0x%08x", pThis, hr );
MFE_INFO( "[dx12 hmft 0x%p] SATDMap: AttachPipeResourceAsSampleExtension failed - hr=0x%08x", pThis, hr );
}
}
@@ -2085,16 +2079,6 @@ CDX12EncHMFT::ProcessMessage( MFT_MESSAGE_TYPE eMessage, ULONG_PTR ulParam )
{
m_EncoderCapabilities.initialize( m_pPipeContext->screen, m_outputPipeProfile );
}
ConfigureMapSampleAllocatorHelper( m_spSATDMapAllocator,
m_EncoderCapabilities.m_HWSupportStatsSATDMapOutput,
m_uiVideoSatdMapBlockSize,
m_bUseSATDMapAllocator );
ConfigureMapSampleAllocatorHelper( m_spBitsusedMapAllocator,
m_EncoderCapabilities.m_HWSupportStatsRCBitAllocationMapOutput,
m_uiVideoOutputBitsUsedMapBlockSize,
m_bUseBitsusedMapAllocator );
break;
}
}
@@ -0,0 +1,107 @@
#ifndef __STATICASYNCCALLBACK__
#define __STATICASYNCCALLBACK__
////////////////////////////////////////////////////////
//
#define STATICASYNCCALLBACK( Callback, Parent ) \
class Callback##AsyncCallback; \
friend class Callback##AsyncCallback; \
class Callback##AsyncCallback : public IMFAsyncCallback \
{ \
public: \
STDMETHOD_( ULONG, AddRef )() \
{ \
Parent *pThis = ( (Parent *) ( (BYTE *) this - offsetof( Parent, m_x##Callback ) ) ); \
return pThis->AddRef(); \
} \
STDMETHOD_( ULONG, Release )() \
{ \
Parent *pThis = ( (Parent *) ( (BYTE *) this - offsetof( Parent, m_x##Callback ) ) ); \
return pThis->Release(); \
} \
STDMETHOD( QueryInterface )( REFIID riid, __RPC__deref_out _Result_nullonfailure_ void **ppvObject ) \
{ \
return E_NOINTERFACE; \
} \
STDMETHOD( GetParameters )( __RPC__out DWORD * pdwFlags, __RPC__out DWORD *pdwQueue ) \
{ \
return S_OK; \
} \
STDMETHOD( Invoke )( __RPC__in_opt IMFAsyncResult * pResult ) \
{ \
Callback( pResult ); \
return S_OK; \
} \
} m_x##Callback;
////////////////////////////////////////////////////////
//
//
// We need to support QI interface so that DCOM is happy when it tries to marshal the interface pointer
//
#define METHODASYNCCALLBACKEX( Callback, Parent, Flag, Queue ) \
class Callback##AsyncCallback; \
friend class Callback##AsyncCallback; \
class Callback##AsyncCallback : public IMFAsyncCallback \
{ \
public: \
STDMETHOD_( ULONG, AddRef )() \
{ \
return GetParent()->AddRef(); \
} \
STDMETHOD_( ULONG, Release )() \
{ \
return GetParent()->Release(); \
} \
STDMETHOD( QueryInterface )( REFIID riid, __RPC__deref_out _Result_nullonfailure_ void **ppvObject ) \
{ \
if( riid == IID_IMFAsyncCallback || riid == IID_IUnknown ) \
{ \
( *ppvObject ) = this; \
AddRef(); \
return S_OK; \
} \
( *ppvObject ) = NULL; \
return E_NOINTERFACE; \
} \
STDMETHOD( GetParameters )( __RPC__out DWORD * pdwFlags, __RPC__out DWORD *pdwQueue ) \
{ \
*pdwFlags = Flag; \
*pdwQueue = Queue; \
return S_OK; \
} \
STDMETHOD( Invoke )( __RPC__in_opt IMFAsyncResult * pResult ) \
{ \
GetParent()->Callback( pResult ); \
return S_OK; \
} \
\
protected: \
Parent *GetParent() \
{ \
return ( (Parent *) ( (BYTE *) this - offsetof( Parent, m_x##Callback ) ) ); \
} \
} m_x##Callback;
////////////////////////////////////////////////////////
//
#define METHODFASTCALLBACK( Callback, Parent ) \
METHODASYNCCALLBACKEX( Callback, Parent, MFASYNC_FAST_IO_PROCESSING_CALLBACK, MFASYNC_CALLBACK_QUEUE_STANDARD )
#define METHODASYNCCALLBACK( Callback, Parent ) METHODASYNCCALLBACKEX( Callback, Parent, 0, MFASYNC_CALLBACK_QUEUE_STANDARD )
#define METHODASYNCRTCALLBACK( Callback, Parent ) METHODASYNCCALLBACKEX( Callback, Parent, 0, MFASYNC_CALLBACK_QUEUE_RT )
#define METHODASYNCIOCALLBACK( Callback, Parent ) \
METHODASYNCCALLBACKEX( Callback, Parent, MFASYNC_FAST_IO_PROCESSING_CALLBACK, MFASYNC_CALLBACK_QUEUE_IO )
////////////////////////////////////////////////////////
//
#define METHODASYNCCALLBACKEX2( Callback, Parent, GetQueue ) METHODASYNCCALLBACKEX( Callback, Parent, 0, GetParent()->GetQueue() )
#define METHODASYNCCALLBACKEX3( Callback, Parent, GetFlags, GetQueue ) \
METHODASYNCCALLBACKEX( Callback, Parent, GetParent()->GetFlags(), GetParent()->GetQueue() )
#endif
@@ -0,0 +1,311 @@
/*
* 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 <mfapi.h>
#include <mfd3d12.h>
#include <mfidl.h>
#include <wrl/client.h>
#include "wpptrace.h"
#include "macros.h"
#include "stats_buffer_manager.h"
#include "stats_buffer_manager.tmh"
using namespace Microsoft::WRL;
HRESULT __stdcall stats_buffer_manager::QueryInterface( const IID &riid, void **ppvObject )
{
if( riid == IID_IUnknown )
{
// If the requested IID is supported, return 'this' pointer
*ppvObject = static_cast<IUnknown *>( this );
// Must call AddRef because we are returning a valid interface pointer
AddRef();
return S_OK;
}
// Interface not supported
*ppvObject = NULL;
return E_NOINTERFACE;
}
ULONG __stdcall stats_buffer_manager::AddRef()
{
return InterlockedIncrement( &m_refCount );
}
ULONG __stdcall stats_buffer_manager::Release()
{
ULONG ulCount = InterlockedDecrement( &m_refCount );
if( ulCount == 0 )
{
delete this;
}
return ulCount;
}
HRESULT
stats_buffer_manager::Create( struct vl_screen *pVlScreen,
struct pipe_context *pPipeContext,
REFGUID guidExtension,
uint32_t width,
uint16_t height,
enum pipe_format buffer_format,
unsigned pool_size,
stats_buffer_manager **ppInstance )
{
if( !ppInstance )
return E_INVALIDARG;
*ppInstance = nullptr;
HRESULT hr;
auto pInstance = new ( std::nothrow )
stats_buffer_manager( pVlScreen, pPipeContext, guidExtension, width, height, buffer_format, pool_size, hr );
if( !pInstance )
return E_OUTOFMEMORY;
if( FAILED( hr ) )
{
delete pInstance;
return hr;
}
pInstance->m_refCount = 1;
*ppInstance = pInstance;
return S_OK;
}
// retrieve a buffer from the pool
struct pipe_resource *
stats_buffer_manager::get_new_tracked_buffer()
{
auto lock = std::lock_guard<std::mutex>( m_lock );
for( auto &entry : m_pool )
{
if( !entry.used )
{
entry.used = true;
return entry.buffer;
}
}
assert( false ); // Did not find an unused buffer
return NULL;
}
// release a buffer back to the pool
void
stats_buffer_manager::release_tracked_buffer( void *target )
{
auto lock = std::lock_guard<std::mutex>( m_lock );
for( auto &entry : m_pool )
{
struct winsys_handle whandle = {};
whandle.type = WINSYS_HANDLE_TYPE_D3D12_RES;
m_pVlScreen->pscreen->resource_get_handle( m_pVlScreen->pscreen, m_pPipeContext, entry.buffer, &whandle, 0u );
if( whandle.com_obj == target )
{
entry.used = false;
break;
}
}
}
stats_buffer_manager::stats_buffer_manager( struct vl_screen *pVlScreen,
struct pipe_context *pPipeContext,
REFGUID resourceGUID,
uint32_t width,
uint16_t height,
enum pipe_format buffer_format,
unsigned pool_size,
HRESULT &hr )
: m_pVlScreen( pVlScreen ), m_pPipeContext( pPipeContext ), m_resourceGUID( resourceGUID ), m_pool( pool_size, { NULL, false } )
{
hr = S_OK;
m_template.target = PIPE_TEXTURE_2D;
m_template.usage = PIPE_USAGE_DEFAULT;
m_template.depth0 = 1;
m_template.array_size = 1;
m_template.width0 = width;
m_template.height0 = height;
m_template.format = buffer_format;
for( auto &entry : m_pool )
{
entry.buffer = m_pVlScreen->pscreen->resource_create( m_pVlScreen->pscreen, &m_template );
if( !entry.buffer )
{
assert( true );
hr = E_FAIL;
break;
}
}
if( FAILED( hr ) )
{
for( auto &entry : m_pool )
{
if( entry.buffer )
{
m_pVlScreen->pscreen->resource_destroy( m_pVlScreen->pscreen, entry.buffer );
}
}
}
}
stats_buffer_manager::~stats_buffer_manager()
{
for( auto &entry : m_pool )
{
if( entry.buffer )
{
m_pVlScreen->pscreen->resource_destroy( m_pVlScreen->pscreen, entry.buffer );
}
}
}
// callback from IMFTrackSample (i.e. application)
IFACEMETHODIMP
stats_buffer_manager::OnSampleAvailable( IMFAsyncResult *pResult )
{
HRESULT hr = S_OK;
ComPtr<IUnknown> spUnk;
ComPtr<IMFSample> spSample;
ComPtr<IMFMediaBuffer> spMediaBuffer;
ComPtr<IMFDXGIBuffer> spDXGIBuffer;
ComPtr<IMFD3D12SynchronizationObjectCommands> spOutputSync;
ComPtr<IMFD3D12SynchronizationObject> spSyncObj;
ComPtr<ID3D12Resource> spDXGISurface;
HANDLE hFree = NULL;
CHECKHR_GOTO( pResult->GetState( &spUnk ), done );
CHECKHR_GOTO( spUnk.As( &spSample ), done );
CHECKHR_GOTO( spSample->GetBufferByIndex( 0, &spMediaBuffer ), done );
CHECKHR_GOTO( spMediaBuffer.As( &spDXGIBuffer ), done );
CHECKHR_GOTO( spDXGIBuffer->GetResource( IID_PPV_ARGS( &spDXGISurface ) ), done );
CHECKHR_GOTO( spDXGIBuffer->GetUnknown( MF_D3D12_SYNCHRONIZATION_OBJECT, IID_PPV_ARGS( &spOutputSync ) ), done );
CHECKHR_GOTO( spOutputSync.As( &spSyncObj ), done );
hFree = CreateEvent( nullptr, FALSE, FALSE, nullptr );
if( !hFree )
{
CHECKHR_GOTO( HRESULT_FROM_WIN32( GetLastError() ), done );
}
CHECKHR_GOTO( spSyncObj->SignalEventOnFinalResourceRelease( hFree ), done );
(void) WaitForSingleObject( hFree, INFINITE );
spOutputSync.Reset();
release_tracked_buffer( (void *) spDXGISurface.Get() );
done:
if( hFree )
{
CloseHandle( hFree );
}
return hr;
}
// Converts a Gallium pipe_resource into a D3D12 resource and wraps it as an IMFMediaBuffer,
// then attaches it as a sample extension on an IMFSample using the specified GUID.
HRESULT
stats_buffer_manager::AttachPipeResourceAsSampleExtension( struct pipe_resource *pPipeRes,
ID3D12CommandQueue *pSyncObjectQueue,
IMFSample *pSample )
{
HRESULT hr = S_OK;
if( !pPipeRes || !pSample || !pSyncObjectQueue )
{
return E_INVALIDARG;
}
struct winsys_handle whandle = {};
whandle.type = WINSYS_HANDLE_TYPE_D3D12_RES;
if( !m_pPipeContext->screen->resource_get_handle( m_pPipeContext->screen, m_pPipeContext, pPipeRes, &whandle, 0u ) )
{
return E_FAIL;
}
if( !whandle.com_obj )
{
return E_POINTER;
}
ComPtr<IMFTrackedSample> spTrackedSample;
hr = MFCreateTrackedSample( &spTrackedSample );
if( FAILED( hr ) )
{
return hr;
}
hr = spTrackedSample->SetAllocator( &m_xOnSampleAvailable, spTrackedSample.Get() );
if( FAILED( hr ) )
{
return hr;
}
ComPtr<IMFSample> spSample;
hr = spTrackedSample.As( &spSample );
if( FAILED( hr ) )
{
return hr;
}
ID3D12Resource *pD3D12Res = static_cast<ID3D12Resource *>( whandle.com_obj );
ComPtr<IMFMediaBuffer> spMediaBuffer;
hr = MFCreateDXGISurfaceBuffer( __uuidof( ID3D12Resource ), pD3D12Res, 0, FALSE, &spMediaBuffer );
if( FAILED( hr ) )
{
return hr;
}
// Tell MF that this buffer is ready to use.
ComPtr<IMFD3D12SynchronizationObjectCommands> spOutputSync; // needed to call Lock() for IMFMediaBuffer.
ComPtr<IMFDXGIBuffer> spDxgiBuffer;
hr = spMediaBuffer->QueryInterface( IID_PPV_ARGS( &spDxgiBuffer ) );
if( SUCCEEDED( hr ) )
{
hr = spDxgiBuffer->GetUnknown( MF_D3D12_SYNCHRONIZATION_OBJECT, IID_PPV_ARGS( &spOutputSync ) );
if( SUCCEEDED( hr ) )
{
hr = spOutputSync->EnqueueResourceReady( pSyncObjectQueue );
spSample->AddBuffer( spMediaBuffer.Get() );
if( FAILED( hr ) )
return hr;
}
else
{
return hr;
}
}
else
{
return hr;
}
return pSample->SetUnknown( m_resourceGUID, spMediaBuffer.Get() );
}
@@ -0,0 +1,98 @@
/*
* 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.
*/
#pragma once
#include <vector>
#include "gallium/include/frontend/sw_winsys.h"
#include "gallium/winsys/sw/null/null_sw_winsys.h"
#include "util/u_video.h"
#include "vl/vl_winsys.h"
#include <unknwn.h>
#include <windows.h>
#include <d3d12.h>
#include <mfobjects.h>
#include <mutex>
#include "pipe_headers.h"
#include "staticasynccallback.h"
class stats_buffer_manager : public IUnknown
{
public:
// retrieve a new tracked buffer from the pool
struct pipe_resource *get_new_tracked_buffer();
// release a tracked buffer back to the pool
void release_tracked_buffer( void *target );
HRESULT __stdcall QueryInterface( const IID &riid, void **ppvObject ) override;
ULONG __stdcall AddRef() override;
ULONG __stdcall Release() override;
HRESULT
AttachPipeResourceAsSampleExtension( struct pipe_resource *pPipeRes, ID3D12CommandQueue *pSyncObjectQueue, IMFSample *pSample );
static HRESULT Create( struct vl_screen *pVlScreen,
struct pipe_context *pPipeContext,
REFGUID guidExtension,
uint32_t width,
uint16_t height,
enum pipe_format buffer_format,
unsigned pool_size,
stats_buffer_manager **ppInstance );
private:
stats_buffer_manager( struct vl_screen *m_pVlScreen,
pipe_context *pPipeContext,
REFGUID guidExtension,
uint32_t width,
uint16_t height,
enum pipe_format buffer_format,
unsigned pool_size,
HRESULT &hr );
~stats_buffer_manager();
STDMETHOD( OnSampleAvailable )( __in IMFAsyncResult *pAsyncResult );
METHODASYNCCALLBACKEX( OnSampleAvailable, stats_buffer_manager, 0, MFASYNC_CALLBACK_QUEUE_MULTITHREADED );
std::mutex m_lock;
GUID m_resourceGUID {};
ULONG m_refCount = 0;
struct vl_screen *m_pVlScreen = nullptr;
struct pipe_context *m_pPipeContext = nullptr;
struct pipe_resource m_template = {};
struct stats_buffer_manager_pool_entry
{
struct pipe_resource *buffer;
bool used;
};
std::vector<struct stats_buffer_manager_pool_entry> m_pool;
};