radv: Implement VK_KHR_video_encode_quantization_map

Implement VK_KHR_video_encode_quantization_map on < VCN5.

Passes CTS for `*quantization_map*`.

Signed-off-by: Autumn Ashton <misyl@froggi.es>
Reviewed-by: David Rosca <david.rosca@amd.com>
Closes: #13717
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/36797>
This commit is contained in:
Autumn Ashton 2025-08-15 22:26:21 +01:00 committed by Marge Bot
parent 11a6a49534
commit ae6ea69c85
5 changed files with 149 additions and 12 deletions

View file

@ -6,3 +6,4 @@ VK_ARM_shader_core_builtins on panvk
VK_KHR_shader_untyped_pointers on anv
cl_ext_immutable_memory_objects
VK_KHR_video_encode_intra_refresh on radv
VK_KHR_video_encode_quantization_map on radv

View file

@ -492,6 +492,10 @@ radv_physical_device_get_format_properties(struct radv_physical_device *pdev, Vk
tiled |= VK_FORMAT_FEATURE_2_HOST_IMAGE_TRANSFER_BIT_EXT;
}
if (pdev->video_encode_enabled && format == VK_FORMAT_R32_SINT) {
linear |= VK_FORMAT_FEATURE_2_VIDEO_ENCODE_QUANTIZATION_DELTA_MAP_BIT_KHR;
}
out_properties->linearTilingFeatures = linear;
out_properties->optimalTilingFeatures = tiled;
out_properties->bufferFeatures = buffer;

View file

@ -649,6 +649,7 @@ radv_physical_device_get_supported_extensions(const struct radv_physical_device
.KHR_video_encode_av1 =
(radv_video_encode_av1_supported(pdev) && VIDEO_CODEC_AV1ENC && pdev->video_encode_enabled),
.KHR_video_encode_intra_refresh = pdev->video_encode_enabled,
.KHR_video_encode_quantization_map = pdev->video_encode_enabled && pdev->info.vcn_ip_version < VCN_5_0_0,
.KHR_video_encode_queue = pdev->video_encode_enabled,
.KHR_vulkan_memory_model = true,
.KHR_workgroup_memory_explicit_layout = true,
@ -1384,6 +1385,9 @@ radv_physical_device_get_features(const struct radv_physical_device *pdev, struc
/* VK_KHR_video_encode_intra_refresh */
.videoEncodeIntraRefresh = true,
/* VK_KHR_video_encode_quantization_map */
.videoEncodeQuantizationMap = true,
};
}
@ -2072,6 +2076,7 @@ radv_get_physical_device_properties(struct radv_physical_device *pdev)
VK_IMAGE_LAYOUT_VIDEO_ENCODE_DPB_KHR,
VK_IMAGE_LAYOUT_ATTACHMENT_FEEDBACK_LOOP_OPTIMAL_EXT,
VK_IMAGE_LAYOUT_ZERO_INITIALIZED_EXT,
VK_IMAGE_LAYOUT_VIDEO_ENCODE_QUANTIZATION_MAP_KHR,
};
p->copySrcLayoutCount = ARRAY_SIZE(supported_layouts);

View file

@ -804,6 +804,20 @@ radv_video_is_profile_supported(struct radv_physical_device *pdev, const VkVideo
return VK_SUCCESS;
}
static uint32_t
radv_video_get_qp_map_texel_size(VkVideoCodecOperationFlagBitsKHR codec)
{
switch (codec) {
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR:
return 16;
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR:
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR:
return 64;
default:
UNREACHABLE("Unsupported video codec operation");
}
}
VKAPI_ATTR VkResult VKAPI_CALL
radv_GetPhysicalDeviceVideoCapabilitiesKHR(VkPhysicalDevice physicalDevice, const VkVideoProfileInfoKHR *pVideoProfile,
VkVideoCapabilitiesKHR *pCapabilities)
@ -873,6 +887,8 @@ radv_GetPhysicalDeviceVideoCapabilitiesKHR(VkPhysicalDevice physicalDevice, cons
(struct VkVideoEncodeCapabilitiesKHR *)vk_find_struct(pCapabilities->pNext, VIDEO_ENCODE_CAPABILITIES_KHR);
struct VkVideoEncodeIntraRefreshCapabilitiesKHR *intra_refresh_caps =
vk_find_struct(pCapabilities->pNext, VIDEO_ENCODE_INTRA_REFRESH_CAPABILITIES_KHR);
struct VkVideoEncodeQuantizationMapCapabilitiesKHR *qp_map_caps =
vk_find_struct(pCapabilities->pNext, VIDEO_ENCODE_QUANTIZATION_MAP_CAPABILITIES_KHR);
if (enc_caps) {
enc_caps->flags = 0;
@ -885,6 +901,9 @@ radv_GetPhysicalDeviceVideoCapabilitiesKHR(VkPhysicalDevice physicalDevice, cons
enc_caps->encodeInputPictureGranularity = pCapabilities->pictureAccessGranularity;
enc_caps->supportedEncodeFeedbackFlags = VK_VIDEO_ENCODE_FEEDBACK_BITSTREAM_BUFFER_OFFSET_BIT_KHR |
VK_VIDEO_ENCODE_FEEDBACK_BITSTREAM_BYTES_WRITTEN_BIT_KHR;
if (pdev->info.vcn_ip_version < VCN_5_0_0)
enc_caps->flags |= VK_VIDEO_ENCODE_CAPABILITY_QUANTIZATION_DELTA_MAP_BIT_KHR;
}
if (intra_refresh_caps) {
intra_refresh_caps->intraRefreshModes = VK_VIDEO_ENCODE_INTRA_REFRESH_MODE_BLOCK_BASED_BIT_KHR |
@ -895,6 +914,11 @@ radv_GetPhysicalDeviceVideoCapabilitiesKHR(VkPhysicalDevice physicalDevice, cons
intra_refresh_caps->partitionIndependentIntraRefreshRegions = true;
intra_refresh_caps->nonRectangularIntraRefreshRegions = false;
}
if (qp_map_caps) {
const uint32_t qp_map_texel_size = radv_video_get_qp_map_texel_size(pVideoProfile->videoCodecOperation);
qp_map_caps->maxQuantizationMapExtent.width = pCapabilities->maxCodedExtent.width / qp_map_texel_size;
qp_map_caps->maxQuantizationMapExtent.height = pCapabilities->maxCodedExtent.height / qp_map_texel_size;
}
pCapabilities->minBitstreamBufferOffsetAlignment = 256;
pCapabilities->minBitstreamBufferSizeAlignment = 8;
if (pdev->info.vcn_ip_version >= VCN_5_0_0)
@ -972,6 +996,8 @@ radv_GetPhysicalDeviceVideoCapabilitiesKHR(VkPhysicalDevice physicalDevice, cons
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR: {
struct VkVideoEncodeH264CapabilitiesKHR *ext =
vk_find_struct(pCapabilities->pNext, VIDEO_ENCODE_H264_CAPABILITIES_KHR);
struct VkVideoEncodeH264QuantizationMapCapabilitiesKHR *qp_map_caps =
vk_find_struct(pCapabilities->pNext, VIDEO_ENCODE_H264_QUANTIZATION_MAP_CAPABILITIES_KHR);
ext->flags = VK_VIDEO_ENCODE_H264_CAPABILITY_HRD_COMPLIANCE_BIT_KHR |
VK_VIDEO_ENCODE_H264_CAPABILITY_PER_PICTURE_TYPE_MIN_MAX_QP_BIT_KHR |
@ -1005,11 +1031,18 @@ radv_GetPhysicalDeviceVideoCapabilitiesKHR(VkPhysicalDevice physicalDevice, cons
MAX2(ext->maxPPictureL0ReferenceCount, ext->maxBPictureL0ReferenceCount + ext->maxL1ReferenceCount);
pCapabilities->minCodedExtent.width = pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_5 ? 96 : 128;
pCapabilities->minCodedExtent.height = pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_5 ? 32 : 128;
if (qp_map_caps) {
qp_map_caps->minQpDelta = -51;
qp_map_caps->maxQpDelta = 51;
}
break;
}
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR: {
struct VkVideoEncodeH265CapabilitiesKHR *ext = (struct VkVideoEncodeH265CapabilitiesKHR *)vk_find_struct(
pCapabilities->pNext, VIDEO_ENCODE_H265_CAPABILITIES_KHR);
struct VkVideoEncodeH265QuantizationMapCapabilitiesKHR *qp_map_caps =
vk_find_struct(pCapabilities->pNext, VIDEO_ENCODE_H265_QUANTIZATION_MAP_CAPABILITIES_KHR);
pCapabilities->pictureAccessGranularity.width = VK_VIDEO_H265_CTU_MAX_WIDTH;
if (enc_caps) {
@ -1057,11 +1090,18 @@ radv_GetPhysicalDeviceVideoCapabilitiesKHR(VkPhysicalDevice physicalDevice, cons
MAX2(ext->maxPPictureL0ReferenceCount, ext->maxBPictureL0ReferenceCount + ext->maxL1ReferenceCount);
pCapabilities->minCodedExtent.width = pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_5 ? 384 : 130;
pCapabilities->minCodedExtent.height = 128;
if (qp_map_caps) {
qp_map_caps->minQpDelta = -51;
qp_map_caps->maxQpDelta = 51;
}
break;
}
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR: {
struct VkVideoEncodeAV1CapabilitiesKHR *ext = (struct VkVideoEncodeAV1CapabilitiesKHR *)vk_find_struct(
pCapabilities->pNext, VIDEO_ENCODE_AV1_CAPABILITIES_KHR);
struct VkVideoEncodeAV1QuantizationMapCapabilitiesKHR *qp_map_caps =
vk_find_struct(pCapabilities->pNext, VIDEO_ENCODE_AV1_QUANTIZATION_MAP_CAPABILITIES_KHR);
pCapabilities->maxDpbSlots = RADV_VIDEO_AV1_MAX_DPB_SLOTS;
pCapabilities->maxActiveReferencePictures = RADV_VIDEO_AV1_MAX_NUM_REF_FRAME;
@ -1120,6 +1160,11 @@ radv_GetPhysicalDeviceVideoCapabilitiesKHR(VkPhysicalDevice physicalDevice, cons
}
pCapabilities->minCodedExtent.width = pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_5 ? 320 : 128;
pCapabilities->minCodedExtent.height = 128;
if (qp_map_caps) {
qp_map_caps->minQIndexDelta = -255;
qp_map_caps->minQIndexDelta = 255;
}
break;
}
default:
@ -1164,10 +1209,18 @@ radv_GetPhysicalDeviceVideoFormatPropertiesKHR(VkPhysicalDevice physicalDevice,
VK_FROM_HANDLE(radv_physical_device, pdev, physicalDevice);
if ((pVideoFormatInfo->imageUsage &
(VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR | VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR)) &&
(VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR | VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR |
VK_IMAGE_USAGE_VIDEO_ENCODE_QUANTIZATION_DELTA_MAP_BIT_KHR)) &&
!pdev->video_encode_enabled)
return VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR;
/* Cannot be a QP map and other video enc/dec usages, as they are totally different formats. */
if ((pVideoFormatInfo->imageUsage & VK_IMAGE_USAGE_VIDEO_ENCODE_QUANTIZATION_DELTA_MAP_BIT_KHR) &&
(pVideoFormatInfo->imageUsage &
(VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR | VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR |
VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR | VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR)))
return VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR;
/* VCN < 5 requires separate allocates for DPB and decode video. */
if (pdev->info.vcn_ip_version < VCN_5_0_0 &&
(pVideoFormatInfo->imageUsage &
@ -1176,6 +1229,7 @@ radv_GetPhysicalDeviceVideoFormatPropertiesKHR(VkPhysicalDevice physicalDevice,
return VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR;
VkFormat format = VK_FORMAT_UNDEFINED;
uint32_t qp_map_texel_size = 0;
const struct VkVideoProfileListInfoKHR *prof_list =
(struct VkVideoProfileListInfoKHR *)vk_find_struct_const(pVideoFormatInfo->pNext, VIDEO_PROFILE_LIST_INFO_KHR);
if (prof_list) {
@ -1191,12 +1245,24 @@ radv_GetPhysicalDeviceVideoFormatPropertiesKHR(VkPhysicalDevice physicalDevice,
VkFormat profile_format = VK_FORMAT_UNDEFINED;
if (profile->lumaBitDepth == VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR)
profile_format = VK_FORMAT_G8_B8R8_2PLANE_420_UNORM;
else if (profile->lumaBitDepth == VK_VIDEO_COMPONENT_BIT_DEPTH_10_BIT_KHR)
profile_format = VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16;
else if (profile->lumaBitDepth == VK_VIDEO_COMPONENT_BIT_DEPTH_12_BIT_KHR)
profile_format = VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16;
if (pVideoFormatInfo->imageUsage & VK_IMAGE_USAGE_VIDEO_ENCODE_QUANTIZATION_DELTA_MAP_BIT_KHR) {
const uint32_t profile_qp_map_texel_size = radv_video_get_qp_map_texel_size(profile->videoCodecOperation);
/* All profiles must share the same qp_map texel size. */
if (qp_map_texel_size != 0 && qp_map_texel_size != profile_qp_map_texel_size)
return VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR;
qp_map_texel_size = profile_qp_map_texel_size;
profile_format = VK_FORMAT_R32_SINT;
} else {
if (profile->lumaBitDepth == VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR)
profile_format = VK_FORMAT_G8_B8R8_2PLANE_420_UNORM;
else if (profile->lumaBitDepth == VK_VIDEO_COMPONENT_BIT_DEPTH_10_BIT_KHR)
profile_format = VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16;
else if (profile->lumaBitDepth == VK_VIDEO_COMPONENT_BIT_DEPTH_12_BIT_KHR)
profile_format = VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16;
}
/* All profiles must share the same format. */
if (format != VK_FORMAT_UNDEFINED && format != profile_format)
@ -1205,12 +1271,17 @@ radv_GetPhysicalDeviceVideoFormatPropertiesKHR(VkPhysicalDevice physicalDevice,
format = profile_format;
}
} else {
/* On AMD, we need a codec specified for qp map as the extents differ. */
if (pVideoFormatInfo->imageUsage & VK_IMAGE_USAGE_VIDEO_ENCODE_QUANTIZATION_DELTA_MAP_BIT_KHR)
return VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR;
format = VK_FORMAT_G8_B8R8_2PLANE_420_UNORM;
}
if (format == VK_FORMAT_UNDEFINED)
return VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR;
const bool qp_map = pVideoFormatInfo->imageUsage & VK_IMAGE_USAGE_VIDEO_ENCODE_QUANTIZATION_DELTA_MAP_BIT_KHR;
const bool dpb = pVideoFormatInfo->imageUsage &
(VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR | VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR);
const bool src_dst = pVideoFormatInfo->imageUsage &
@ -1218,13 +1289,17 @@ radv_GetPhysicalDeviceVideoFormatPropertiesKHR(VkPhysicalDevice physicalDevice,
VkImageTiling tiling[3];
uint32_t num_tiling = 0;
tiling[num_tiling++] = VK_IMAGE_TILING_OPTIMAL;
if (src_dst && !dpb)
if (qp_map) {
tiling[num_tiling++] = VK_IMAGE_TILING_LINEAR;
} else {
tiling[num_tiling++] = VK_IMAGE_TILING_OPTIMAL;
if (src_dst && pdev->info.gfx_level >= GFX9)
tiling[num_tiling++] = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT;
if (src_dst && !dpb)
tiling[num_tiling++] = VK_IMAGE_TILING_LINEAR;
if (src_dst && pdev->info.gfx_level >= GFX9)
tiling[num_tiling++] = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT;
}
VK_OUTARRAY_MAKE_TYPED(VkVideoFormatPropertiesKHR, out, pVideoFormatProperties, pVideoFormatPropertyCount);
@ -1242,6 +1317,22 @@ radv_GetPhysicalDeviceVideoFormatPropertiesKHR(VkPhysicalDevice physicalDevice,
p->imageType = VK_IMAGE_TYPE_2D;
p->imageTiling = tiling[i];
p->imageUsageFlags = pVideoFormatInfo->imageUsage;
if (qp_map) {
struct VkVideoFormatQuantizationMapPropertiesKHR *qp_map_props =
vk_find_struct(p->pNext, VIDEO_FORMAT_QUANTIZATION_MAP_PROPERTIES_KHR);
struct VkVideoFormatH265QuantizationMapPropertiesKHR *qp_map_h265_props =
vk_find_struct(p->pNext, VIDEO_FORMAT_H265_QUANTIZATION_MAP_PROPERTIES_KHR);
struct VkVideoFormatAV1QuantizationMapPropertiesKHR *qp_map_av1_props =
vk_find_struct(p->pNext, VIDEO_FORMAT_AV1_QUANTIZATION_MAP_PROPERTIES_KHR);
if (qp_map_props)
qp_map_props->quantizationMapTexelSize = (VkExtent2D){qp_map_texel_size, qp_map_texel_size};
if (qp_map_h265_props)
qp_map_h265_props->compatibleCtbSizes = VK_VIDEO_ENCODE_H265_CTB_SIZE_64_BIT_KHR;
if (qp_map_av1_props)
qp_map_av1_props->compatibleSuperblockSizes = VK_VIDEO_ENCODE_AV1_SUPERBLOCK_SIZE_64_BIT_KHR;
}
}
}

View file

@ -1707,6 +1707,40 @@ radv_enc_intra_refresh(struct radv_cmd_buffer *cmd_buffer, const VkVideoEncodeIn
RADEON_ENC_END();
}
static void
radv_enc_qp_map(struct radv_cmd_buffer *cmd_buffer, const struct VkVideoEncodeInfoKHR *enc_info)
{
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
const struct VkVideoEncodeQuantizationMapInfoKHR *quantiziation_map_info =
vk_find_struct_const(enc_info->pNext, VIDEO_ENCODE_QUANTIZATION_MAP_INFO_KHR);
const struct radv_image_view *qp_map_view =
quantiziation_map_info ? radv_image_view_from_handle(quantiziation_map_info->quantizationMap) : NULL;
const struct radv_image *qp_map = qp_map_view ? qp_map_view->image : NULL;
RADEON_ENC_BEGIN(pdev->vcn_enc_cmds.enc_qp_map);
if (enc_info->flags & VK_VIDEO_ENCODE_WITH_QUANTIZATION_DELTA_MAP_BIT_KHR && qp_map) {
/* VCN < 5 uses a 32-bit signed integer for QP maps. */
assert(qp_map->vk.format == VK_FORMAT_R32_SINT);
const uint32_t qp_map_type = cmd_buffer->video.vid->enc_rate_control_method == RENCODE_RATE_CONTROL_METHOD_NONE
? RENCODE_QP_MAP_TYPE_DELTA
: RENCODE_QP_MAP_TYPE_MAP_PA;
radv_cs_add_buffer(device->ws, cmd_buffer->cs->b, qp_map->bindings[0].bo);
const uint64_t va = qp_map->bindings[0].addr;
RADEON_ENC_CS(qp_map_type);
RADEON_ENC_CS(va >> 32);
RADEON_ENC_CS(va & 0xffffffff);
RADEON_ENC_CS(qp_map->planes[0].surface.u.gfx9.surf_pitch);
} else {
RADEON_ENC_CS(RENCODE_QP_MAP_TYPE_NONE);
RADEON_ENC_CS(0);
RADEON_ENC_CS(0);
RADEON_ENC_CS(0);
}
RADEON_ENC_END();
}
static void
radv_enc_rc_per_pic(struct radv_cmd_buffer *cmd_buffer, const VkVideoEncodeInfoKHR *enc_info,
rvcn_enc_rate_ctl_per_picture_t *per_pic)
@ -2776,6 +2810,8 @@ radv_vcn_encode_video(struct radv_cmd_buffer *cmd_buffer, const VkVideoEncodeInf
}
// intra_refresh
radv_enc_intra_refresh(cmd_buffer, enc_info);
// qp map
radv_enc_qp_map(cmd_buffer, enc_info);
// v2 input format
if (pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_2) {
radv_enc_input_format(cmd_buffer);