d3d12: Implement Intra Refresh for H264, HEVC, AV1

Reviewed-by: Jesse Natalie <jenatali@microsoft.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/26223>
This commit is contained in:
Sil Vilerino 2023-11-14 10:43:42 -05:00 committed by Marge Bot
parent a560706648
commit c81967fa89
6 changed files with 171 additions and 2 deletions

View file

@ -353,6 +353,8 @@ d3d12_video_encoder_reconfigure_encoder_objects(struct d3d12_video_encoder *pD3D
((pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags & d3d12_video_encoder_config_dirty_flag_gop) != 0);
bool motionPrecisionLimitChanged = ((pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags &
d3d12_video_encoder_config_dirty_flag_motion_precision_limit) != 0);
bool irChanged = ((pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags &
d3d12_video_encoder_config_dirty_flag_intra_refresh) != 0);
// Events that that trigger a re-creation of the reference picture manager
// Stores codec agnostic textures so only input format, resolution and gop (num dpb references) affects this
@ -528,6 +530,10 @@ d3d12_video_encoder_reconfigure_encoder_objects(struct d3d12_video_encoder *pD3D
(pD3D12Enc->m_fenceValue > 1) && (!reCreatedEncoder || !reCreatedEncoderHeap)) {
pD3D12Enc->m_currentEncodeConfig.m_seqFlags |= D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAG_GOP_SEQUENCE_CHANGE;
}
if(irChanged)
pD3D12Enc->m_currentEncodeConfig.m_seqFlags |= D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAG_REQUEST_INTRA_REFRESH;
return true;
}
@ -1221,6 +1227,16 @@ bool d3d12_video_encoder_negotiate_requested_features_and_d3d12_driver_caps(stru
&& (capEncoderSupportData1.ValidationFlags == D3D12_VIDEO_ENCODER_VALIDATION_FLAG_NONE));
}
if (pD3D12Enc->m_currentEncodeConfig.m_IntraRefresh.IntraRefreshDuration >
pD3D12Enc->m_currentEncodeCapabilities.m_currentResolutionSupportCaps.MaxIntraRefreshFrameDuration)
{
debug_printf("[d3d12_video_encoder] Desired duration of intrarefresh %d is not supported (higher than max "
"reported IR duration %d in query caps) for current resolution.\n",
pD3D12Enc->m_currentEncodeConfig.m_IntraRefresh.IntraRefreshDuration,
pD3D12Enc->m_currentEncodeCapabilities.m_currentResolutionSupportCaps.MaxIntraRefreshFrameDuration);
return false;
}
if(!configSupported) {
debug_printf("[d3d12_video_encoder] Cap negotiation failed, see more details below:\n");

View file

@ -113,6 +113,7 @@ enum d3d12_video_encoder_config_dirty_flags
d3d12_video_encoder_config_dirty_flag_gop = 0x100,
d3d12_video_encoder_config_dirty_flag_motion_precision_limit = 0x200,
d3d12_video_encoder_config_dirty_flag_sequence_info = 0x400,
d3d12_video_encoder_config_dirty_flag_intra_refresh = 0x800,
};
DEFINE_ENUM_FLAG_OPERATORS(d3d12_video_encoder_config_dirty_flags);

View file

@ -983,6 +983,48 @@ d3d12_video_encoder_convert_av1_codec_configuration(struct d3d12_video_encoder *
return config;
}
static bool
d3d12_video_encoder_update_intra_refresh_av1(struct d3d12_video_encoder *pD3D12Enc,
struct pipe_video_buffer * srcTexture,
struct pipe_av1_enc_picture_desc * picture)
{
if (picture->intra_refresh.mode != INTRA_REFRESH_MODE_NONE)
{
// D3D12 only supports row intra-refresh
if (picture->intra_refresh.mode != INTRA_REFRESH_MODE_UNIT_ROWS)
{
debug_printf("[d3d12_video_encoder_update_intra_refresh_av1] Unsupported INTRA_REFRESH_MODE %d\n", picture->intra_refresh.mode);
return false;
}
uint32_t sbSize = ((pD3D12Enc->m_currentEncodeConfig.m_encoderCodecSpecificConfigDesc.m_AV1Config.FeatureFlags &
D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_128x128_SUPERBLOCK) != 0) ? 128u : 64u;
uint32_t total_frame_blocks = static_cast<uint32_t>(std::ceil(srcTexture->height / sbSize)) *
static_cast<uint32_t>(std::ceil(srcTexture->width / sbSize));
D3D12_VIDEO_ENCODER_INTRA_REFRESH targetIntraRefresh = {
D3D12_VIDEO_ENCODER_INTRA_REFRESH_MODE_ROW_BASED,
total_frame_blocks / picture->intra_refresh.region_size,
};
double ir_wave_progress = (picture->intra_refresh.offset == 0) ? 0 :
picture->intra_refresh.offset / (double) total_frame_blocks;
pD3D12Enc->m_currentEncodeConfig.m_IntraRefreshCurrentFrameIndex =
static_cast<uint32_t>(std::ceil(ir_wave_progress * targetIntraRefresh.IntraRefreshDuration));
// Set intra refresh state
pD3D12Enc->m_currentEncodeConfig.m_IntraRefresh = targetIntraRefresh;
// Need to send the sequence flag during all the IR duration
pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_intra_refresh;
} else {
pD3D12Enc->m_currentEncodeConfig.m_IntraRefreshCurrentFrameIndex = 0;
pD3D12Enc->m_currentEncodeConfig.m_IntraRefresh = {
D3D12_VIDEO_ENCODER_INTRA_REFRESH_MODE_NONE,
0,
};
}
return true;
}
bool
d3d12_video_encoder_update_current_encoder_config_state_av1(struct d3d12_video_encoder *pD3D12Enc,
struct pipe_video_buffer *srcTexture,
@ -1103,6 +1145,12 @@ d3d12_video_encoder_update_current_encoder_config_state_av1(struct d3d12_video_e
return false;
}
// Set intra-refresh config
if(!d3d12_video_encoder_update_intra_refresh_av1(pD3D12Enc, srcTexture, av1Pic)) {
debug_printf("d3d12_video_encoder_update_intra_refresh_av1 failed!\n");
return false;
}
// m_currentEncodeConfig.m_encoderPicParamsDesc pic params are set in d3d12_video_encoder_reconfigure_encoder_objects
// after re-allocating objects if needed

View file

@ -805,6 +805,46 @@ d3d12_video_encoder_convert_h264_codec_configuration(struct d3d12_video_encoder
return config;
}
static bool
d3d12_video_encoder_update_intra_refresh_h264(struct d3d12_video_encoder *pD3D12Enc,
struct pipe_video_buffer * srcTexture,
struct pipe_h264_enc_picture_desc * picture)
{
if (picture->intra_refresh.mode != INTRA_REFRESH_MODE_NONE)
{
// D3D12 only supports row intra-refresh
if (picture->intra_refresh.mode != INTRA_REFRESH_MODE_UNIT_ROWS)
{
debug_printf("[d3d12_video_encoder_update_intra_refresh_h264] Unsupported INTRA_REFRESH_MODE %d\n", picture->intra_refresh.mode);
return false;
}
uint32_t total_frame_blocks = static_cast<uint32_t>(std::ceil(srcTexture->height / D3D12_VIDEO_H264_MB_IN_PIXELS)) *
static_cast<uint32_t>(std::ceil(srcTexture->width / D3D12_VIDEO_H264_MB_IN_PIXELS));
D3D12_VIDEO_ENCODER_INTRA_REFRESH targetIntraRefresh = {
D3D12_VIDEO_ENCODER_INTRA_REFRESH_MODE_ROW_BASED,
total_frame_blocks / picture->intra_refresh.region_size,
};
double ir_wave_progress = (picture->intra_refresh.offset == 0) ? 0 :
picture->intra_refresh.offset / (double) total_frame_blocks;
pD3D12Enc->m_currentEncodeConfig.m_IntraRefreshCurrentFrameIndex =
static_cast<uint32_t>(std::ceil(ir_wave_progress * targetIntraRefresh.IntraRefreshDuration));
// Set intra refresh state
pD3D12Enc->m_currentEncodeConfig.m_IntraRefresh = targetIntraRefresh;
// Need to send the sequence flag during all the IR duration
pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_intra_refresh;
} else {
pD3D12Enc->m_currentEncodeConfig.m_IntraRefreshCurrentFrameIndex = 0;
pD3D12Enc->m_currentEncodeConfig.m_IntraRefresh = {
D3D12_VIDEO_ENCODER_INTRA_REFRESH_MODE_NONE,
0,
};
}
return true;
}
bool
d3d12_video_encoder_update_current_encoder_config_state_h264(struct d3d12_video_encoder *pD3D12Enc,
struct pipe_video_buffer *srcTexture,
@ -847,6 +887,12 @@ d3d12_video_encoder_update_current_encoder_config_state_h264(struct d3d12_video_
return false;
}
// Set intra-refresh config
if(!d3d12_video_encoder_update_intra_refresh_h264(pD3D12Enc, srcTexture, h264Pic)) {
debug_printf("d3d12_video_encoder_update_intra_refresh_h264 failed!\n");
return false;
}
// Set resolution
if ((pD3D12Enc->m_currentEncodeConfig.m_currentResolution.Width != srcTexture->width) ||
(pD3D12Enc->m_currentEncodeConfig.m_currentResolution.Height != srcTexture->height)) {

View file

@ -734,6 +734,48 @@ d3d12_video_encoder_convert_hevc_codec_configuration(struct d3d12_video_encoder
return config;
}
static bool
d3d12_video_encoder_update_intra_refresh_hevc(struct d3d12_video_encoder *pD3D12Enc,
struct pipe_video_buffer * srcTexture,
struct pipe_h265_enc_picture_desc * picture)
{
if (picture->intra_refresh.mode != INTRA_REFRESH_MODE_NONE)
{
// D3D12 only supports row intra-refresh
if (picture->intra_refresh.mode != INTRA_REFRESH_MODE_UNIT_ROWS)
{
debug_printf("[d3d12_video_encoder_update_intra_refresh_hevc] Unsupported INTRA_REFRESH_MODE %d\n", picture->intra_refresh.mode);
return false;
}
uint8_t ctbSize = d3d12_video_encoder_convert_12cusize_to_pixel_size_hevc(
pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_HEVCCodecCaps.MaxLumaCodingUnitSize);
uint32_t total_frame_blocks = static_cast<uint32_t>(std::ceil(srcTexture->height / ctbSize)) *
static_cast<uint32_t>(std::ceil(srcTexture->width / ctbSize));
D3D12_VIDEO_ENCODER_INTRA_REFRESH targetIntraRefresh = {
D3D12_VIDEO_ENCODER_INTRA_REFRESH_MODE_ROW_BASED,
total_frame_blocks / picture->intra_refresh.region_size,
};
double ir_wave_progress = (picture->intra_refresh.offset == 0) ? 0 :
picture->intra_refresh.offset / (double) total_frame_blocks;
pD3D12Enc->m_currentEncodeConfig.m_IntraRefreshCurrentFrameIndex =
static_cast<uint32_t>(std::ceil(ir_wave_progress * targetIntraRefresh.IntraRefreshDuration));
// Set intra refresh state
pD3D12Enc->m_currentEncodeConfig.m_IntraRefresh = targetIntraRefresh;
// Need to send the sequence flag during all the IR duration
pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_intra_refresh;
} else {
pD3D12Enc->m_currentEncodeConfig.m_IntraRefreshCurrentFrameIndex = 0;
pD3D12Enc->m_currentEncodeConfig.m_IntraRefresh = {
D3D12_VIDEO_ENCODER_INTRA_REFRESH_MODE_NONE,
0,
};
}
return true;
}
bool
d3d12_video_encoder_update_current_encoder_config_state_hevc(struct d3d12_video_encoder *pD3D12Enc,
struct pipe_video_buffer *srcTexture,
@ -868,6 +910,12 @@ d3d12_video_encoder_update_current_encoder_config_state_hevc(struct d3d12_video_
return false;
}
// Set intra-refresh config
if(!d3d12_video_encoder_update_intra_refresh_hevc(pD3D12Enc, srcTexture, hevcPic)) {
debug_printf("d3d12_video_encoder_update_intra_refresh_hevc failed!\n");
return false;
}
// m_currentEncodeConfig.m_encoderPicParamsDesc pic params are set in d3d12_video_encoder_reconfigure_encoder_objects
// after re-allocating objects if needed

View file

@ -803,7 +803,8 @@ d3d12_has_video_encode_support(struct pipe_screen *pscreen,
uint32_t &isRCMaxFrameSizeSupported,
uint32_t &maxQualityLevels,
uint32_t &max_tile_rows,
uint32_t &max_tile_cols)
uint32_t &max_tile_cols,
uint32_t &maxIRDuration)
{
ComPtr<ID3D12VideoDevice3> spD3D12VideoDevice;
struct d3d12_screen *pD3D12Screen = (struct d3d12_screen *) pscreen;
@ -887,6 +888,7 @@ d3d12_has_video_encode_support(struct pipe_screen *pscreen,
else
maxSlices = resolutionDepCaps.MaxSubregionsNumber;
maxIRDuration = resolutionDepCaps.MaxIntraRefreshFrameDuration;
isRCMaxFrameSizeSupported = ((capEncoderSupportData1.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_MAX_FRAME_SIZE_AVAILABLE) != 0) ? 1 : 0;
maxReferencesPerFrame =
d3d12_video_encode_supported_references_per_frame_structures(codecDesc,
@ -1059,6 +1061,8 @@ d3d12_has_video_encode_support(struct pipe_screen *pscreen,
maxSlices = 0;
else
maxSlices = resolutionDepCaps.MaxSubregionsNumber;
maxIRDuration = resolutionDepCaps.MaxIntraRefreshFrameDuration;
isRCMaxFrameSizeSupported = ((capEncoderSupportData1.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_MAX_FRAME_SIZE_AVAILABLE) != 0) ? 1 : 0;
}
}
@ -1316,6 +1320,7 @@ d3d12_has_video_encode_support(struct pipe_screen *pscreen,
else
maxSlices = resolutionDepCaps.MaxSubregionsNumber;
maxIRDuration = resolutionDepCaps.MaxIntraRefreshFrameDuration;
codecSupport.av1_support.features_ext2.bits.max_tile_num_minus1 = maxSlices - 1;
isRCMaxFrameSizeSupported = ((capEncoderSupportData1.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_MAX_FRAME_SIZE_AVAILABLE) != 0) ? 1 : 0;
@ -1621,6 +1626,7 @@ d3d12_screen_get_video_param_encode(struct pipe_screen *pscreen,
uint32_t maxQualityLevels = 0u;
uint32_t max_tile_rows = 0u;
uint32_t max_tile_cols = 0u;
uint32_t maxIRDuration = 0u;
struct d3d12_encode_codec_support codec_specific_support;
memset(&codec_specific_support, 0, sizeof(codec_specific_support));
switch (param) {
@ -1650,6 +1656,7 @@ d3d12_screen_get_video_param_encode(struct pipe_screen *pscreen,
case PIPE_VIDEO_CAP_ENC_QUALITY_LEVEL:
case PIPE_VIDEO_CAP_ENC_MAX_TILE_ROWS:
case PIPE_VIDEO_CAP_ENC_MAX_TILE_COLS:
case PIPE_VIDEO_CAP_ENC_INTRA_REFRESH_MAX_DURATION:
{
if (d3d12_has_video_encode_support(pscreen,
profile,
@ -1663,7 +1670,8 @@ d3d12_screen_get_video_param_encode(struct pipe_screen *pscreen,
isRCMaxFrameSizeSupported,
maxQualityLevels,
max_tile_rows,
max_tile_cols)) {
max_tile_cols,
maxIRDuration)) {
DXGI_FORMAT format = d3d12_convert_pipe_video_profile_to_dxgi_format(profile);
auto pipeFmt = d3d12_get_pipe_format(format);
@ -1691,6 +1699,8 @@ d3d12_screen_get_video_param_encode(struct pipe_screen *pscreen,
return max_tile_cols;
} else if (param == PIPE_VIDEO_CAP_ENC_MAX_REFERENCES_PER_FRAME) {
return maxReferencesPerFrame;
} else if (param == PIPE_VIDEO_CAP_ENC_INTRA_REFRESH_MAX_DURATION) {
return maxIRDuration;
} else if (param == PIPE_VIDEO_CAP_ENC_SUPPORTS_MAX_FRAME_SIZE) {
return isRCMaxFrameSizeSupported;
} else if (param == PIPE_VIDEO_CAP_ENC_HEVC_FEATURE_FLAGS) {