mediafoundation: Add AVEncVideoReconstructedPictureOutputMode and MFSampleExtension_VideoEncodeReconstructedPicture

Reviewed-by: Pohsiang (John) Hsu <pohhsu@microsoft.com>
Reviewed-by: Yubo Xie <yuboxie@microsoft.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/38144>
This commit is contained in:
Silvio Vilerino
2025-10-24 16:00:20 -04:00
committed by Marge Bot
parent 0953924dbe
commit 4169a7f36a
11 changed files with 479 additions and 29 deletions
@@ -266,6 +266,10 @@ StringFromCodecAPI( const GUID *Api )
{
return "CODECAPI_AVEncVideoSatdMapBlockSize";
}
else if( *Api == CODECAPI_AVEncVideoReconstructedPictureOutputMode )
{
return "CODECAPI_AVEncVideoReconstructedPictureOutputMode";
}
else if( *Api == CODECAPI_AVEncVideoRateControlFramePreAnalysis )
{
return "CODECAPI_AVEncVideoRateControlFramePreAnalysis";
@@ -444,6 +448,15 @@ CDX12EncHMFT::IsSupported( const GUID *Api )
}
}
if( m_EncoderCapabilities.m_bHWSupportReadableReconstructedPicture )
{
if( *Api == CODECAPI_AVEncVideoReconstructedPictureOutputMode )
{
hr = S_OK;
return hr;
}
}
if( m_EncoderCapabilities.m_HWSupportsVideoEncodeROI.bits.roi_rc_qp_delta_support )
{
if( *Api == CODECAPI_AVEncVideoInputDeltaQPBlockSettings )
@@ -906,6 +919,11 @@ CDX12EncHMFT::GetValue( const GUID *Api, VARIANT *Value )
Value->vt = VT_UI4;
Value->ulVal = m_bVideoEnableFramePsnrYuv;
}
else if( *Api == CODECAPI_AVEncVideoReconstructedPictureOutputMode )
{
Value->vt = VT_UI4;
Value->ulVal = (UINT32)m_VideoReconstructedPictureMode;
}
else if( *Api == CODECAPI_AVEncVideoEnableSpatialAdaptiveQuantization )
{
Value->vt = VT_UI4;
@@ -1180,6 +1198,15 @@ CDX12EncHMFT::SetValue( const GUID *Api, VARIANT *Value )
{
m_bLowLatency = TRUE;
}
// Enforce disabling read only shared resource output mode when low latency is disabled
if ((m_bLowLatency == FALSE) &&
(m_VideoReconstructedPictureMode == RECON_PIC_OUTPUT_MODE_READ_ONLY_SHARED_RESOURCE))
{
m_VideoReconstructedPictureMode = RECON_PIC_OUTPUT_MODE_DISABLED;
debug_printf("[dx12 hmft 0x%p] Disabling RECON_PIC_OUTPUT_MODE_READ_ONLY_SHARED_RESOURCE as low latency is disabled. "
"Please use to RECON_PIC_OUTPUT_MODE_BLIT_COPY for LowLatency Mode disabled scenarios.\n", this);
}
}
}
else if( *Api == CODECAPI_AVEncH264CABACEnable )
@@ -1661,6 +1688,37 @@ CDX12EncHMFT::SetValue( const GUID *Api, VARIANT *Value )
}
m_bVideoEnableFramePsnrYuv = Value->ulVal ? TRUE : FALSE;
}
else if( *Api == CODECAPI_AVEncVideoReconstructedPictureOutputMode )
{
debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncVideoReconstructedPictureOutputMode - %u\n", this, Value->ulVal );
if( Value->vt != VT_UI4 )
{
CHECKHR_GOTO( E_INVALIDARG, done );
}
if( Value->ulVal > RECON_PIC_OUTPUT_MODE_READ_ONLY_SHARED_RESOURCE )
{
MFE_ERROR( "[dx12 hmft 0x%p] Invalid value %u for CODECAPI_AVEncVideoReconstructedPictureOutputMode. Valid values are 0-2.",
this, Value->ulVal );
CHECKHR_GOTO( E_INVALIDARG, done );
}
if ( !m_EncoderCapabilities.m_bHWSupportReadableReconstructedPicture && Value->ulVal != RECON_PIC_OUTPUT_MODE_DISABLED )
{
MFE_ERROR( "[dx12 hmft 0x%p] User tried to enable CODECAPI_AVEncVideoReconstructedPictureOutputMode, but this encoder "
"does NOT support this feature.",
this );
CHECKHR_GOTO( E_INVALIDARG, done );
}
if ( Value->ulVal == RECON_PIC_OUTPUT_MODE_READ_ONLY_SHARED_RESOURCE && !m_bLowLatency )
{
MFE_ERROR( "[dx12 hmft 0x%p] RECON_PIC_OUTPUT_MODE_READ_ONLY_SHARED_RESOURCE requires low latency mode to be enabled.",
this );
CHECKHR_GOTO( E_INVALIDARG, done );
}
m_VideoReconstructedPictureMode = (RECON_PIC_OUTPUT_MODE)Value->ulVal;
}
else if( *Api == CODECAPI_AVEncVideoEnableSpatialAdaptiveQuantization )
{
debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncVideoEnableSpatialAdaptiveQuantization - %u\n", this, Value->ulVal );
@@ -47,6 +47,11 @@ typedef class DX12EncodeContext
pipe_resource *pPipeResourceSATDMapStats = nullptr;
pipe_resource *pPipeResourceRCBitAllocMapStats = nullptr;
pipe_resource *pPipeResourcePSNRStats = nullptr;
pipe_resource *pPipeResourceReconstructedPicture = nullptr;
UINT PipeResourceReconstructedPictureSubresource = 0;
pipe_fence_handle *pPipeFenceReconstructedPictureCompletionFence = NULL;
ComPtr<ID3D12Fence> spReconstructedPictureCompletionFence;
UINT64 ReconstructedPictureCompletionFenceValue = 0;
// Keep all the media and sync objects until encode is done
// and then signal EnqueueResourceRelease so the media
@@ -82,6 +87,43 @@ typedef class DX12EncodeContext
((m_Codec == D3D12_VIDEO_ENCODER_CODEC_HEVC) && (encoderPicInfo.h265enc.slice_mode == PIPE_VIDEO_SLICE_MODE_AUTO));
}
pipe_video_buffer *get_current_dpb_pic_buffer()
{
pipe_video_buffer* vid_buf = nullptr;
switch( m_Codec )
{
case D3D12_VIDEO_ENCODER_CODEC_H264:
if (encoderPicInfo.h264enc.not_referenced) return nullptr;
vid_buf = encoderPicInfo.h264enc.dpb[encoderPicInfo.h264enc.dpb_curr_pic].buffer;
break;
case D3D12_VIDEO_ENCODER_CODEC_HEVC:
if (encoderPicInfo.h265enc.not_referenced) return nullptr;
vid_buf = encoderPicInfo.h265enc.dpb[encoderPicInfo.h265enc.dpb_curr_pic].buffer;
break;
case D3D12_VIDEO_ENCODER_CODEC_AV1:
if (encoderPicInfo.av1enc.refresh_frame_flags == 0) return nullptr;
vid_buf = encoderPicInfo.av1enc.dpb[encoderPicInfo.av1enc.dpb_curr_pic].buffer;
break;
}
return vid_buf;
}
pipe_resource *get_current_dpb_pic_resource()
{
pipe_video_buffer* vid_buf = get_current_dpb_pic_buffer();
if (vid_buf)
{
struct pipe_resource *buf_resources[VL_NUM_COMPONENTS];
memset(buf_resources, 0, sizeof(buf_resources));
vid_buf->get_resources( vid_buf, &buf_resources[0] );
assert(buf_resources[0]);
return buf_resources[0];
}
return nullptr;
}
const D3D12_VIDEO_ENCODER_CODEC m_Codec = D3D12_VIDEO_ENCODER_CODEC_H264;
UINT32 GetPictureType()
{
@@ -220,5 +262,7 @@ typedef class DX12EncodeContext
pVlScreen->pscreen->resource_destroy( pVlScreen->pscreen, pPipeResourcePSNRStats );
if( pDownscaledTwoPassPipeVideoBufferCompletionFence )
pVlScreen->pscreen->fence_reference( pVlScreen->pscreen, &pDownscaledTwoPassPipeVideoBufferCompletionFence, NULL );
if ( pPipeFenceReconstructedPictureCompletionFence )
pVlScreen->pscreen->fence_reference( pVlScreen->pscreen, &pPipeFenceReconstructedPictureCompletionFence, NULL );
}
} *LPDX12EncodeContext;
@@ -22,6 +22,95 @@
*/
#include "dpb_buffer_manager.h"
#include "frontend/winsys_handle.h"
#include "vl/vl_video_buffer.h"
#include "gallium/drivers/d3d12/d3d12_interop_public.h"
HRESULT
dpb_buffer_manager::get_read_only_handle(struct pipe_video_buffer *buffer,
struct pipe_context *pipe,
ComPtr<ID3D12Device>& device,
HANDLE *pReadOnlyHandle,
UINT* pSubresourceIndex)
{
if (!buffer || !pipe || !device || !pReadOnlyHandle || !pSubresourceIndex)
return E_POINTER;
*pReadOnlyHandle = nullptr;
// Get pipe resources from the video buffer
struct pipe_resource *buf_resources[VL_NUM_COMPONENTS];
memset(buf_resources, 0, sizeof(buf_resources));
buffer->get_resources(buffer, &buf_resources[0]);
if (!buf_resources[0])
return E_INVALIDARG;
// Get the winsys handle for the resource
struct winsys_handle src_wshandle = {};
src_wshandle.type = WINSYS_HANDLE_TYPE_D3D12_RES;
if (!pipe->screen->resource_get_handle(pipe->screen,
pipe,
buf_resources[0],
&src_wshandle,
0 /*usage*/))
{
return E_FAIL;
}
if (!src_wshandle.com_obj)
return E_FAIL;
// Create a read-only shared handle from the D3D12 resource
HRESULT hr = S_OK;
HANDLE originalHandle = nullptr;
// First, create a shared handle with full access from the original resource
hr = device->CreateSharedHandle(static_cast<ID3D12Resource*>(src_wshandle.com_obj),
nullptr, // Security attributes (default)
GENERIC_ALL, // Full access for the original handle
nullptr, // Name
&originalHandle);
if (FAILED(hr) || !originalHandle)
return hr;
// Duplicate the handle with restricted (read-only) access rights
// This creates a new handle that can only be used for reading
BOOL duplicateResult = DuplicateHandle(
GetCurrentProcess(), // Source process handle
originalHandle, // Source handle
GetCurrentProcess(), // Target process handle
pReadOnlyHandle, // Target handle
GENERIC_READ, // Desired access (read-only)
FALSE, // Inherit handle
0); // Options
// Clean up the original handle since we only need the read-only version
CloseHandle(originalHandle);
if (!duplicateResult || !*pReadOnlyHandle)
{
return HRESULT_FROM_WIN32(GetLastError());
}
// Retrieve subresource index if available
// from the associated data of the video buffer
// which will contain the subresource index
// if the underlying resource uses texture arrays
if (buffer->associated_data)
{
struct d3d12_interop_video_buffer_associated_data* associated_data =
static_cast<struct d3d12_interop_video_buffer_associated_data*>(buffer->associated_data);
*pSubresourceIndex = associated_data->subresource_index;
}
else
{
*pSubresourceIndex = 0; // Default to 0 if no associated data
}
return S_OK;
}
// retrieve a buffer from the pool
struct pipe_video_buffer *
@@ -62,6 +151,14 @@ dpb_buffer_manager::dpb_buffer_manager(
m_template.height = height;
m_template.buffer_format = buffer_format;
if (codec->context->screen->get_video_param( codec->context->screen,
codec->profile,
PIPE_VIDEO_ENTRYPOINT_ENCODE,
PIPE_VIDEO_CAP_ENC_READABLE_RECONSTRUCTED_PICTURE ) != 0)
{
m_template.bind = PIPE_BIND_SHARED; // Indicate we want shared resource capabilities
}
for( auto &entry : m_pool )
entry.buffer = m_codec->create_dpb_buffer( m_codec, NULL, &m_template );
}
@@ -24,7 +24,7 @@
#pragma once
#include <vector>
#include "pipe_headers.h"
#include "hmft_entrypoints.h"
class dpb_buffer_manager
{
@@ -39,6 +39,15 @@ class dpb_buffer_manager
// release a buffer back to the pool
void release_dpb_buffer( struct pipe_video_buffer *target );
/**
* get a read-only shared handle from the video buffer's internal D3D12 resource
*/
static HRESULT get_read_only_handle(struct pipe_video_buffer *buffer,
struct pipe_context *pipe,
ComPtr<ID3D12Device>& device,
HANDLE *pReadOnlyHandle,
UINT* pSubresourceIndex);
private:
struct pipe_video_codec *m_codec = NULL;
struct pipe_video_buffer m_template = {};
@@ -538,6 +538,33 @@ CDX12EncHMFT::PrepareForEncode( IMFSample *pSample, LPDX12EncodeContext *ppDX12E
pDX12EncodeContext->encoderPicInfo.base.in_fence_value = pipeEncoderInputFenceHandleValue;
CHECKHR_GOTO( PrepareForEncodeHelper( pDX12EncodeContext, bReceivedDirtyRectBlob, dirtyRectFrameNum ), 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
if( (m_VideoReconstructedPictureMode == RECON_PIC_OUTPUT_MODE_BLIT_COPY) &&
m_EncoderCapabilities.m_bHWSupportReadableReconstructedPicture)
{
if( !m_spReconstructedPictureBufferPool )
{
CHECKHR_GOTO( stats_buffer_manager::Create( m_pVlScreen,
m_pPipeContext,
MFSampleExtension_VideoEncodeReconstructedPicture,
pDX12EncodeContext->pPipeVideoBuffer->width,
static_cast<uint16_t>( pDX12EncodeContext->pPipeVideoBuffer->height ),
pDX12EncodeContext->pPipeVideoBuffer->buffer_format,
( m_bLowLatency ? MFT_STAT_POOL_MIN_SIZE : MFT_INPUT_QUEUE_DEPTH ),
m_spReconstructedPictureBufferPool.GetAddressOf() ),
done );
}
// Only allocate the reconstructed picture copy buffer if the current frame is used as reference
if (pDX12EncodeContext->get_current_dpb_pic_resource() != nullptr)
{
pDX12EncodeContext->pPipeResourceReconstructedPicture = m_spReconstructedPictureBufferPool->get_new_tracked_buffer();
pDX12EncodeContext->PipeResourceReconstructedPictureSubresource = 0;
CHECKNULL_GOTO( pDX12EncodeContext->pPipeResourceReconstructedPicture, E_OUTOFMEMORY, done );
}
}
{
struct pipe_resource templ = {};
@@ -388,6 +388,32 @@ DEFINE_GUID( MFSampleExtension_VideoEncodeSatdMap, 0xadf61d96, 0xc2d3, 0x4b57, 0
#endif
// MFSampleExtension_VideoEncodeReconstructedPicture {3E8A1B7F-5C92-4D6E-B834-F0A729E65C48}
// Type: IMFMediaBuffer
// The reconstructed picture data of an encoded video frame (Experimental).
DEFINE_GUID( MFSampleExtension_VideoEncodeReconstructedPicture, 0x3e8a1b7f, 0x5c92, 0x4d6e, 0xb8, 0x34, 0xf0, 0xa7, 0x29, 0xe6, 0x5c, 0x48 );
#ifndef CODECAPI_AVEncVideoReconstructedPictureOutputMode
// AVEncVideoReconstructedPictureOutputMode (VT_UI4) (Experimental, Testing only)
// Specifies the reconstructed picture output mode for video encoding.
// 0: disable; 1: blit copy; 2: read-only shared resource
DEFINE_CODECAPI_GUID( AVEncVideoReconstructedPictureOutputMode,
"4A7B2E8F-1D93-4C6A-B548-91E2F8C5A7D3",
0x4a7b2e8f,
0x1d93,
0x4c6a,
0xb5,
0x48,
0x91,
0xe2,
0xf8,
0xc5,
0xa7,
0xd3 )
#define CODECAPI_AVEncVideoReconstructedPictureOutputMode DEFINE_CODECAPI_GUIDNAMED( AVEncVideoReconstructedPictureOutputMode )
#endif
#ifndef CODECAPI_AVEncVideoInputDeltaQPBlockSettings
// AVEncVideoInputDeltaQPSettings (VT_BLOB)
// Read-only parameter that specifies the settings that the encoder MFT supports with respect to delta QP values as input.
@@ -619,6 +645,10 @@ class __declspec( uuid( HMFT_GUID ) ) CDX12EncHMFT : CMFD3DManager,
pipe_resource *pPipeResourceSATDMapStats,
ComPtr<ID3D12Fence> &pResolveStatsCompletionFence,
UINT64 ResolveStatsCompletionFenceValue,
pipe_resource *pPipeResourceReconstructedPicture,
UINT PipeResourceReconstructedPictureSubresource,
ComPtr<ID3D12Fence>& spReconstructedPictureCompletionFence,
UINT64 ReconstructedPictureCompletionFenceValue,
ID3D12CommandQueue *pSyncObjectQueue );
void GetSliceBitstreamMetadata( LPDX12EncodeContext pDX12EncodeContext, uint32_t slice_idx, std::vector<struct codec_unit_location_t> &codec_unit_metadata );
void ProcessSliceBitstreamZeroCopy( LPDX12EncodeContext pDX12EncodeContext,
@@ -633,6 +663,7 @@ class __declspec( uuid( HMFT_GUID ) ) CDX12EncHMFT : CMFD3DManager,
DWORD dwReceivedInput,
BOOL bIsLastSlice,
uint64_t ResolveStatsCompletionFenceValue );
HRESULT UpdateAvailableInputType();
HRESULT InternalCheckInputType( IMFMediaType *pType );
HRESULT InternalCheckOutputType( IMFMediaType *pType );
@@ -770,6 +801,15 @@ class __declspec( uuid( HMFT_GUID ) ) CDX12EncHMFT : CMFD3DManager,
UINT32 m_uiVideoOutputBitsUsedMapBlockSize = 0;
UINT32 m_uiVideoSatdMapBlockSize = 0;
typedef enum RECON_PIC_OUTPUT_MODE
{
RECON_PIC_OUTPUT_MODE_DISABLED = 0,
RECON_PIC_OUTPUT_MODE_BLIT_COPY = 1,
RECON_PIC_OUTPUT_MODE_READ_ONLY_SHARED_RESOURCE = 2,
} RECON_PIC_OUTPUT_MODE;
RECON_PIC_OUTPUT_MODE m_VideoReconstructedPictureMode = RECON_PIC_OUTPUT_MODE_DISABLED;
UINT32 m_uiSliceGenerationMode = 0;
BOOL m_bSliceGenerationModeSet = FALSE;
@@ -287,6 +287,7 @@ done:
HRESULT
MFAttachPipeResourceAsSampleExtension( struct pipe_context *pPipeContext,
struct pipe_resource *pPipeRes,
UINT PipeResourceReconstructedPictureSubresource,
ID3D12CommandQueue *pSyncObjectQueue,
REFGUID guidExtension,
IMFSample *pSample )
@@ -311,7 +312,7 @@ MFAttachPipeResourceAsSampleExtension( struct pipe_context *pPipeContext,
ID3D12Resource *pD3D12Res = static_cast<ID3D12Resource *>( whandle.com_obj );
ComPtr<IMFMediaBuffer> spMediaBuffer;
HRESULT hr = MFCreateDXGISurfaceBuffer( __uuidof( ID3D12Resource ), pD3D12Res, 0, FALSE, &spMediaBuffer );
HRESULT hr = MFCreateDXGISurfaceBuffer( __uuidof( ID3D12Resource ), pD3D12Res, PipeResourceReconstructedPictureSubresource, FALSE, &spMediaBuffer );
if( FAILED( hr ) )
{
@@ -51,6 +51,7 @@ MFCopySample( IMFSample *dest, IMFSample *src, IMFMediaType *pmt );
HRESULT
MFAttachPipeResourceAsSampleExtension( struct pipe_context *pPipeContext,
struct pipe_resource *pPipeRes,
UINT PipeResourceReconstructedPictureSubresource,
ID3D12CommandQueue *pSyncObjectQueue,
REFGUID guidExtension,
IMFSample *pSample );
@@ -87,6 +87,11 @@ CMFD3DManager::Shutdown( bool bReleaseDeviceManager )
m_spQPMapStatsBufferPool.Reset();
}
if( m_spReconstructedPictureBufferPool )
{
m_spReconstructedPictureBufferPool.Reset();
}
if( m_spDeviceManager != nullptr )
{
if( m_hDevice != NULL )
@@ -327,8 +332,6 @@ CMFD3DManager::xOnSetD3DManager( ULONG_PTR ulParam )
HRESULT hr = S_OK;
Shutdown();
d3d12_interop_device_info1 screen_interop_info = {};
if( ulParam == 0 )
{
return hr;
@@ -349,17 +352,18 @@ CMFD3DManager::xOnSetD3DManager( ULONG_PTR ulParam )
MF_E_DXGI_DEVICE_NOT_INITIALIZED,
done );
if( ( m_pVlScreen->pscreen->interop_query_device_info( m_pVlScreen->pscreen,
sizeof( d3d12_interop_device_info1 ),
&screen_interop_info ) != 0 ) &&
( screen_interop_info.set_context_queue_priority_manager != NULL ) )
m_pVlScreen->pscreen->interop_query_device_info( m_pVlScreen->pscreen,
sizeof( d3d12_interop_device_info1 ),
&m_ScreenInteropInfo );
assert( m_ScreenInteropInfo.set_context_queue_priority_manager != NULL );
{
CHECKBOOL_GOTO( thrd_success == mtx_init( &m_ContextPriorityMgr.m_lock, mtx_plain ), MF_E_DXGI_DEVICE_NOT_INITIALIZED, done );
m_ContextPriorityMgr.base.register_work_queue = MFTRegisterWorkQueue;
m_ContextPriorityMgr.base.unregister_work_queue = MFTUnregisterWorkQueue;
CHECKBOOL_GOTO( screen_interop_info.set_context_queue_priority_manager( m_pPipeContext, &m_ContextPriorityMgr.base ) == 0,
CHECKBOOL_GOTO( m_ScreenInteropInfo.set_context_queue_priority_manager( m_pPipeContext, &m_ContextPriorityMgr.base ) == 0,
MF_E_DXGI_DEVICE_NOT_INITIALIZED,
done );
@@ -104,12 +104,14 @@ class CMFD3DManager
ComPtr<stats_buffer_manager> m_spSatdStatsBufferPool;
ComPtr<stats_buffer_manager> m_spBitsUsedStatsBufferPool;
ComPtr<stats_buffer_manager> m_spQPMapStatsBufferPool;
ComPtr<stats_buffer_manager> m_spReconstructedPictureBufferPool;
UINT32 m_uiResetToken = 0;
HANDLE m_hDevice = NULL;
struct vl_screen *m_pVlScreen = nullptr;
struct sw_winsys *m_pWinsys = nullptr;
struct pipe_context *m_pPipeContext = nullptr;
struct d3d12_interop_device_info1 m_ScreenInteropInfo = {};
struct mft_context_queue_priority_manager m_ContextPriorityMgr = {};
@@ -27,6 +27,7 @@
#include "mfpipeinterop.h"
#include "wpptrace.h"
#include "d3d12_suballoc_mediabuffer.h"
#include "dpb_buffer_manager.h"
#include "mftransform.tmh"
@@ -569,6 +570,7 @@ CDX12EncHMFT::OnOutputTypeChanged()
m_spSatdStatsBufferPool.Reset();
m_spBitsUsedStatsBufferPool.Reset();
m_spQPMapStatsBufferPool.Reset();
m_spReconstructedPictureBufferPool.Reset();
}
// Indicate that we'll be adding MF_NALU_LENGTH_INFORMATION on each output sample that comes
@@ -926,29 +928,24 @@ CDX12EncHMFT::InitializeEncoder( pipe_video_profile videoProfile, UINT32 Width,
#endif // ENCODE_WITH_TWO_PASS_LOWEST_RES
encoderSettings.two_pass.skip_1st_dpb_texture = m_bRateControlFramePreAnalysisExternalReconDownscale ? true : false;
if( encoderSettings.two_pass.enable && ( encoderSettings.two_pass.pow2_downscale_factor > 0 ) )
{
struct pipe_video_codec blitterSettings = {};
blitterSettings.entrypoint = PIPE_VIDEO_ENTRYPOINT_PROCESSING;
blitterSettings.width = Width;
blitterSettings.height = Height;
CHECKNULL_GOTO( m_pPipeVideoBlitter = m_pPipeContext->create_video_codec( m_pPipeContext, &blitterSettings ),
MF_E_UNEXPECTED,
done );
}
}
struct d3d12_interop_device_info1 screen_interop_info = {};
if( ( m_pPipeContext->screen->interop_query_device_info( m_pPipeContext->screen,
sizeof( d3d12_interop_device_info1 ),
&screen_interop_info ) != 0 ) &&
( screen_interop_info.set_video_encoder_max_async_queue_depth != nullptr ) )
if( (encoderSettings.two_pass.enable && ( encoderSettings.two_pass.pow2_downscale_factor > 0 )) ||
(m_VideoReconstructedPictureMode == RECON_PIC_OUTPUT_MODE_BLIT_COPY) )
{
screen_interop_info.set_video_encoder_max_async_queue_depth( m_pPipeContext,
( m_bLowLatency ? 1 : MFT_INPUT_QUEUE_DEPTH ) );
struct pipe_video_codec blitterSettings = {};
blitterSettings.entrypoint = PIPE_VIDEO_ENTRYPOINT_PROCESSING;
blitterSettings.width = Width;
blitterSettings.height = Height;
CHECKNULL_GOTO( m_pPipeVideoBlitter = m_pPipeContext->create_video_codec( m_pPipeContext, &blitterSettings ),
MF_E_UNEXPECTED,
done );
}
assert( m_ScreenInteropInfo.set_video_encoder_max_async_queue_depth != nullptr );
m_ScreenInteropInfo.set_video_encoder_max_async_queue_depth( m_pPipeContext,
( m_bLowLatency ? 1 : MFT_INPUT_QUEUE_DEPTH ) );
CHECKNULL_GOTO( m_pPipeVideoCodec = m_pPipeContext->create_video_codec( m_pPipeContext, &encoderSettings ),
MF_E_UNEXPECTED,
done );
@@ -1209,6 +1206,10 @@ CDX12EncHMFT::ConfigureAsyncStatsMetadataOutputSampleAttributes( IMFSample *pSam
pipe_resource *pPipeResourceSATDMapStats,
ComPtr<ID3D12Fence> &pResolveStatsCompletionFence,
UINT64 ResolveStatsCompletionFenceValue,
pipe_resource *pPipeResourceReconstructedPicture,
UINT PipeResourceReconstructedPictureSubresource,
ComPtr<ID3D12Fence>& spReconstructedPictureCompletionFence,
UINT64 ReconstructedPictureCompletionFenceValue,
ID3D12CommandQueue *pSyncObjectQueue )
{
HRESULT hr = S_OK;
@@ -1228,6 +1229,7 @@ CDX12EncHMFT::ConfigureAsyncStatsMetadataOutputSampleAttributes( IMFSample *pSam
{
CHECKHR_GOTO( MFAttachPipeResourceAsSampleExtension( m_pPipeContext,
pPipeResourcePSNRStats,
0 /*subresource*/,
pSyncObjectQueue,
MFSampleExtension_FramePsnrYuv,
pSample ), done );
@@ -1263,6 +1265,35 @@ CDX12EncHMFT::ConfigureAsyncStatsMetadataOutputSampleAttributes( IMFSample *pSam
pSample ), done );
}
// Conditionally attach reconstructed picture copy (d3d12resource), gated by the completion fence
// of the recon picture copy operation if any
if (pPipeResourceReconstructedPicture)
{
if (m_VideoReconstructedPictureMode == RECON_PIC_OUTPUT_MODE_READ_ONLY_SHARED_RESOURCE)
{
assert(pPipeResourceReconstructedPicture);
assert(!spReconstructedPictureCompletionFence); // No fence in this mode
CHECKHR_GOTO( MFAttachPipeResourceAsSampleExtension( m_pPipeContext,
pPipeResourceReconstructedPicture,
PipeResourceReconstructedPictureSubresource,
pSyncObjectQueue,
MFSampleExtension_VideoEncodeReconstructedPicture,
pSample ), done );
}
else if (m_VideoReconstructedPictureMode == RECON_PIC_OUTPUT_MODE_BLIT_COPY)
{
assert(PipeResourceReconstructedPictureSubresource == 0); // Only single subresource in the copy output texture
assert(pPipeResourceReconstructedPicture);
assert(spReconstructedPictureCompletionFence); // Copy completion fence must be valid in this mode
pSyncObjectQueue->Wait( spReconstructedPictureCompletionFence.Get(), ReconstructedPictureCompletionFenceValue );
CHECKHR_GOTO( m_spReconstructedPictureBufferPool->AttachPipeResourceAsSampleExtension(
pPipeResourceReconstructedPicture,
pSyncObjectQueue,
pSample ), done );
}
}
done:
return hr;
}
@@ -1340,6 +1371,10 @@ CDX12EncHMFT::FinalizeAndEmitOutputSample( LPDX12EncodeContext pDX12EncodeContex
pDX12EncodeContext->pPipeResourceSATDMapStats,
pDX12EncodeContext->spAsyncFence,
ResolveStatsCompletionFenceValue,
pDX12EncodeContext->pPipeResourceReconstructedPicture,
pDX12EncodeContext->PipeResourceReconstructedPictureSubresource,
pDX12EncodeContext->spReconstructedPictureCompletionFence,
pDX12EncodeContext->ReconstructedPictureCompletionFenceValue,
pDX12EncodeContext->pSyncObjectQueue ) ) )
{
MFE_ERROR( "[dx12 hmft 0x%p] ConfigureAsyncStatsMetadataOutputSampleAttributes failed", this );
@@ -1777,6 +1812,17 @@ CDX12EncHMFT::xThreadProc( void *pCtx )
pDX12EncodeContext->pAsyncFence = nullptr;
pDX12EncodeContext->spAsyncFence.Reset();
// CPU wait and destroy reconstructed picture copy fence if not null
if( pDX12EncodeContext->pPipeFenceReconstructedPictureCompletionFence )
{
HMFT_ETW_EVENT_START( "ReconstructedPictureFenceWait", pThis );
ASSERTED int wait_res = pThis->m_pPipeVideoCodec->fence_wait( pThis->m_pPipeVideoCodec,
pDX12EncodeContext->pPipeFenceReconstructedPictureCompletionFence,
OS_TIMEOUT_INFINITE );
HMFT_ETW_EVENT_STOP( "ReconstructedPictureFenceWait", pThis );
assert( wait_res );
}
HMFT_ETW_EVENT_STOP( "TimeToProcessOutput", pThis );
delete pDX12EncodeContext;
} // while try_pop
@@ -2434,10 +2480,10 @@ CDX12EncHMFT::ProcessInput( DWORD dwInputStreamIndex, IMFSample *pSample, DWORD
&pDX12EncodeContext->encoderPicInfo.base );
HMFT_ETW_EVENT_STOP( "PipeEndFrame", this );
uint64_t fence_value = 0;
uint64_t AsyncFenceValue = 0;
HANDLE fence_handle = (HANDLE) m_pPipeContext->screen->fence_get_win32_handle( m_pPipeContext->screen,
pDX12EncodeContext->pAsyncFence,
&fence_value );
&AsyncFenceValue );
CHECKNULL_GOTO( fence_handle, E_FAIL, done );
CHECKHR_GOTO( m_spDevice->OpenSharedHandle( fence_handle, IID_PPV_ARGS( pDX12EncodeContext->spAsyncFence.ReleaseAndGetAddressOf() ) ), done );
CloseHandle( fence_handle );
@@ -2451,6 +2497,127 @@ CDX12EncHMFT::ProcessInput( DWORD dwInputStreamIndex, IMFSample *pSample, DWORD
HMFT_ETW_EVENT_START( "PipeFlush", this );
m_pPipeVideoCodec->flush( m_pPipeVideoCodec );
HMFT_ETW_EVENT_STOP( "PipeFlush", this );
// Handle reconstructed picture copy if enabled
if( m_VideoReconstructedPictureMode != RECON_PIC_OUTPUT_MODE_DISABLED )
{
HMFT_ETW_EVENT_START( "ReconstructedPictureSubmit", this );
// Get last slice completion fence
pipe_fence_handle *fence_to_wait = nullptr;
uint64_t fence_value = 0;
assert( m_ScreenInteropInfo.get_video_enc_last_slice_completion_fence );
m_ScreenInteropInfo.get_video_enc_last_slice_completion_fence(
m_pPipeVideoCodec,
pDX12EncodeContext->pAsyncCookie,
&fence_to_wait );
if( fence_to_wait )
{
HANDLE fence_handle = (HANDLE) m_pPipeContext->screen->fence_get_win32_handle( m_pPipeContext->screen,
fence_to_wait,
&fence_value );
if( fence_handle )
CloseHandle( fence_handle );
}
struct pipe_video_buffer *src_buffer = pDX12EncodeContext->get_current_dpb_pic_buffer();
assert( src_buffer );
// TODO: Readonly flags for get handle
// We only support zero copy read only reconstructed picture in low latency mode
// and guarantee the src_buffer won't be modified until the next ProcessInput.
// While technically we could guarantee the recon pic buffer will not be reused/
// or rewritten by longer, it gets complicated to track and manage with all possible
// LTR/SVC/NumRef combinations, so we limit it to the next ProcessInput in LowLatency mode.
if (m_VideoReconstructedPictureMode == RECON_PIC_OUTPUT_MODE_READ_ONLY_SHARED_RESOURCE)
{
// We only support this mode in low latency mode for lifetime management reasons
if (!m_bLowLatency)
{
debug_printf("[dx12 hmft 0x%p] Zero copy read only reconstructed picture is ONLY supported in low latency mode\n", this);
assert(m_bLowLatency);
CHECKHR_GOTO(E_FAIL, done);
}
// Get read-only handle directly from the video buffer
HANDLE readOnlyHandle = nullptr;
HRESULT hr = dpb_buffer_manager::get_read_only_handle(src_buffer,
m_pPipeContext,
m_spDevice,
&readOnlyHandle,
&pDX12EncodeContext->PipeResourceReconstructedPictureSubresource);
CHECKHR_GOTO( hr, done );
CHECKNULL_GOTO( readOnlyHandle, E_FAIL, done );
if( !readOnlyHandle )
debug_printf("[dx12 hmft 0x%p] Failed to get read-only handle from video buffer\n", this);
struct winsys_handle src_wshandle = {};
src_wshandle.type = WINSYS_HANDLE_TYPE_FD;
src_wshandle.handle = readOnlyHandle;
assert(src_wshandle.handle);
if (!src_wshandle.handle) {
debug_printf("[dx12 hmft 0x%p] Invalid handle for reconstructed picture resource\n", this);
CHECKHR_GOTO(E_FAIL, done);
}
// Import the reconstructed picture resource from handle
pDX12EncodeContext->pPipeResourceReconstructedPicture =
m_pPipeContext->screen->resource_from_handle( m_pPipeContext->screen,
NULL,
&src_wshandle,
0 /*usage*/ );
assert( pDX12EncodeContext->pPipeResourceReconstructedPicture );
if (!pDX12EncodeContext->pPipeResourceReconstructedPicture) {
debug_printf("[dx12 hmft 0x%p] Failed to import reconstructed picture resource\n", this);
CHECKHR_GOTO(E_FAIL, done);
}
CloseHandle( readOnlyHandle );
}
else
{
assert(m_pPipeVideoBlitter);
struct winsys_handle whandle = {};
whandle.type = WINSYS_HANDLE_TYPE_D3D12_RES;
whandle.modifier = 2; // Expected by video_buffer_from_handle to place a pipe_resource in the pipe_video_buffer
whandle.com_obj = (void *) pDX12EncodeContext->pPipeResourceReconstructedPicture;
struct pipe_video_buffer *dst_buffer =
m_pPipeContext->video_buffer_from_handle( m_pPipeContext, src_buffer, &whandle, 0 );
assert( dst_buffer );
pDX12EncodeContext->PipeResourceReconstructedPictureSubresource = 0;
struct pipe_vpp_desc vpblit_params = {};
vpblit_params.base.in_fence = fence_to_wait;
vpblit_params.base.in_fence_value = fence_value;
vpblit_params.base.out_fence = &pDX12EncodeContext->pPipeFenceReconstructedPictureCompletionFence;
vpblit_params.base.input_format = src_buffer->buffer_format;
vpblit_params.base.output_format = dst_buffer->buffer_format;
vpblit_params.src_region.x1 = src_buffer->width;
vpblit_params.src_region.y1 = src_buffer->height;
vpblit_params.dst_region.x1 = dst_buffer->width;
vpblit_params.dst_region.y1 = dst_buffer->height;
m_pPipeVideoBlitter->begin_frame( m_pPipeVideoBlitter, dst_buffer, &vpblit_params.base );
m_pPipeVideoBlitter->process_frame( m_pPipeVideoBlitter, src_buffer, &vpblit_params );
m_pPipeVideoBlitter->end_frame( m_pPipeVideoBlitter, dst_buffer, &vpblit_params.base );
m_pPipeVideoBlitter->flush( m_pPipeVideoBlitter );
// Get D3D12 fence handle for synchronization
HANDLE fence_handle = (HANDLE) m_pPipeContext->screen->fence_get_win32_handle( m_pPipeContext->screen,
pDX12EncodeContext->pPipeFenceReconstructedPictureCompletionFence,
&pDX12EncodeContext->ReconstructedPictureCompletionFenceValue );
if( fence_handle )
{
CHECKHR_GOTO( m_spDevice->OpenSharedHandle( fence_handle, IID_PPV_ARGS( pDX12EncodeContext->spReconstructedPictureCompletionFence.ReleaseAndGetAddressOf() ) ), done );
CloseHandle( fence_handle );
}
}
HMFT_ETW_EVENT_STOP( "ReconstructedPictureSubmit", this );
}
}
// Release the QP map buffer after encode_bitstream call returns.
if( qpMapBuffer && qpSize != 0 && qpData != nullptr )