d3d12: Implement full/lower two pass video encode

Reviewed-by: Pohsiang (John) Hsu <pohhsu@microsoft.com>
Reviewed-by: Jesse Natalie <jenatali@microsoft.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/35263>
This commit is contained in:
Sil Vilerino
2025-05-22 13:33:55 -04:00
committed by Marge Bot
parent daa16cf0d4
commit 228a0a46a5
3 changed files with 585 additions and 79 deletions
+290 -3
View File
@@ -484,6 +484,179 @@ d3d12_video_encoder_update_qpmap_input(struct d3d12_video_encoder *pD3D12Enc,
#endif
}
/*
* Called on encoder creation with the encoder creation parameters
*/
void d3d12_video_encoder_initialize_two_pass(struct d3d12_video_encoder *pD3D12Enc,
const struct pipe_enc_two_pass_encoder_config& two_pass)
{
#if D3D12_VIDEO_USE_NEW_ENCODECMDLIST4_INTERFACE
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc = {};
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.two_pass_support.value =
pD3D12Enc->m_screen->get_video_param(pD3D12Enc->m_screen,
pD3D12Enc->base.profile,
pD3D12Enc->base.entrypoint,
PIPE_VIDEO_CAP_ENC_TWO_PASS);
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.AppRequested = two_pass.enable;
if (two_pass.pow2_downscale_factor > 0)
{
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.Pow2DownscaleFactor = two_pass.pow2_downscale_factor;
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.bUseExternalDPBScaling = two_pass.skip_1st_dpb_texture;
}
#endif // D3D12_VIDEO_USE_NEW_ENCODECMDLIST4_INTERFACE
}
#if D3D12_VIDEO_USE_NEW_ENCODECMDLIST4_INTERFACE
static
struct pipe_enc_two_pass_frame_config
d3d12_video_encoder_get_two_pass_config_from_picparams(struct pipe_picture_desc* picture,
enum pipe_video_format codec)
{
struct pipe_enc_two_pass_frame_config twopass_frame_config = {};
switch (codec) {
#if VIDEO_CODEC_H264ENC
case PIPE_VIDEO_FORMAT_MPEG4_AVC:
{
twopass_frame_config = ((struct pipe_h264_enc_picture_desc *)picture)->twopass_frame_config;
} break;
#endif
#if VIDEO_CODEC_H265ENC
case PIPE_VIDEO_FORMAT_HEVC:
{
twopass_frame_config = ((struct pipe_h265_enc_picture_desc *)picture)->twopass_frame_config;
} break;
#endif
default:
{
unreachable("Unsupported pipe_video_format");
} break;
}
return twopass_frame_config;
}
#endif // D3D12_VIDEO_USE_NEW_ENCODECMDLIST4_INTERFACE
/*
* Caller once per frame to update the frame specific two pass settings
* such as the skip_1st_pass_on_frame flag.
*
* This is called after the encoder has been created and the
* encoder settings have been initialized in d3d12_video_encoder_initialize_two_pass
* with the encoder creation two pass parameters.
*/
void
d3d12_video_encoder_update_two_pass_frame_settings(struct d3d12_video_encoder *pD3D12Enc,
enum pipe_video_format codec,
struct pipe_picture_desc* picture)
{
#if D3D12_VIDEO_USE_NEW_ENCODECMDLIST4_INTERFACE
if (pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.AppRequested)
{
struct pipe_enc_two_pass_frame_config two_pass_frame_cfg = d3d12_video_encoder_get_two_pass_config_from_picparams(picture, codec);
// Assume two pass enabled for all frames unless supports_dynamic_1st_pass_skip is not supported and skip requested
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.bSkipTwoPassInCurrentFrame = false;
if (pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.two_pass_support.bits.supports_dynamic_1st_pass_skip)
{
// Honor the app's request to skip the 1st pass if supports_dynamic_1st_pass_skip supported
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.bSkipTwoPassInCurrentFrame = two_pass_frame_cfg.skip_1st_pass != 0;
}
//
// For when two pass is enabled for this frame AND Pow2DownscaleFactor > 0
// also convert input downscaled texture and input recon pics (in/out)
//
if ((!pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.bSkipTwoPassInCurrentFrame) &&
(pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.Pow2DownscaleFactor > 0))
{
//
// Convert the input downscaled texture from the pic params
//
struct d3d12_video_buffer *pDownscaledInputBuffer12 = (struct d3d12_video_buffer *) two_pass_frame_cfg.downscaled_source;
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.pDownscaledInputTexture = pDownscaledInputBuffer12 ? d3d12_resource_resource(pDownscaledInputBuffer12->texture) : NULL;
//
// Convert the DPB input and output params from the picparams DPB array
//
switch (codec) {
#if VIDEO_CODEC_H264ENC
case PIPE_VIDEO_FORMAT_MPEG4_AVC:
{
struct pipe_h264_enc_picture_desc *h264Pic = (struct pipe_h264_enc_picture_desc *) picture;
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pResources.resize(h264Pic->dpb_size);
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pSubresources.resize(h264Pic->dpb_size);
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.FrameAnalysisReconstructedPictureOutput = { NULL, 0u };
for (uint8_t i = 0; i < h264Pic->dpb_size; i++) {
struct d3d12_video_buffer *vidbuf = (struct d3d12_video_buffer *) h264Pic->dpb[i].downscaled_buffer;
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pResources[i] = d3d12_resource_resource(vidbuf->texture);
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pSubresources[i] = vidbuf->idx_texarray_slots;
if (!pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.bUseExternalDPBScaling && // Pass NULL to the output recon pic 1st pass if bUseExternalDPBScaling set
(h264Pic->dpb[i].pic_order_cnt == h264Pic->pic_order_cnt))
{
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.FrameAnalysisReconstructedPictureOutput.pReconstructedPicture =
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pResources[i];
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.FrameAnalysisReconstructedPictureOutput.ReconstructedPictureSubresource =
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pSubresources[i];
}
}
// Now that we found the recon pio in the loop above
// only fill the references for frame types as DX12 expects
if ((h264Pic->picture_type == PIPE_H2645_ENC_PICTURE_TYPE_I) ||
(h264Pic->picture_type == PIPE_H2645_ENC_PICTURE_TYPE_IDR))
{
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pResources.clear();
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pSubresources.clear();
}
} break;
#endif
#if VIDEO_CODEC_H265ENC
case PIPE_VIDEO_FORMAT_HEVC:
{
struct pipe_h265_enc_picture_desc *h265Pic = (struct pipe_h265_enc_picture_desc *) picture;
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pResources.resize(h265Pic->dpb_size);
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pSubresources.resize(h265Pic->dpb_size);
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.FrameAnalysisReconstructedPictureOutput = { NULL, 0u };
for (uint8_t i = 0; i < h265Pic->dpb_size; i++) {
struct d3d12_video_buffer *vidbuf = (struct d3d12_video_buffer *) h265Pic->dpb[i].downscaled_buffer;
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pResources[i] = d3d12_resource_resource(vidbuf->texture);
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pSubresources[i] = vidbuf->idx_texarray_slots;
if (!pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.bUseExternalDPBScaling && // Pass NULL to the output recon pic 1st pass if bUseExternalDPBScaling set
(h265Pic->dpb[i].pic_order_cnt == h265Pic->pic_order_cnt))
{
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.FrameAnalysisReconstructedPictureOutput.pReconstructedPicture =
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pResources[i];
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.FrameAnalysisReconstructedPictureOutput.ReconstructedPictureSubresource =
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pSubresources[i];
}
}
// Now that we found the recon pio in the loop above
// only fill the references for frame types as DX12 expects
if (h265Pic->picture_type == PIPE_H2645_ENC_PICTURE_TYPE_IDR)
{
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pResources.clear();
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pSubresources.clear();
}
} break;
#endif
default:
{
unreachable("Unsupported pipe_video_format");
} break;
}
}
}
#endif // D3D12_VIDEO_USE_NEW_ENCODECMDLIST4_INTERFACE
}
void
d3d12_video_encoder_update_dirty_rects(struct d3d12_video_encoder *pD3D12Enc,
const struct pipe_enc_dirty_info& rects)
@@ -805,6 +978,11 @@ d3d12_video_encoder_reconfigure_encoder_objects(struct d3d12_video_encoder *pD3D
heapFlags |= D3D12_VIDEO_ENCODER_HEAP_FLAG_ALLOW_SUBREGION_NOTIFICATION_SINGLE_BUFFER;
}
if (pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.AppRequested)
{
heapFlags |= D3D12_VIDEO_ENCODER_HEAP_FLAG_ALLOW_RATE_CONTROL_FRAME_ANALYSIS;
}
D3D12_VIDEO_ENCODER_HEAP_DESC1 heapDesc1 = {
pD3D12Enc->m_NodeMask,
heapFlags,
@@ -816,7 +994,9 @@ d3d12_video_encoder_reconfigure_encoder_objects(struct d3d12_video_encoder *pD3D
// resolution list
&pD3D12Enc->m_currentEncodeConfig.m_currentResolution,
// UINT Pow2DownscaleFactor
0,
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.AppRequested ?
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.Pow2DownscaleFactor
: 0,
};
// Create encoder heap
@@ -1703,6 +1883,11 @@ bool d3d12_video_encoder_negotiate_requested_features_and_d3d12_driver_caps(stru
0) {
debug_printf("[d3d12_video_encoder] Requested input dirty regions is not supported\n");
}
if ((capEncoderSupportData1.ValidationFlags & D3D12_VIDEO_ENCODER_VALIDATION_FLAG_FRAME_ANALYSIS_NOT_SUPPORTED ) !=
0) {
debug_printf("[d3d12_video_encoder] Requested two pass encode is not supported\n");
}
#else
#endif
@@ -1756,6 +1941,12 @@ bool d3d12_video_encoder_query_d3d12_driver_caps(struct d3d12_video_encoder *pD3
capEncoderSupportData1.MotionSearch.BidirectionalRefFrameEnabled = TRUE;
}
capEncoderSupportData1.FrameAnalysis.Enabled = pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.AppRequested;
if (capEncoderSupportData1.FrameAnalysis.Enabled)
{
capEncoderSupportData1.FrameAnalysis.Pow2DownscaleFactor = pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.Pow2DownscaleFactor;
}
#endif
enum pipe_video_format codec = u_reduce_video_profile(pD3D12Enc->base.profile);
@@ -2118,6 +2309,7 @@ d3d12_video_encoder_update_current_encoder_config_state(struct d3d12_video_encod
d3d12_video_encoder_update_qpmap_input(pD3D12Enc, ((struct pipe_h264_enc_picture_desc *)picture)->input_gpu_qpmap,
((struct pipe_h264_enc_picture_desc *)picture)->roi,
((struct pipe_h264_enc_picture_desc *)picture)->pic_ctrl.temporal_id);
d3d12_video_encoder_update_two_pass_frame_settings(pD3D12Enc, codec, picture);
// ...encoder_config_state_h264 calls encoder support cap, set any state before this call
bCodecUpdatesSuccess = d3d12_video_encoder_update_current_encoder_config_state_h264(pD3D12Enc, srcTextureDesc, picture);
} break;
@@ -2135,6 +2327,7 @@ d3d12_video_encoder_update_current_encoder_config_state(struct d3d12_video_encod
d3d12_video_encoder_update_qpmap_input(pD3D12Enc, ((struct pipe_h265_enc_picture_desc *)picture)->input_gpu_qpmap,
((struct pipe_h265_enc_picture_desc *)picture)->roi,
((struct pipe_h265_enc_picture_desc *)picture)->pic.temporal_id);
d3d12_video_encoder_update_two_pass_frame_settings(pD3D12Enc, codec, picture);
// ...encoder_config_state_hevc calls encoder support cap, set any state before this call
bCodecUpdatesSuccess = d3d12_video_encoder_update_current_encoder_config_state_hevc(pD3D12Enc, srcTextureDesc, picture);
} break;
@@ -2294,6 +2487,7 @@ d3d12_video_encoder_create_encoder(struct pipe_context *context, const struct pi
pD3D12Enc->supports_sliced_fences.value = context->screen->get_video_param(context->screen, codec->profile,
codec->entrypoint,
PIPE_VIDEO_CAP_ENC_SLICED_NOTIFICATIONS);
d3d12_video_encoder_initialize_two_pass(pD3D12Enc, codec->two_pass);
return &pD3D12Enc->base;
@@ -3447,6 +3641,93 @@ d3d12_video_encoder_encode_bitstream_impl(struct pipe_video_codec *codec,
d12_gpu_stats_rc_bitallocation_map = d3d12_resource_resource(pD3D12Enc->m_currentEncodeConfig.m_GPURCBitAllocationStatsResource);
}
D3D12_VIDEO_ENCODER_FRAME_ANALYSIS FrameAnalysis = {};
D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE FrameAnalysisReconstructedPicture = {};
std::vector<D3D12_RESOURCE_BARRIER> pTwoPassExtraBarriers;
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc[pD3D12Enc->m_currentEncodeConfig.m_activeRateControlIndex].m_Flags &=
~D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_FRAME_ANALYSIS;
if ((pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.AppRequested) &&
(!pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.bSkipTwoPassInCurrentFrame))
{
pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc[pD3D12Enc->m_currentEncodeConfig.m_activeRateControlIndex].m_Flags |=
D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_FRAME_ANALYSIS;
//
// When Pow2DownscaleFactor is zero, is full resolution two pass, which leaves FrameAnalysis empty/zero filled.
// For lower 1st pass resolution, we fill FrameAnalysis appropiately
//
if (pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.Pow2DownscaleFactor > 0)
{
//
// Schedule barrier transitions (reverse ones are scheduled later by doing swap to pTwoPassExtraBarriers)
//
if (pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.pDownscaledInputTexture)
{
pTwoPassExtraBarriers.push_back(CD3DX12_RESOURCE_BARRIER::Transition(
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.pDownscaledInputTexture,
D3D12_RESOURCE_STATE_COMMON,
D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ));
}
if (pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pResources.size() > 0)
{
if ((pD3D12Enc->m_currentEncodeCapabilities.m_SupportFlags &
D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RECONSTRUCTED_FRAMES_REQUIRE_TEXTURE_ARRAYS) != 0)
{
pTwoPassExtraBarriers.push_back(CD3DX12_RESOURCE_BARRIER::Transition(
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pResources[0],
D3D12_RESOURCE_STATE_COMMON,
D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ));
}
else
{
for (unsigned i = 0; i < pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pResources.size(); i++)
pTwoPassExtraBarriers.push_back(CD3DX12_RESOURCE_BARRIER::Transition(
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pResources[i],
D3D12_RESOURCE_STATE_COMMON,
D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ));
}
}
if (pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.FrameAnalysisReconstructedPictureOutput.pReconstructedPicture) // can be NULL if external dpb scaling
{
pTwoPassExtraBarriers.push_back(CD3DX12_RESOURCE_BARRIER::Transition(
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.FrameAnalysisReconstructedPictureOutput.pReconstructedPicture,
D3D12_RESOURCE_STATE_COMMON,
D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE));
}
//
// Set EncodeFrame params
//
FrameAnalysisReconstructedPicture = pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.FrameAnalysisReconstructedPictureOutput;
FrameAnalysis =
{
// ID3D12Resource *pDownscaledFrame;
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.pDownscaledInputTexture,
// UINT64 Subresource;
0u,
// D3D12_VIDEO_ENCODE_REFERENCE_FRAMES DownscaledReferences;
{
// UINT NumTexture2Ds;
static_cast<UINT>(pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pResources.size()),
// _Field_size_full_(NumTexture2Ds) ID3D12Resource **ppTexture2Ds;
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pResources.data(),
// _Field_size_full_(NumTexture2Ds) UINT *pSubresources;
pD3D12Enc->m_currentEncodeConfig.m_TwoPassEncodeDesc.DownscaledReferences.pSubresources.data(),
},
};
}
spEncodeCommandList4->ResourceBarrier(static_cast<uint32_t>(pTwoPassExtraBarriers.size()),
pTwoPassExtraBarriers.data());
}
const D3D12_VIDEO_ENCODER_ENCODEFRAME_INPUT_ARGUMENTS1 inputStreamArguments = {
// D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_DESC
{ // D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAGS
@@ -3475,7 +3756,7 @@ d3d12_video_encoder_encode_bitstream_impl(struct pipe_video_codec *codec,
// D3D12_VIDEO_ENCODER_QUANTIZATION_OPAQUE_MAP QuantizationTextureMap;
QuantizationTextureMap,
// D3D12_VIDEO_ENCODER_FRAME_ANALYSIS FrameAnalysis;
{ },
FrameAnalysis,
},
pInputVideoD3D12Res,
inputVideoD3D12Subresource,
@@ -3659,7 +3940,7 @@ d3d12_video_encoder_encode_bitstream_impl(struct pipe_video_codec *codec,
// D3D12_VIDEO_ENCODER_ENCODE_OPERATION_METADATA_BUFFER
{ pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_spMetadataOutputBuffer.Get(), 0 },
// D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE FrameAnalysisReconstructedPicture;
{},
FrameAnalysisReconstructedPicture,
};
debug_printf("DX12 EncodeFrame submission fenceValue %" PRIu64 " current_metadata_slot %" PRIu64 " - POC %d picture_type %s LayoutMode %d SlicesCount %d IRMode %d IRIndex %d\n",
@@ -3803,6 +4084,12 @@ d3d12_video_encoder_encode_bitstream_impl(struct pipe_video_codec *codec,
}
spEncodeCommandList4->ResourceBarrier(static_cast<uint32_t>(pSlicedEncodingExtraBarriers.size()),
pSlicedEncodingExtraBarriers.data());
for (auto &BarrierDesc : pTwoPassExtraBarriers) {
std::swap(BarrierDesc.Transition.StateBefore, BarrierDesc.Transition.StateAfter);
}
spEncodeCommandList4->ResourceBarrier(static_cast<uint32_t>(pTwoPassExtraBarriers.size()),
pTwoPassExtraBarriers.data());
}
else
#endif // D3D12_VIDEO_USE_NEW_ENCODECMDLIST4_INTERFACE
@@ -378,6 +378,50 @@ struct D3D12EncodeConfiguration
struct d3d12_resource *m_GPUQPStatsResource = NULL;
struct d3d12_resource *m_GPUSATDStatsResource = NULL;
struct d3d12_resource *m_GPURCBitAllocationStatsResource = NULL;
struct
{
//
// Cached caps on encoder creation members
//
union pipe_enc_cap_two_pass two_pass_support;
//
// Encoder scope members
//
// Indicates if two pass enabled
bool AppRequested;
// Indicates, if enabled, the downscale factor for two pass
UINT Pow2DownscaleFactor;
// If enabled, disable 1st pass recon pic output
// as app will generate that by downscaling the
// 2nd pass dpn recon externally
bool bUseExternalDPBScaling;
//
// Per frame scope members
//
// If AppRequested is set, this
// indicates when a specific frame two pass
// will be disabled and its updated per frame
// from the pipe pic params
//
// Note: IHV drivers may not support two pass
// on all frame types, so the ones not supported
// will naturally be always "skipped" anyways
bool bSkipTwoPassInCurrentFrame;
ID3D12Resource* pDownscaledInputTexture;
struct
{
std::vector<ID3D12Resource *> pResources;
std::vector<uint32_t> pSubresources;
} DownscaledReferences;
D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE FrameAnalysisReconstructedPictureOutput;
} m_TwoPassEncodeDesc = {};
#endif
};
@@ -703,6 +747,13 @@ d3d12_video_encoder_update_qpmap_input(struct d3d12_video_encoder *pD3D12Enc,
struct pipe_resource* qpmap,
struct pipe_enc_roi roi,
uint32_t temporal_id);
void
d3d12_video_encoder_initialize_two_pass(struct d3d12_video_encoder *pD3D12Enc,
const struct pipe_enc_two_pass_encoder_config& two_pass);
void
d3d12_video_encoder_update_two_pass_frame_settings(struct d3d12_video_encoder *pD3D12Enc,
enum pipe_video_format codec,
struct pipe_picture_desc* picture);
///
/// d3d12_video_encoder functions ends
///
+244 -76
View File
@@ -907,8 +907,235 @@ static d3d12_video_encode_get_hevc_codec_support ( const D3D12_VIDEO_ENCODER_COD
}
#endif // VIDEO_CODEC_H265ENC
static bool
d3d12_has_video_process_support(struct pipe_screen *pscreen,
D3D12_FEATURE_DATA_VIDEO_PROCESS_SUPPORT &supportCaps,
D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC& outMinSupportedInput,
D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC& outMaxSupportedInput)
{
ComPtr<ID3D12VideoDevice> spD3D12VideoDevice;
struct d3d12_screen *pD3D12Screen = (struct d3d12_screen *) pscreen;
if (FAILED(pD3D12Screen->dev->QueryInterface(IID_PPV_ARGS(spD3D12VideoDevice.GetAddressOf())))) {
// No video process support in underlying d3d12 device (needs ID3D12VideoDevice)
return false;
}
D3D12_FEATURE_DATA_VIDEO_FEATURE_AREA_SUPPORT VideoFeatureAreaSupport = {};
if (FAILED(spD3D12VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_FEATURE_AREA_SUPPORT,
&VideoFeatureAreaSupport,
sizeof(VideoFeatureAreaSupport)))) {
return false;
}
D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC resolutionsList[] = {
{ 8192, 8192 }, // 8k
{ 8192, 4320 }, // 8k - alternative
{ 7680, 4800 }, // 8k - alternative
{ 7680, 4320 }, // 8k - alternative
{ 4096, 2304 }, // 2160p (4K)
{ 4096, 2160 }, // 2160p (4K) - alternative
{ 2560, 1440 }, // 1440p
{ 1920, 1200 }, // 1200p
{ 1920, 1080 }, // 1080p
{ 1280, 720 }, // 720p
{ 800, 600 },
{ 352, 480 },
{ 352, 240 },
{ 176, 144 },
{ 128, 128 },
{ 96, 96 },
{ 64, 64 },
{ 32, 32 },
{ 16, 16 },
{ 8, 8 },
{ 4, 4 },
{ 2, 2 },
{ 1, 1 },
};
outMinSupportedInput = {};
outMaxSupportedInput = {};
uint32_t idxResol = 0;
bool bSupportsAny = false;
while (idxResol < ARRAY_SIZE(resolutionsList)) {
supportCaps.InputSample.Width = resolutionsList[idxResol].Width;
supportCaps.InputSample.Height = resolutionsList[idxResol].Height;
if (SUCCEEDED(spD3D12VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_PROCESS_SUPPORT, &supportCaps, sizeof(supportCaps)))) {
if ((supportCaps.SupportFlags & D3D12_VIDEO_PROCESS_SUPPORT_FLAG_SUPPORTED) != 0)
{
// Save the first (maximum)
if(!bSupportsAny) {
outMaxSupportedInput = resolutionsList[idxResol];
bSupportsAny = true;
}
// Keep saving the other supported values to get the minimum
outMinSupportedInput = resolutionsList[idxResol];
}
}
idxResol++;
}
return VideoFeatureAreaSupport.VideoProcessSupport && bSupportsAny;
}
#if D3D12_VIDEO_USE_NEW_ENCODECMDLIST4_INTERFACE
static
union pipe_enc_cap_two_pass
query_two_pass_support(struct pipe_screen *pscreen,
D3D12_VIDEO_ENCODER_INPUT_MAP_SESSION_INFO sessionInfo,
ID3D12VideoDevice3* pD3D12VideoDevice,
bool bDriverSupportsReadableDPBReconPic)
{
union pipe_enc_cap_two_pass two_pass_support = {};
D3D12_FEATURE_DATA_VIDEO_ENCODER_RATE_CONTROL_FRAME_ANALYSIS twoPassCapData =
{
// UINT NodeIndex;
0u,
//D3D12_VIDEO_ENCODER_CODEC Codec;
sessionInfo.Codec,
//D3D12_VIDEO_ENCODER_PROFILE_DESC Profile;
sessionInfo.Profile,
//D3D12_VIDEO_ENCODER_LEVEL_SETTING Level;
sessionInfo.Level,
//DXGI_FORMAT InputFormat;
sessionInfo.InputFormat,
//D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC InputResolution;
sessionInfo.InputResolution,
//D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION CodecConfiguration;
sessionInfo.CodecConfiguration,
//D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE SubregionFrameEncoding;
sessionInfo.SubregionFrameEncoding,
//D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA SubregionFrameEncodingData;
sessionInfo.SubregionFrameEncodingData,
//D3D12_VIDEO_ENCODER_QPMAP_CONFIGURATION QPMap;
{
//BOOL Enabled;
FALSE,
//D3D12_VIDEO_ENCODER_INPUT_MAP_SOURCE MapSource;
D3D12_VIDEO_ENCODER_INPUT_MAP_SOURCE_CPU_BUFFER,
},
//D3D12_VIDEO_ENCODER_DIRTY_REGIONS_CONFIGURATION DirtyRegions;
{
//BOOL Enabled;
FALSE,
//D3D12_VIDEO_ENCODER_INPUT_MAP_SOURCE MapSource;
D3D12_VIDEO_ENCODER_INPUT_MAP_SOURCE_CPU_BUFFER,
//D3D12_VIDEO_ENCODER_DIRTY_REGIONS_MAP_VALUES_MODE MapValuesType;
D3D12_VIDEO_ENCODER_DIRTY_REGIONS_MAP_VALUES_MODE_DIRTY,
},
//D3D12_VIDEO_ENCODER_MOTION_SEARCH_CONFIGURATION MotionSearch;
{
//BOOL Enabled;
FALSE,
//D3D12_VIDEO_ENCODER_INPUT_MAP_SOURCE MapSource;
D3D12_VIDEO_ENCODER_INPUT_MAP_SOURCE_CPU_BUFFER,
//D3D12_VIDEO_ENCODER_FRAME_MOTION_SEARCH_MODE MotionSearchMode;
D3D12_VIDEO_ENCODER_FRAME_MOTION_SEARCH_MODE_FULL_SEARCH,
//BOOL BidirectionalRefFrameEnabled;
FALSE,
},
//UINT Pow2DownscaleFactor;
0u,
//D3D12_VIDEO_ENCODER_RATE_CONTROL_FRAME_ANALYSIS_SUPPORT_FLAGS SupportFlags;
D3D12_VIDEO_ENCODER_RATE_CONTROL_FRAME_ANALYSIS_SUPPORT_FLAG_NONE,
};
//
// Find out if the driver supports D3D12_VIDEO_SCALE_SUPPORT_FLAG_DPB_ENCODER_RESOURCES
//
D3D12_FEATURE_DATA_VIDEO_PROCESS_SUPPORT vpblitCapsData =
{
0, // NodeIndex
{
sessionInfo.InputResolution.Width,
sessionInfo.InputResolution.Height,
{
sessionInfo.InputFormat,
DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709
}
},
D3D12_VIDEO_FIELD_TYPE_NONE,
D3D12_VIDEO_FRAME_STEREO_FORMAT_NONE,
{
30,
1
},
{
sessionInfo.InputFormat,
DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709
},
D3D12_VIDEO_FRAME_STEREO_FORMAT_NONE,
{
30,
1
},
};
bool bDriverSupportsOpaqueDPBReconScaling = false;
D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC minSupportedInput = {};
D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC maxSupportedInput = {};
if (d3d12_has_video_process_support(pscreen, vpblitCapsData, minSupportedInput, maxSupportedInput) &&
(sessionInfo.InputResolution.Width <= maxSupportedInput.Width) &&
(sessionInfo.InputResolution.Height <= maxSupportedInput.Height))
{
bDriverSupportsOpaqueDPBReconScaling = (vpblitCapsData.ScaleSupport.Flags & D3D12_VIDEO_SCALE_SUPPORT_FLAG_DPB_ENCODER_RESOURCES) != 0;
}
//
// Find the min/max supported Pow2DownscaleFactor values.
//
constexpr unsigned c_MaxPow2DownscaleToTry = 3u;
// set a value bigger than c_MaxPow2DownscaleToTry and find the min in the loop below using std::min
two_pass_support.bits.min_pow2_downscale_factor = c_MaxPow2DownscaleToTry + 1u;
for (unsigned CurPow2DownscaleFactor = 0; CurPow2DownscaleFactor <= c_MaxPow2DownscaleToTry; CurPow2DownscaleFactor++)
{
//
// Query the cap with CurPow2DownscaleFactor
//
twoPassCapData.Pow2DownscaleFactor = CurPow2DownscaleFactor;
if ((SUCCEEDED(pD3D12VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_ENCODER_RATE_CONTROL_FRAME_ANALYSIS, &twoPassCapData, sizeof(twoPassCapData)))) &&
(twoPassCapData.SupportFlags & D3D12_VIDEO_ENCODER_RATE_CONTROL_FRAME_ANALYSIS_SUPPORT_FLAG_SUPPORTED))
{
two_pass_support.bits.supports_two_pass = 1u;
two_pass_support.bits.min_pow2_downscale_factor = std::min(two_pass_support.bits.min_pow2_downscale_factor, twoPassCapData.Pow2DownscaleFactor);
two_pass_support.bits.max_pow2_downscale_factor = std::max(two_pass_support.bits.min_pow2_downscale_factor, twoPassCapData.Pow2DownscaleFactor);
two_pass_support.bits.supports_dynamic_1st_pass_skip = (twoPassCapData.SupportFlags & D3D12_VIDEO_ENCODER_RATE_CONTROL_FRAME_ANALYSIS_SUPPORT_FLAG_DYNAMIC_1ST_PASS_SKIP) ? 1u : 0u;
if (twoPassCapData.SupportFlags & D3D12_VIDEO_ENCODER_RATE_CONTROL_FRAME_ANALYSIS_SUPPORT_FLAG_EXTERNAL_DPB_DOWNSCALING)
{
//
// For external DPB recon downscaling, one of these must be true:
// 1. The DPB pictures are non-opaque layout (e.g without the reference only resource flag)
// 2. The DPB pictures are opaque layout, but the driver also supports
// D3D12_VIDEO_SCALE_SUPPORT_FLAG_DPB_ENCODER_RESOURCES (in the VPblit caps)
//
if (bDriverSupportsReadableDPBReconPic)
{
two_pass_support.bits.supports_1pass_recon_writing_skip = 1u;
}
else
{
two_pass_support.bits.supports_1pass_recon_writing_skip = bDriverSupportsOpaqueDPBReconScaling ? 1u : 0u;
}
}
}
}
if (!two_pass_support.bits.supports_two_pass)
{
two_pass_support = {}; // Clean min_pow2_downscale_factor
}
return two_pass_support;
}
static
union pipe_enc_cap_dirty_info
query_dirty_rects_support(D3D12_VIDEO_ENCODER_INPUT_MAP_SESSION_INFO sessionInfo,
@@ -1251,7 +1478,8 @@ d3d12_has_video_encode_support(struct pipe_screen *pscreen,
union pipe_enc_cap_sliced_notifications &sliced_encode_support,
union pipe_enc_cap_dirty_info &dirty_rects_support_gpu,
union pipe_enc_cap_qpmap &qpmap_support,
union pipe_enc_cap_motion_vector_map &gpu_motion_input_support)
union pipe_enc_cap_motion_vector_map &gpu_motion_input_support,
union pipe_enc_cap_two_pass &two_pass_support)
{
ComPtr<ID3D12VideoDevice3> spD3D12VideoDevice;
struct d3d12_screen *pD3D12Screen = (struct d3d12_screen *) pscreen;
@@ -1389,6 +1617,9 @@ d3d12_has_video_encode_support(struct pipe_screen *pscreen,
sliced_encode_support = get_sliced_encode_support(capEncoderSupportData1.SupportFlags);
qpmap_support = get_qpmap_gpuinput_support(sessionInfo, spD3D12VideoDevice.Get());
gpu_motion_input_support = get_motion_gpuinput_support(sessionInfo, spD3D12VideoDevice.Get());
two_pass_support = query_two_pass_support(pscreen, sessionInfo, spD3D12VideoDevice.Get(),
((capEncoderSupportData1.SupportFlags &
D3D12_VIDEO_ENCODER_SUPPORT_FLAG_READABLE_RECONSTRUCTED_PICTURE_LAYOUT_AVAILABLE) != 0));
#endif
}
} break;
@@ -1715,6 +1946,9 @@ d3d12_has_video_encode_support(struct pipe_screen *pscreen,
get_gpu_output_stats_support(sessionInfo, capEncoderSupportData1.SupportFlags, spD3D12VideoDevice.Get(), gpu_stats_qp, gpu_stats_satd, gpu_stats_rcbits);
sliced_encode_support = get_sliced_encode_support(capEncoderSupportData1.SupportFlags);
gpu_motion_input_support = get_motion_gpuinput_support(sessionInfo, spD3D12VideoDevice.Get());
two_pass_support = query_two_pass_support(pscreen, sessionInfo, spD3D12VideoDevice.Get(),
((capEncoderSupportData1.SupportFlags &
D3D12_VIDEO_ENCODER_SUPPORT_FLAG_READABLE_RECONSTRUCTED_PICTURE_LAYOUT_AVAILABLE) != 0));
#endif
}
}
@@ -2080,79 +2314,6 @@ d3d12_screen_get_video_param_decode(struct pipe_screen *pscreen,
}
}
static bool
d3d12_has_video_process_support(struct pipe_screen *pscreen,
D3D12_FEATURE_DATA_VIDEO_PROCESS_SUPPORT &supportCaps,
D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC& outMinSupportedInput,
D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC& outMaxSupportedInput)
{
ComPtr<ID3D12VideoDevice> spD3D12VideoDevice;
struct d3d12_screen *pD3D12Screen = (struct d3d12_screen *) pscreen;
if (FAILED(pD3D12Screen->dev->QueryInterface(IID_PPV_ARGS(spD3D12VideoDevice.GetAddressOf())))) {
// No video process support in underlying d3d12 device (needs ID3D12VideoDevice)
return false;
}
D3D12_FEATURE_DATA_VIDEO_FEATURE_AREA_SUPPORT VideoFeatureAreaSupport = {};
if (FAILED(spD3D12VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_FEATURE_AREA_SUPPORT,
&VideoFeatureAreaSupport,
sizeof(VideoFeatureAreaSupport)))) {
return false;
}
D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC resolutionsList[] = {
{ 8192, 8192 }, // 8k
{ 8192, 4320 }, // 8k - alternative
{ 7680, 4800 }, // 8k - alternative
{ 7680, 4320 }, // 8k - alternative
{ 4096, 2304 }, // 2160p (4K)
{ 4096, 2160 }, // 2160p (4K) - alternative
{ 2560, 1440 }, // 1440p
{ 1920, 1200 }, // 1200p
{ 1920, 1080 }, // 1080p
{ 1280, 720 }, // 720p
{ 800, 600 },
{ 352, 480 },
{ 352, 240 },
{ 176, 144 },
{ 128, 128 },
{ 96, 96 },
{ 64, 64 },
{ 32, 32 },
{ 16, 16 },
{ 8, 8 },
{ 4, 4 },
{ 2, 2 },
{ 1, 1 },
};
outMinSupportedInput = {};
outMaxSupportedInput = {};
uint32_t idxResol = 0;
bool bSupportsAny = false;
while (idxResol < ARRAY_SIZE(resolutionsList)) {
supportCaps.InputSample.Width = resolutionsList[idxResol].Width;
supportCaps.InputSample.Height = resolutionsList[idxResol].Height;
if (SUCCEEDED(spD3D12VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_PROCESS_SUPPORT, &supportCaps, sizeof(supportCaps)))) {
if ((supportCaps.SupportFlags & D3D12_VIDEO_PROCESS_SUPPORT_FLAG_SUPPORTED) != 0)
{
// Save the first (maximum)
if(!bSupportsAny) {
outMaxSupportedInput = resolutionsList[idxResol];
bSupportsAny = true;
}
// Keep saving the other supported values to get the minimum
outMinSupportedInput = resolutionsList[idxResol];
}
}
idxResol++;
}
return VideoFeatureAreaSupport.VideoProcessSupport && bSupportsAny;
}
static int
d3d12_screen_get_video_param_postproc(struct pipe_screen *pscreen,
enum pipe_video_profile profile,
@@ -2305,6 +2466,7 @@ d3d12_screen_get_video_param_encode(struct pipe_screen *pscreen,
union pipe_enc_cap_sliced_notifications sliced_encode_support = {};
union pipe_enc_cap_qpmap gpu_qpmap_input = {};
union pipe_enc_cap_motion_vector_map gpu_motion_input = {};
union pipe_enc_cap_two_pass two_pass_support = {};
memset(&codec_specific_support, 0, sizeof(codec_specific_support));
switch (param) {
case PIPE_VIDEO_CAP_REQUIRES_FLUSH_ON_END_FRAME:
@@ -2376,6 +2538,7 @@ d3d12_screen_get_video_param_encode(struct pipe_screen *pscreen,
case PIPE_VIDEO_CAP_ENC_DIRTY_MAPS:
case PIPE_VIDEO_CAP_ENC_QP_MAPS:
case PIPE_VIDEO_CAP_ENC_MOTION_VECTOR_MAPS:
case PIPE_VIDEO_CAP_ENC_TWO_PASS:
{
if (d3d12_has_video_encode_support(pscreen,
profile,
@@ -2404,7 +2567,8 @@ d3d12_screen_get_video_param_encode(struct pipe_screen *pscreen,
sliced_encode_support,
dirty_rects_support_gpu,
gpu_qpmap_input,
gpu_motion_input)) {
gpu_motion_input,
two_pass_support)) {
DXGI_FORMAT format = d3d12_convert_pipe_video_profile_to_dxgi_format(profile);
auto pipeFmt = d3d12_get_pipe_format(format);
@@ -2507,6 +2671,8 @@ d3d12_screen_get_video_param_encode(struct pipe_screen *pscreen,
return gpu_qpmap_input.value;
} else if (param == PIPE_VIDEO_CAP_ENC_MOTION_VECTOR_MAPS) {
return gpu_motion_input.value;
} else if (param == PIPE_VIDEO_CAP_ENC_TWO_PASS ) {
return two_pass_support.value;
}
}
} else if (param == PIPE_VIDEO_CAP_ENC_QUALITY_LEVEL) {
@@ -2584,6 +2750,7 @@ d3d12_video_encode_requires_texture_array_dpb(struct d3d12_screen* pScreen, enum
union pipe_enc_cap_sliced_notifications sliced_encode_support = {};
union pipe_enc_cap_qpmap gpu_qpmap_input = {};
union pipe_enc_cap_motion_vector_map gpu_motion_input = {};
union pipe_enc_cap_two_pass two_pass_support = {};
if (d3d12_has_video_encode_support(&pScreen->base,
profile,
maxLvlEncode,
@@ -2611,7 +2778,8 @@ d3d12_video_encode_requires_texture_array_dpb(struct d3d12_screen* pScreen, enum
sliced_encode_support,
dirty_rects_support_gpu,
gpu_qpmap_input,
gpu_motion_input))
gpu_motion_input,
two_pass_support))
{
return bVideoEncodeRequiresTextureArray;
}