diff --git a/src/gallium/drivers/d3d12/d3d12_video_enc.cpp b/src/gallium/drivers/d3d12/d3d12_video_enc.cpp index 821fbd88d27..5d71d6e7ef5 100644 --- a/src/gallium/drivers/d3d12/d3d12_video_enc.cpp +++ b/src/gallium/drivers/d3d12/d3d12_video_enc.cpp @@ -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"); diff --git a/src/gallium/drivers/d3d12/d3d12_video_enc.h b/src/gallium/drivers/d3d12/d3d12_video_enc.h index 0444681a500..2974dab0ee1 100644 --- a/src/gallium/drivers/d3d12/d3d12_video_enc.h +++ b/src/gallium/drivers/d3d12/d3d12_video_enc.h @@ -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); diff --git a/src/gallium/drivers/d3d12/d3d12_video_enc_av1.cpp b/src/gallium/drivers/d3d12/d3d12_video_enc_av1.cpp index 2c1964aa274..2d6f4cd83b3 100644 --- a/src/gallium/drivers/d3d12/d3d12_video_enc_av1.cpp +++ b/src/gallium/drivers/d3d12/d3d12_video_enc_av1.cpp @@ -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(std::ceil(srcTexture->height / sbSize)) * + static_cast(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(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 diff --git a/src/gallium/drivers/d3d12/d3d12_video_enc_h264.cpp b/src/gallium/drivers/d3d12/d3d12_video_enc_h264.cpp index 37a80d5baae..33d6cc7830e 100644 --- a/src/gallium/drivers/d3d12/d3d12_video_enc_h264.cpp +++ b/src/gallium/drivers/d3d12/d3d12_video_enc_h264.cpp @@ -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(std::ceil(srcTexture->height / D3D12_VIDEO_H264_MB_IN_PIXELS)) * + static_cast(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(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)) { diff --git a/src/gallium/drivers/d3d12/d3d12_video_enc_hevc.cpp b/src/gallium/drivers/d3d12/d3d12_video_enc_hevc.cpp index fdfa45ef3dd..c20b67a1400 100644 --- a/src/gallium/drivers/d3d12/d3d12_video_enc_hevc.cpp +++ b/src/gallium/drivers/d3d12/d3d12_video_enc_hevc.cpp @@ -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(std::ceil(srcTexture->height / ctbSize)) * + static_cast(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(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 diff --git a/src/gallium/drivers/d3d12/d3d12_video_screen.cpp b/src/gallium/drivers/d3d12/d3d12_video_screen.cpp index 691d23a2337..9255113b1b4 100644 --- a/src/gallium/drivers/d3d12/d3d12_video_screen.cpp +++ b/src/gallium/drivers/d3d12/d3d12_video_screen.cpp @@ -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 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) {