anv: Add support for creating layered surfaces for video encode/decode

Layered surfaces (array textures) with video encode/decode usage bits
will have their slices aligned to make them addressable to the media
engine. Multi-planar layered surfaces will be stored with their slices
interleaved so that a relative offset can be programmed between the
gamma and chroma slices.

Reviewed-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/35651>
This commit is contained in:
Calder Young 2025-06-19 23:06:12 -07:00 committed by Marge Bot
parent 73608eb8b7
commit 3fb25cc78a
2 changed files with 210 additions and 6 deletions

View file

@ -255,8 +255,8 @@ anv_image_choose_isl_surf_usage(struct anv_physical_device *device,
ISL_SURF_USAGE_DISABLE_AUX_BIT; ISL_SURF_USAGE_DISABLE_AUX_BIT;
if (vk_usage & VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR || if (vk_usage & VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR ||
vk_usage & VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR ||
vk_usage & VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR || vk_usage & VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR ||
vk_usage & VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR ||
vk_usage & VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR) vk_usage & VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR)
isl_usage |= ISL_SURF_USAGE_VIDEO_DECODE_BIT; isl_usage |= ISL_SURF_USAGE_VIDEO_DECODE_BIT;
@ -907,17 +907,35 @@ add_video_buffers(struct anv_device *device,
if (size == 0) if (size == 0)
return VK_SUCCESS; return VK_SUCCESS;
if (image->vk.array_layers > 1) {
image->vid_dmv_top_surface_pitch_B = align(size, 64);
size = image->vid_dmv_top_surface_pitch_B *
(image->vk.array_layers - 1) + size;
} else {
image->vid_dmv_top_surface_pitch_B = size;
}
ok = image_binding_grow(device, image, ANV_IMAGE_MEMORY_BINDING_PRIVATE, ok = image_binding_grow(device, image, ANV_IMAGE_MEMORY_BINDING_PRIVATE,
ANV_OFFSET_IMPLICIT, size, 65536, &image->vid_dmv_top_surface); ANV_OFFSET_IMPLICIT, size, 64, &image->vid_dmv_top_surface);
if (ok != VK_SUCCESS) if (ok != VK_SUCCESS)
return ok; return ok;
size = av1_cdf_max_num_bytes;
if (image->vk.array_layers > 1) {
image->av1_cdf_table_pitch_B = align(size, 64);
size = image->av1_cdf_table_pitch_B *
(image->vk.array_layers - 1) + size;
} else {
image->av1_cdf_table_pitch_B = size;
}
/* Doesn't work for av1 without provided profiles */ /* Doesn't work for av1 without provided profiles */
if (!independent_profile) { if (!independent_profile) {
for (unsigned i = 0; i < profile_list->profileCount; i++) { for (unsigned i = 0; i < profile_list->profileCount; i++) {
if (profile_list->pProfiles[i].videoCodecOperation == VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR) { if (profile_list->pProfiles[i].videoCodecOperation == VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR) {
ok = image_binding_grow(device, image, ANV_IMAGE_MEMORY_BINDING_PRIVATE, ok = image_binding_grow(device, image, ANV_IMAGE_MEMORY_BINDING_PRIVATE,
ANV_OFFSET_IMPLICIT, av1_cdf_max_num_bytes, 4096, &image->av1_cdf_table); ANV_OFFSET_IMPLICIT, size, 4096, &image->av1_cdf_table);
} }
} }
} else { } else {
@ -925,7 +943,7 @@ add_video_buffers(struct anv_device *device,
* tables all the time. * tables all the time.
*/ */
ok = image_binding_grow(device, image, ANV_IMAGE_MEMORY_BINDING_PRIVATE, ok = image_binding_grow(device, image, ANV_IMAGE_MEMORY_BINDING_PRIVATE,
ANV_OFFSET_IMPLICIT, av1_cdf_max_num_bytes, 4096, &image->av1_cdf_table); ANV_OFFSET_IMPLICIT, size, 4096, &image->av1_cdf_table);
} }
return ok; return ok;
@ -1027,7 +1045,6 @@ check_memory_range_s(const struct check_memory_range_params *p)
&p->accum_ranges[p->expect_binding]; &p->accum_ranges[p->expect_binding];
assert(test_range->binding == p->expect_binding); assert(test_range->binding == p->expect_binding);
assert(test_range->offset >= memory_range_end(*accum_range));
assert(memory_range_is_aligned(*test_range)); assert(memory_range_is_aligned(*test_range));
if (p->test_surface) { if (p->test_surface) {
@ -1072,7 +1089,8 @@ check_memory_bindings(const struct anv_device *device,
/* Aliasing is incompatible with the private binding because it does not /* Aliasing is incompatible with the private binding because it does not
* live in a VkDeviceMemory. The exception is either swapchain images or * live in a VkDeviceMemory. The exception is either swapchain images or
* that the private binding is for a video motion vector buffer. * that the private binding is for a video motion vector buffer or a
* video CDF table.
*/ */
assert(!(image->vk.create_flags & VK_IMAGE_CREATE_ALIAS_BIT) || assert(!(image->vk.create_flags & VK_IMAGE_CREATE_ALIAS_BIT) ||
image->from_wsi || image->from_wsi ||
@ -1217,6 +1235,112 @@ check_drm_format_mod(const struct anv_device *device,
return VK_SUCCESS; return VK_SUCCESS;
} }
/**
* Use when the app does not provide
* VkImageDrmFormatModifierExplicitCreateInfoEXT and
* creates a multi-planar layered surface that needs
* its slices to be addressable to the media engine.
*/
static VkResult MUST_CHECK
add_all_surfaces_implicit_interleaved_arrays_layout(
struct anv_device *device,
struct anv_image *image,
const VkImageFormatListCreateInfo *format_list_info,
unsigned num_aspects,
const VkImageAspectFlagBits *aspects,
isl_tiling_flags_t isl_tiling_flags,
isl_surf_usage_flags_t isl_extra_usage_flags)
{
VkResult result;
const struct vk_format_ycbcr_info *ycbcr_info =
vk_format_get_ycbcr_info(image->vk.format);
assert(num_aspects <= 3);
uint32_t surfs_offsets[3];
struct isl_surf *surfs[3];
struct isl_surf_init_info infos[3];
struct anv_format_plane plane_format[3];
/* Configure the surfaces that need their slices to be interleaved */
for (unsigned i = 0; i < num_aspects; i++) {
VkImageAspectFlagBits aspect = aspects[i];
const uint32_t plane = anv_image_aspect_to_plane(image, aspect);
plane_format[i] = anv_get_format_plane(device->physical, image->vk.format,
plane, image->vk.tiling);
assert(plane_format[i].isl_format != ISL_FORMAT_UNSUPPORTED);
VkImageUsageFlags vk_usage = vk_image_usage(&image->vk, aspect);
isl_surf_usage_flags_t isl_usage =
anv_image_choose_isl_surf_usage(device->physical,
image->vk.format,
image->vk.create_flags, vk_usage,
isl_extra_usage_flags, aspect,
image->vk.compr_flags);
uint32_t width = image->vk.extent.width;
uint32_t height = image->vk.extent.height;
if (ycbcr_info) {
assert(plane < ycbcr_info->n_planes);
width /= ycbcr_info->planes[plane].denominator_scales[0];
height /= ycbcr_info->planes[plane].denominator_scales[1];
}
surfs[i] = &image->planes[plane].primary_surface.isl;
infos[i] = (struct isl_surf_init_info) {
.dim = vk_to_isl_surf_dim[image->vk.image_type],
.format = plane_format[i].isl_format,
.width = width,
.height = height,
.depth = image->vk.extent.depth,
.levels = image->vk.mip_levels,
.array_len = image->vk.array_layers,
.samples = image->vk.samples,
.min_alignment_B = 0,
.row_pitch_B = 0,
.usage = isl_usage,
.tiling_flags = isl_tiling_flags
};
}
/* Calculate the pitches and offsets to interleave the surfaces
* with the correct alignments for the media engine
*/
if (!isl_surf_init_interleaved_arrays(&device->isl_dev, num_aspects, surfs,
surfs_offsets, infos))
return vk_errorf(device, VK_ERROR_UNKNOWN, "Unable to create interleaved arrayed image");
/* Add the interleaved surfaces to the image */
for (unsigned i = 0; i < num_aspects; i++) {
VkImageAspectFlagBits aspect = aspects[i];
const uint32_t plane = anv_image_aspect_to_plane(image, aspect);
image->planes[plane].aux_usage = ISL_AUX_USAGE_NONE;
result = add_surface(device, image,
&image->planes[plane].primary_surface,
ANV_IMAGE_MEMORY_BINDING_PLANE_0 + plane,
surfs_offsets[i]);
if (result != VK_SUCCESS)
return result;
}
/* Add any required aux surfaces to the image */
for (unsigned i = 0; i < num_aspects; i++) {
const uint32_t plane = anv_image_aspect_to_plane(image, aspects[i]);
result = add_aux_surface_if_supported(device, image, plane,
plane_format[i],
format_list_info,
ANV_OFFSET_IMPLICIT, 0,
ANV_OFFSET_IMPLICIT);
if (result != VK_SUCCESS)
return result;
}
return VK_SUCCESS;
}
/** /**
* Use when the app does not provide * Use when the app does not provide
* VkImageDrmFormatModifierExplicitCreateInfoEXT. * VkImageDrmFormatModifierExplicitCreateInfoEXT.
@ -1258,6 +1382,22 @@ add_all_surfaces_implicit_layout(
aspects[2] = VK_IMAGE_ASPECT_PLANE_1_BIT; aspects[2] = VK_IMAGE_ASPECT_PLANE_1_BIT;
} }
/* If the image is a multi-planar layered surface and will be used for
* video coding, we need to apply a special layout where the slices
* of each plane are interleaved to make them addressable to the
* media engine.
*/
if (ycbcr_info && num_aspects > 1 && image->vk.array_layers > 1 &&
image->vk.usage & (VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR |
VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR |
VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR |
VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR)) {
assert(stride == 0);
return add_all_surfaces_implicit_interleaved_arrays_layout(device,
image, format_list_info, num_aspects, aspects,
isl_tiling_flags, isl_extra_usage_flags);
}
for (unsigned i = 0; i < num_aspects; i++) { for (unsigned i = 0; i < num_aspects; i++) {
VkImageAspectFlagBits aspect = aspects[i]; VkImageAspectFlagBits aspect = aspects[i];
const uint32_t plane = anv_image_aspect_to_plane(image, aspect); const uint32_t plane = anv_image_aspect_to_plane(image, aspect);

View file

@ -5847,6 +5847,10 @@ struct anv_image {
} aux_tt; } aux_tt;
} planes[3]; } planes[3];
/* Array pitch of video coding private surfaces */
uint32_t vid_dmv_top_surface_pitch_B;
uint32_t av1_cdf_table_pitch_B;
struct anv_image_memory_range vid_dmv_top_surface; struct anv_image_memory_range vid_dmv_top_surface;
/* Link in the anv_device.image_private_objects list */ /* Link in the anv_device.image_private_objects list */
@ -6760,6 +6764,66 @@ uint32_t anv_video_get_image_mv_size(struct anv_device *device,
struct anv_image *image, struct anv_image *image,
const struct VkVideoProfileListInfoKHR *profile_list); const struct VkVideoProfileListInfoKHR *profile_list);
static inline struct anv_address MUST_CHECK
anv_image_dpb_address(const struct anv_image_view *iv,
uint32_t arrayLayer)
{
assert(iv->vk.base_mip_level == 0);
assert(iv->vk.layer_count > arrayLayer);
struct anv_address addr =
anv_image_address(iv->image, &iv->image->planes[0].primary_surface.memory_range);
if (anv_address_is_null(addr))
return addr;
/* Will assert if the intra-tile offsets are not zero */
uint64_t offset_B;
isl_surf_get_image_offset_B_tile_sa(&iv->image->planes[0].primary_surface.isl,
0,
iv->vk.base_array_layer + arrayLayer,
0,
&offset_B,
NULL,
NULL);
return anv_address_add(addr, offset_B);
}
static inline struct anv_address MUST_CHECK
anv_image_dmv_top_address(const struct anv_image_view *iv,
uint32_t arrayLayer)
{
assert(iv->vk.base_mip_level == 0);
assert(iv->vk.layer_count > arrayLayer);
struct anv_address addr = anv_image_address(iv->image,
&iv->image->vid_dmv_top_surface);
if (anv_address_is_null(addr))
return addr;
return anv_address_add(addr, iv->image->vid_dmv_top_surface_pitch_B *
(iv->vk.base_array_layer + arrayLayer));
}
static inline struct anv_address MUST_CHECK
anv_image_av1_table_address(const struct anv_image_view *iv,
uint32_t arrayLayer)
{
assert(iv->vk.base_mip_level == 0);
assert(iv->vk.layer_count > arrayLayer);
struct anv_address addr = anv_image_address(iv->image,
&iv->image->av1_cdf_table);
if (anv_address_is_null(addr))
return addr;
return anv_address_add(addr, iv->image->av1_cdf_table_pitch_B *
(iv->vk.base_array_layer + arrayLayer));
}
void void
anv_dump_pipe_bits(enum anv_pipe_bits bits, FILE *f); anv_dump_pipe_bits(enum anv_pipe_bits bits, FILE *f);