radv/video: Add support for decode tier3

On VCN5 both distinct and coincide output/dpb are supported. Tier3
(coincide) requires tiling, Tier2 (distinct) also works with linear.
Application can decide which one to use.

Reviewed-by: Dave Airlie <airlied@redhat.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/35878>
This commit is contained in:
David Rosca
2025-06-02 16:28:42 +02:00
committed by Marge Bot
parent 2d06b43292
commit f78222dc29
2 changed files with 174 additions and 64 deletions
+173 -63
View File
@@ -53,6 +53,18 @@ radv_check_vcn_fw_version(const struct radv_physical_device *pdev, uint32_t dec,
pdev->info.vcn_fw_revision >= rev);
}
static bool
radv_enable_tier3(struct radv_physical_device *pdev, VkVideoCodecOperationFlagBitsKHR operation)
{
if (pdev->info.vcn_ip_version < VCN_5_0_0)
return false;
if (operation == VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR)
return radv_check_vcn_fw_version(pdev, 9, 9, 14);
return true;
}
static bool
radv_enable_tier2(struct radv_physical_device *pdev)
{
@@ -182,7 +194,7 @@ radv_vcn_sq_start(struct radv_cmd_buffer *cmd_buffer)
{
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
radeon_check_space(device->ws, cmd_buffer->cs, 256);
radeon_check_space(device->ws, cmd_buffer->cs, 512);
radv_vcn_sq_header(cmd_buffer->cs, &cmd_buffer->video.sq, RADEON_VCN_ENGINE_TYPE_DECODE, false);
rvcn_decode_ib_package_t *ib_header = (rvcn_decode_ib_package_t *)&(cmd_buffer->cs->buf[cmd_buffer->cs->cdw]);
ib_header->package_size = sizeof(struct rvcn_decode_buffer_s) + sizeof(struct rvcn_decode_ib_package_s);
@@ -492,21 +504,30 @@ radv_CreateVideoSessionKHR(VkDevice _device, const VkVideoSessionCreateInfoKHR *
switch (vid->vk.op) {
case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR:
vid->stream_type = RDECODE_CODEC_H264_PERF;
if (radv_enable_tier2(pdev))
if (radv_enable_tier3(pdev, vid->vk.op))
vid->dpb_type = DPB_DYNAMIC_TIER_3;
else if (radv_enable_tier2(pdev))
vid->dpb_type = DPB_DYNAMIC_TIER_2;
break;
case VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR:
vid->stream_type = RDECODE_CODEC_H265;
if (radv_enable_tier2(pdev))
if (radv_enable_tier3(pdev, vid->vk.op))
vid->dpb_type = DPB_DYNAMIC_TIER_3;
else if (radv_enable_tier2(pdev))
vid->dpb_type = DPB_DYNAMIC_TIER_2;
break;
case VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR:
vid->stream_type = RDECODE_CODEC_AV1;
vid->dpb_type = DPB_DYNAMIC_TIER_2;
if (radv_enable_tier3(pdev, vid->vk.op) && vid->vk.av1.profile != STD_VIDEO_AV1_PROFILE_PROFESSIONAL)
vid->dpb_type = DPB_DYNAMIC_TIER_3;
else
vid->dpb_type = DPB_DYNAMIC_TIER_2;
break;
case VK_VIDEO_CODEC_OPERATION_DECODE_VP9_BIT_KHR:
vid->stream_type = RDECODE_CODEC_VP9;
if (pdev->info.vcn_ip_version >= VCN_3_0_0)
if (radv_enable_tier3(pdev, vid->vk.op))
vid->dpb_type = DPB_DYNAMIC_TIER_3;
else if (pdev->info.vcn_ip_version >= VCN_3_0_0)
vid->dpb_type = DPB_DYNAMIC_TIER_2;
else
vid->dpb_type = DPB_DYNAMIC_TIER_1;
@@ -705,8 +726,11 @@ radv_GetPhysicalDeviceVideoCapabilitiesKHR(VkPhysicalDevice physicalDevice, cons
if (!is_encode) {
dec_caps =
(struct VkVideoDecodeCapabilitiesKHR *)vk_find_struct(pCapabilities->pNext, VIDEO_DECODE_CAPABILITIES_KHR);
if (dec_caps)
if (dec_caps) {
dec_caps->flags = VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_DISTINCT_BIT_KHR;
if (radv_enable_tier3(pdev, pVideoProfile->videoCodecOperation))
dec_caps->flags |= VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_COINCIDE_BIT_KHR;
}
pCapabilities->minBitstreamBufferOffsetAlignment = 128;
pCapabilities->minBitstreamBufferSizeAlignment = 128;
} else {
@@ -817,9 +841,13 @@ radv_GetPhysicalDeviceVideoCapabilitiesKHR(VkPhysicalDevice physicalDevice, cons
(!have_12bit || pVideoProfile->lumaBitDepth != VK_VIDEO_COMPONENT_BIT_DEPTH_12_BIT_KHR))
return VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR;
if (av1_profile->stdProfile == STD_VIDEO_AV1_PROFILE_PROFESSIONAL)
dec_caps->flags &= ~VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_COINCIDE_BIT_KHR;
pCapabilities->maxDpbSlots = RADV_VIDEO_AV1_MAX_DPB_SLOTS;
pCapabilities->maxActiveReferencePictures = RADV_VIDEO_AV1_MAX_NUM_REF_FRAME;
pCapabilities->flags |= VK_VIDEO_CAPABILITY_SEPARATE_REFERENCE_IMAGES_BIT_KHR;
ext->maxLevel = STD_VIDEO_AV1_LEVEL_6_1; /* For VCN3/4, the only h/w currently with AV1 decode support */
strcpy(pCapabilities->stdHeaderVersion.extensionName, VK_STD_VULKAN_VIDEO_CODEC_AV1_DECODE_EXTENSION_NAME);
pCapabilities->stdHeaderVersion.specVersion = VK_STD_VULKAN_VIDEO_CODEC_AV1_DECODE_SPEC_VERSION;
@@ -1053,8 +1081,8 @@ radv_GetPhysicalDeviceVideoFormatPropertiesKHR(VkPhysicalDevice physicalDevice,
!pdev->video_encode_enabled)
return VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR;
/* radv requires separate allocates for DPB and decode video. */
if ((pVideoFormatInfo->imageUsage &
/* VCN < 5 requires separate allocates for DPB and decode video. */
if (pdev->info.vcn_ip_version < VCN_5_0_0 && (pVideoFormatInfo->imageUsage &
(VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR | VK_IMAGE_USAGE_VIDEO_DECODE_DST_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;
@@ -1459,7 +1487,7 @@ get_h264_msg(struct radv_video_session *vid, struct radv_video_session_params *p
result.sps_info_flags |= sps->flags.mb_adaptive_frame_field_flag << 1;
result.sps_info_flags |= sps->flags.frame_mbs_only_flag << 2;
result.sps_info_flags |= sps->flags.delta_pic_order_always_zero_flag << 3;
if (vid->dpb_type != DPB_DYNAMIC_TIER_2)
if (vid->dpb_type < DPB_DYNAMIC_TIER_2)
result.sps_info_flags |= 1 << RDECODE_SPS_INFO_H264_EXTENSION_SUPPORT_FLAG_SHIFT;
result.bit_depth_luma_minus8 = sps->bit_depth_luma_minus8;
@@ -2275,6 +2303,27 @@ get_av1_msg(struct radv_device *device, struct radv_video_session *vid, struct r
return result;
}
static void
fill_ref_buffer(rvcn_dec_ref_buffer_t *ref, struct radv_image *img, uint32_t slice, uint32_t index)
{
uint64_t y_addr = img->bindings[0].addr + img->planes[0].surface.u.gfx9.surf_offset +
slice * img->planes[0].surface.u.gfx9.surf_slice_size;
uint64_t uv_addr = img->bindings[0].addr + img->planes[1].surface.u.gfx9.surf_offset +
slice * img->planes[1].surface.u.gfx9.surf_slice_size;
memset(ref, 0, sizeof(*ref));
ref->index = index;
ref->y_pitch = img->planes[0].surface.u.gfx9.surf_pitch;
ref->y_aligned_height = img->planes[0].surface.u.gfx9.surf_height;
ref->y_aligned_size = img->planes[0].surface.u.gfx9.surf_slice_size;
ref->y_ref_buffer_address_hi = y_addr >> 32;
ref->y_ref_buffer_address_lo = y_addr;
ref->uv_pitch = img->planes[1].surface.u.gfx9.surf_pitch;
ref->uv_aligned_height = img->planes[1].surface.u.gfx9.surf_height;
ref->uv_aligned_size = img->planes[1].surface.u.gfx9.surf_slice_size;
ref->uv_ref_buffer_address_hi = uv_addr >> 32;
ref->uv_ref_buffer_address_lo = uv_addr;
}
static bool
rvcn_dec_message_decode(struct radv_cmd_buffer *cmd_buffer, struct radv_video_session *vid,
@@ -2297,6 +2346,15 @@ rvcn_dec_message_decode(struct radv_cmd_buffer *cmd_buffer, struct radv_video_se
struct radv_image_plane *chroma = &img->planes[1];
bool use_intra_only_allocation_for_dpb = false;
if (vid->dpb_type == DPB_DYNAMIC_TIER_3) {
VkImageUsageFlags coincide =
VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR | VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR;
if (luma->surface.is_linear || (img->vk.usage & coincide) != coincide)
vid->dpb_type = DPB_DYNAMIC_TIER_2;
else
assert(!luma->surface.is_linear);
}
header = ptr;
sizes += sizeof(rvcn_dec_message_header_t);
@@ -2468,6 +2526,16 @@ rvcn_dec_message_decode(struct radv_cmd_buffer *cmd_buffer, struct radv_video_se
decode->hw_ctxt_size = vid->ctx.size;
uint64_t addr;
if (use_intra_only_allocation_for_dpb) {
addr = radv_buffer_get_va(vid->intra_only_dpb.mem->bo) + vid->intra_only_dpb.offset;
radv_cs_add_buffer(device->ws, cmd_buffer->cs, vid->intra_only_dpb.mem->bo);
} else {
addr = dpb->bindings[0].addr;
radv_cs_add_buffer(device->ws, cmd_buffer->cs, dpb->bindings[0].bo);
addr += dpb_array_idx * (dpb->planes[0].surface.u.gfx9.surf_slice_size + dpb->planes[1].surface.u.gfx9.surf_slice_size);
}
if (vid->dpb_type == DPB_DYNAMIC_TIER_1) {
decode->decode_flags |= (RDECODE_FLAGS_USE_DYNAMIC_DPB_MASK | RDECODE_FLAGS_USE_PAL_MASK);
@@ -2480,64 +2548,106 @@ rvcn_dec_message_decode(struct radv_cmd_buffer *cmd_buffer, struct radv_video_se
dynamic_dpb->dpbChromaAlignedHeight = dpb->planes[1].surface.u.gfx9.surf_height;
dynamic_dpb->dpbChromaAlignedSize = dpb->planes[1].surface.u.gfx9.surf_slice_size;
dynamic_dpb->dpbReserved0[0] = vid->db_alignment;
}
} else if (vid->dpb_type == DPB_DYNAMIC_TIER_2) {
decode->decode_flags = RDECODE_FLAGS_USE_DYNAMIC_DPB_MASK;
if (vid->dpb_type != DPB_DYNAMIC_TIER_2)
return true;
dynamic_dpb_t2->dpbCurrLo = addr;
dynamic_dpb_t2->dpbCurrHi = addr >> 32;
uint64_t addr;
if (use_intra_only_allocation_for_dpb) {
addr = radv_buffer_get_va(vid->intra_only_dpb.mem->bo) + vid->intra_only_dpb.offset;
radv_cs_add_buffer(device->ws, cmd_buffer->cs, vid->intra_only_dpb.mem->bo);
} else {
addr = dpb->bindings[0].addr;
radv_cs_add_buffer(device->ws, cmd_buffer->cs, dpb->bindings[0].bo);
addr += dpb_array_idx * (dpb->planes[0].surface.u.gfx9.surf_slice_size + dpb->planes[1].surface.u.gfx9.surf_slice_size);
}
dynamic_dpb_t2->dpbCurrLo = addr;
dynamic_dpb_t2->dpbCurrHi = addr >> 32;
if (vid->vk.op == VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR) {
/* The following loop will fill in the references for the current frame,
* this ensures all DPB addresses are "valid" (pointing at the current
* decode target), so that the firmware doesn't evict things it should not.
* It will not perform any actual writes to these dummy slots.
*/
for (int i = 0; i < STD_VIDEO_AV1_NUM_REF_FRAMES; i++) {
dynamic_dpb_t2->dpbAddrHi[i] = addr;
dynamic_dpb_t2->dpbAddrLo[i] = addr >> 32;
if (vid->vk.op == VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR) {
/* The following loop will fill in the references for the current frame,
* this ensures all DPB addresses are "valid" (pointing at the current
* decode target), so that the firmware doesn't evict things it should not.
* It will not perform any actual writes to these dummy slots.
*/
for (int i = 0; i < STD_VIDEO_AV1_NUM_REF_FRAMES; i++) {
dynamic_dpb_t2->dpbAddrHi[i] = addr;
dynamic_dpb_t2->dpbAddrLo[i] = addr >> 32;
}
}
for (int i = 0; i < frame_info->referenceSlotCount; i++) {
struct radv_image_view *f_dpb_iv =
radv_image_view_from_handle(frame_info->pReferenceSlots[i].pPictureResource->imageViewBinding);
assert(f_dpb_iv != NULL);
struct radv_image *dpb_img = f_dpb_iv->image;
int f_dpb_array_idx = frame_info->pReferenceSlots[i].pPictureResource->baseArrayLayer + f_dpb_iv->vk.base_array_layer;
radv_cs_add_buffer(device->ws, cmd_buffer->cs, dpb_img->bindings[0].bo);
addr = dpb_img->bindings[0].addr;
addr += f_dpb_array_idx * (dpb_img->planes[0].surface.u.gfx9.surf_slice_size + dpb_img->planes[1].surface.u.gfx9.surf_slice_size);
dynamic_dpb_t2->dpbAddrLo[i] = addr;
dynamic_dpb_t2->dpbAddrHi[i] = addr >> 32;
++dynamic_dpb_t2->dpbArraySize;
}
dynamic_dpb_t2->dpbConfigFlags = 0;
dynamic_dpb_t2->dpbLumaPitch = dpb->planes[0].surface.u.gfx9.surf_pitch;
dynamic_dpb_t2->dpbLumaAlignedHeight = dpb->planes[0].surface.u.gfx9.surf_height;
dynamic_dpb_t2->dpbLumaAlignedSize = dpb->planes[0].surface.u.gfx9.surf_slice_size;
dynamic_dpb_t2->dpbChromaPitch = dpb->planes[1].surface.u.gfx9.surf_pitch;
dynamic_dpb_t2->dpbChromaAlignedHeight = dpb->planes[1].surface.u.gfx9.surf_height;
dynamic_dpb_t2->dpbChromaAlignedSize = dpb->planes[1].surface.u.gfx9.surf_slice_size;
} else if (vid->dpb_type == DPB_DYNAMIC_TIER_3) {
decode->decode_flags = RDECODE_FLAGS_UNIFIED_DT_MASK;
bool add_setup_slot = false;
uint32_t num_bufs = frame_info->referenceSlotCount;
if (vid->vk.op == VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR) {
num_bufs = STD_VIDEO_AV1_NUM_REF_FRAMES;
/* Film grain is applied to decode target only. */
if (frame_info->pSetupReferenceSlot && (img != dpb || dt_array_idx != dpb_array_idx)) {
add_setup_slot = true;
num_bufs++;
}
}
uint32_t size = sizeof(rvcn_dec_ref_buffers_header_t) + sizeof(rvcn_dec_ref_buffer_t) * num_bufs;
rvcn_decode_ib_package_t *ib_header = (rvcn_decode_ib_package_t *)&(cmd_buffer->cs->buf[cmd_buffer->cs->cdw]);
ib_header->package_size = size + sizeof(struct rvcn_decode_ib_package_s);
cmd_buffer->cs->cdw++;
ib_header->package_type = RDECODE_IB_PARAM_DYNAMIC_REFLIST_BUFFER;
cmd_buffer->cs->cdw++;
rvcn_dec_ref_buffers_header_t *refs = (rvcn_dec_ref_buffers_header_t *)&(cmd_buffer->cs->buf[cmd_buffer->cs->cdw]);
cmd_buffer->cs->cdw += size / 4;
refs->size = size;
refs->num_bufs = 0;
uint16_t used_slots = 1 << (vid->vk.max_dpb_slots ? frame_info->pSetupReferenceSlot->slotIndex : 0);
for (int i = 0; i < frame_info->referenceSlotCount; i++) {
struct radv_image_view *f_dpb_iv =
radv_image_view_from_handle(frame_info->pReferenceSlots[i].pPictureResource->imageViewBinding);
assert(f_dpb_iv != NULL);
struct radv_image *dpb_img = f_dpb_iv->image;
uint32_t f_dpb_array_idx = frame_info->pReferenceSlots[i].pPictureResource->baseArrayLayer + f_dpb_iv->vk.base_array_layer;
fill_ref_buffer(&refs->pBufs[refs->num_bufs++], dpb_img, f_dpb_array_idx, frame_info->pReferenceSlots[i].slotIndex);
radv_cs_add_buffer(device->ws, cmd_buffer->cs, dpb_img->bindings[0].bo);
used_slots |= 1 << frame_info->pReferenceSlots[i].slotIndex;
}
if (add_setup_slot)
fill_ref_buffer(&refs->pBufs[refs->num_bufs++], dpb, dpb_array_idx, frame_info->pSetupReferenceSlot->slotIndex);
if (vid->vk.op == VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR) {
for (int j = 0; j < STD_VIDEO_AV1_NUM_REF_FRAMES + 1; j++) {
if ((used_slots & (1 << j)) == 0) {
fill_ref_buffer(&refs->pBufs[refs->num_bufs++], dpb, dpb_array_idx, j);
used_slots |= 1 << j;
}
}
}
assert(refs->num_bufs == num_bufs);
cmd_buffer->video.decode_buffer->valid_buf_flag |= RDECODE_CMDBUF_FLAGS_MSG_BUFFER;
}
for (int i = 0; i < frame_info->referenceSlotCount; i++) {
struct radv_image_view *f_dpb_iv =
radv_image_view_from_handle(frame_info->pReferenceSlots[i].pPictureResource->imageViewBinding);
assert(f_dpb_iv != NULL);
struct radv_image *dpb_img = f_dpb_iv->image;
int f_dpb_array_idx = frame_info->pReferenceSlots[i].pPictureResource->baseArrayLayer + f_dpb_iv->vk.base_array_layer;
radv_cs_add_buffer(device->ws, cmd_buffer->cs, dpb_img->bindings[0].bo);
addr = dpb_img->bindings[0].addr;
addr += f_dpb_array_idx * (dpb_img->planes[0].surface.u.gfx9.surf_slice_size + dpb_img->planes[1].surface.u.gfx9.surf_slice_size);
dynamic_dpb_t2->dpbAddrLo[i] = addr;
dynamic_dpb_t2->dpbAddrHi[i] = addr >> 32;
++dynamic_dpb_t2->dpbArraySize;
}
decode->decode_flags = 1;
dynamic_dpb_t2->dpbConfigFlags = 0;
dynamic_dpb_t2->dpbLumaPitch = dpb->planes[0].surface.u.gfx9.surf_pitch;
dynamic_dpb_t2->dpbLumaAlignedHeight = dpb->planes[0].surface.u.gfx9.surf_height;
dynamic_dpb_t2->dpbLumaAlignedSize = dpb->planes[0].surface.u.gfx9.surf_slice_size;
dynamic_dpb_t2->dpbChromaPitch = dpb->planes[1].surface.u.gfx9.surf_pitch;
dynamic_dpb_t2->dpbChromaAlignedHeight = dpb->planes[1].surface.u.gfx9.surf_height;
dynamic_dpb_t2->dpbChromaAlignedSize = dpb->planes[1].surface.u.gfx9.surf_slice_size;
return true;
}
@@ -3174,7 +3284,7 @@ radv_vcn_decode_video(struct radv_cmd_buffer *cmd_buffer, const VkVideoDecodeInf
radv_buffer_get_va(vid->sessionctx.mem->bo) + vid->sessionctx.offset);
send_cmd(cmd_buffer, RDECODE_CMD_MSG_BUFFER, msg_bo, radv_buffer_get_va(msg_bo) + out_offset);
if (vid->dpb_type != DPB_DYNAMIC_TIER_2) {
if (vid->dpb_type < DPB_DYNAMIC_TIER_2) {
if (vid->vk.max_dpb_slots == 0) {
uint64_t addr = radv_buffer_get_va(vid->intra_only_dpb.mem->bo) + vid->intra_only_dpb.offset;
send_cmd(cmd_buffer, RDECODE_CMD_DPB_BUFFER, vid->intra_only_dpb.mem->bo, addr);
+1 -1
View File
@@ -43,7 +43,7 @@ struct radv_video_session {
uint32_t stream_handle;
unsigned stream_type;
bool encode;
enum { DPB_MAX_RES = 0, DPB_DYNAMIC_TIER_1, DPB_DYNAMIC_TIER_2 } dpb_type;
enum { DPB_MAX_RES = 0, DPB_DYNAMIC_TIER_1, DPB_DYNAMIC_TIER_2, DPB_DYNAMIC_TIER_3 } dpb_type;
unsigned db_alignment;
struct radv_vid_mem sessionctx;