mesa/src/amd/vulkan/radv_image_view.c
David Rosca 3474978d52 radv: Fix sampling from image layers of video decode target
Video decode target needs custom height alignment, but tex descriptor
still needs to be set to the original size the image was created with.
This makes the descriptor wrong for layer > 0, so we need to calculate
the layer offset and add it to bo address for this case.

Fixes: 5deb476095 ("radv: align video images internal width/height inside the driver.")
Reviewed-by: Dave Airlie <airlied@redhat.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/32069>
2025-01-03 01:28:07 +00:00

683 lines
29 KiB
C

/*
* Copyright © 2016 Red Hat.
* Copyright © 2016 Bas Nieuwenhuizen
*
* based in part on anv driver which is:
* Copyright © 2015 Intel Corporation
*
* SPDX-License-Identifier: MIT
*/
#include "vk_log.h"
#include "radv_image_view.h"
#include "radv_buffer_view.h"
#include "radv_entrypoints.h"
#include "radv_formats.h"
#include "radv_image.h"
#include "ac_descriptors.h"
#include "ac_formats.h"
#include "gfx10_format_table.h"
static unsigned
radv_tex_dim(VkImageType image_type, VkImageViewType view_type, unsigned nr_layers, unsigned nr_samples,
bool is_storage_image, bool gfx9)
{
if (view_type == VK_IMAGE_VIEW_TYPE_CUBE || view_type == VK_IMAGE_VIEW_TYPE_CUBE_ARRAY)
return is_storage_image ? V_008F1C_SQ_RSRC_IMG_2D_ARRAY : V_008F1C_SQ_RSRC_IMG_CUBE;
/* GFX9 allocates 1D textures as 2D. */
if (gfx9 && image_type == VK_IMAGE_TYPE_1D)
image_type = VK_IMAGE_TYPE_2D;
switch (image_type) {
case VK_IMAGE_TYPE_1D:
return nr_layers > 1 ? V_008F1C_SQ_RSRC_IMG_1D_ARRAY : V_008F1C_SQ_RSRC_IMG_1D;
case VK_IMAGE_TYPE_2D:
if (nr_samples > 1)
return nr_layers > 1 ? V_008F1C_SQ_RSRC_IMG_2D_MSAA_ARRAY : V_008F1C_SQ_RSRC_IMG_2D_MSAA;
else
return nr_layers > 1 ? V_008F1C_SQ_RSRC_IMG_2D_ARRAY : V_008F1C_SQ_RSRC_IMG_2D;
case VK_IMAGE_TYPE_3D:
if (view_type == VK_IMAGE_VIEW_TYPE_3D)
return V_008F1C_SQ_RSRC_IMG_3D;
else
return V_008F1C_SQ_RSRC_IMG_2D_ARRAY;
default:
unreachable("illegal image type");
}
}
void
radv_set_mutable_tex_desc_fields(struct radv_device *device, struct radv_image *image,
const struct legacy_surf_level *base_level_info, unsigned plane_id,
unsigned base_level, unsigned first_level, unsigned block_width, bool is_stencil,
bool is_storage_image, bool disable_compression, bool enable_write_compression,
uint32_t *state, const struct ac_surf_nbc_view *nbc_view, uint64_t offset)
{
struct radv_image_plane *plane = &image->planes[plane_id];
const uint32_t bind_idx = image->disjoint ? plane_id : 0;
struct radv_image_binding *binding = &image->bindings[bind_idx];
uint64_t gpu_address = binding->bo ? radv_image_get_va(image, bind_idx) + offset : 0;
const struct radv_physical_device *pdev = radv_device_physical(device);
const struct ac_mutable_tex_state ac_state = {
.surf = &plane->surface,
.va = gpu_address,
.gfx10 =
{
.write_compress_enable =
radv_dcc_enabled(image, first_level) && is_storage_image && enable_write_compression,
.iterate_256 = radv_image_get_iterate256(device, image),
},
.gfx9 =
{
.nbc_view = nbc_view,
},
.gfx6 =
{
.base_level_info = base_level_info,
.base_level = base_level,
.block_width = block_width,
},
.is_stencil = is_stencil,
.dcc_enabled = !disable_compression && radv_dcc_enabled(image, first_level),
.tc_compat_htile_enabled = !disable_compression && radv_image_is_tc_compat_htile(image),
};
ac_set_mutable_tex_desc_fields(&pdev->info, &ac_state, state);
}
/**
* Build the sampler view descriptor for a texture (GFX10).
*/
static void
gfx10_make_texture_descriptor(struct radv_device *device, struct radv_image *image, bool is_storage_image,
VkImageViewType view_type, VkFormat vk_format, const VkComponentMapping *mapping,
unsigned first_level, unsigned last_level, unsigned first_layer, unsigned last_layer,
unsigned width, unsigned height, unsigned depth, float min_lod, uint32_t *state,
uint32_t *fmask_state, const struct ac_surf_nbc_view *nbc_view,
const VkImageViewSlicedCreateInfoEXT *sliced_3d)
{
const struct radv_physical_device *pdev = radv_device_physical(device);
const bool create_2d_view_of_3d =
(image->vk.create_flags & VK_IMAGE_CREATE_2D_VIEW_COMPATIBLE_BIT_EXT) && view_type == VK_IMAGE_VIEW_TYPE_2D;
enum pipe_format format = radv_format_to_pipe_format(vk_format);
const struct util_format_description *desc;
enum pipe_swizzle swizzle[4];
unsigned array_pitch = 0;
unsigned type;
/* For emulated ETC2 without alpha we need to override the format to a 3-componenent format, so
* that border colors work correctly (alpha forced to 1). Since Vulkan has no such format,
* this uses the Gallium formats to set the description. */
if (image->vk.format == VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK && format == PIPE_FORMAT_R8G8B8A8_UNORM) {
format = PIPE_FORMAT_R8G8B8X8_UNORM;
} else if (image->vk.format == VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK && format == PIPE_FORMAT_R8G8B8A8_SRGB) {
format = PIPE_FORMAT_R8G8B8X8_SRGB;
}
desc = util_format_description(format);
radv_compose_swizzle(desc, mapping, swizzle);
if (create_2d_view_of_3d) {
assert(image->vk.image_type == VK_IMAGE_TYPE_3D);
type = V_008F1C_SQ_RSRC_IMG_3D;
} else {
type = radv_tex_dim(image->vk.image_type, view_type, image->vk.array_layers, image->vk.samples, is_storage_image,
pdev->info.gfx_level == GFX9);
}
if (type == V_008F1C_SQ_RSRC_IMG_1D_ARRAY) {
height = 1;
depth = image->vk.array_layers;
} else if (type == V_008F1C_SQ_RSRC_IMG_2D_ARRAY || type == V_008F1C_SQ_RSRC_IMG_2D_MSAA_ARRAY) {
if (view_type != VK_IMAGE_VIEW_TYPE_3D)
depth = image->vk.array_layers;
} else if (type == V_008F1C_SQ_RSRC_IMG_CUBE)
depth = image->vk.array_layers / 6;
if (create_2d_view_of_3d) {
assert(type == V_008F1C_SQ_RSRC_IMG_3D);
depth = !is_storage_image ? depth : u_minify(depth, first_level);
array_pitch = is_storage_image;
} else if (sliced_3d) {
assert(type == V_008F1C_SQ_RSRC_IMG_3D && is_storage_image);
const unsigned total = u_minify(depth, first_level);
const unsigned slice_count = sliced_3d->sliceCount == VK_REMAINING_3D_SLICES_EXT
? MAX2(1, total - sliced_3d->sliceOffset)
: sliced_3d->sliceCount;
first_layer = sliced_3d->sliceOffset;
depth = sliced_3d->sliceOffset + slice_count;
array_pitch = 1;
}
const struct ac_texture_state tex_state = {
.surf = &image->planes[0].surface,
.format = format,
.img_format = radv_format_to_pipe_format(image->vk.format),
.width = width,
.height = height,
.depth = type == V_008F1C_SQ_RSRC_IMG_3D ? depth - 1 : last_layer,
.type = type,
.swizzle =
{
swizzle[0],
swizzle[1],
swizzle[2],
swizzle[3],
},
.num_samples = image->vk.samples,
.num_storage_samples = image->vk.samples,
.first_level = first_level,
.last_level = last_level,
.num_levels = image->vk.mip_levels,
.first_layer = first_layer,
.last_layer = last_layer,
.min_lod = min_lod,
.gfx10 =
{
.uav3d = array_pitch,
},
.gfx9 =
{
.nbc_view = nbc_view,
},
.dcc_enabled = radv_dcc_enabled(image, first_level),
.tc_compat_htile_enabled = radv_image_is_tc_compat_htile(image),
};
ac_build_texture_descriptor(&pdev->info, &tex_state, &state[0]);
/* Initialize the sampler view for FMASK. */
if (fmask_state) {
if (radv_image_has_fmask(image)) {
assert(image->plane_count == 1);
const struct ac_fmask_state ac_state = {
.surf = &image->planes[0].surface,
.va = radv_image_get_va(image, 0),
.width = width,
.height = height,
.depth = depth,
.type = radv_tex_dim(image->vk.image_type, view_type, image->vk.array_layers, 0, false, false),
.first_layer = first_layer,
.last_layer = last_layer,
.num_samples = image->vk.samples,
.num_storage_samples = image->vk.samples,
.tc_compat_cmask = radv_image_is_tc_compat_cmask(image),
};
ac_build_fmask_descriptor(pdev->info.gfx_level, &ac_state, &fmask_state[0]);
} else
memset(fmask_state, 0, 8 * 4);
}
}
/**
* Build the sampler view descriptor for a texture (SI-GFX9)
*/
static void
gfx6_make_texture_descriptor(struct radv_device *device, struct radv_image *image, bool is_storage_image,
VkImageViewType view_type, VkFormat vk_format, const VkComponentMapping *mapping,
unsigned first_level, unsigned last_level, unsigned first_layer, unsigned last_layer,
unsigned width, unsigned height, unsigned depth, float min_lod, uint32_t *state,
uint32_t *fmask_state)
{
const struct radv_physical_device *pdev = radv_device_physical(device);
const struct radv_instance *instance = radv_physical_device_instance(pdev);
enum pipe_format format = radv_format_to_pipe_format(vk_format);
const bool create_2d_view_of_3d =
(image->vk.create_flags & VK_IMAGE_CREATE_2D_VIEW_COMPATIBLE_BIT_EXT) && view_type == VK_IMAGE_VIEW_TYPE_2D;
const struct util_format_description *desc;
enum pipe_swizzle swizzle[4];
unsigned type;
/* For emulated ETC2 without alpha we need to override the format to a 3-componenent format, so
* that border colors work correctly (alpha forced to 1). Since Vulkan has no such format,
* this uses the Gallium formats to set the description. */
if (image->vk.format == VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK && format == PIPE_FORMAT_R8G8B8A8_UNORM) {
format = PIPE_FORMAT_R8G8B8X8_UNORM;
} else if (image->vk.format == VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK && format == PIPE_FORMAT_R8G8B8A8_SRGB) {
format = PIPE_FORMAT_R8G8B8X8_SRGB;
}
desc = util_format_description(format);
radv_compose_swizzle(desc, mapping, swizzle);
if (pdev->info.gfx_level == GFX9 && create_2d_view_of_3d) {
assert(image->vk.image_type == VK_IMAGE_TYPE_3D);
type = V_008F1C_SQ_RSRC_IMG_3D;
} else {
type = radv_tex_dim(image->vk.image_type, view_type, image->vk.array_layers, image->vk.samples, is_storage_image,
pdev->info.gfx_level == GFX9);
}
if (type == V_008F1C_SQ_RSRC_IMG_1D_ARRAY) {
height = 1;
depth = image->vk.array_layers;
} else if (type == V_008F1C_SQ_RSRC_IMG_2D_ARRAY || type == V_008F1C_SQ_RSRC_IMG_2D_MSAA_ARRAY) {
if (view_type != VK_IMAGE_VIEW_TYPE_3D)
depth = image->vk.array_layers;
} else if (type == V_008F1C_SQ_RSRC_IMG_CUBE)
depth = image->vk.array_layers / 6;
const struct ac_texture_state tex_state = {
.surf = &image->planes[0].surface,
.format = format,
.img_format = radv_format_to_pipe_format(image->vk.format),
.width = width,
.height = height,
.depth = depth,
.type = type,
.swizzle =
{
swizzle[0],
swizzle[1],
swizzle[2],
swizzle[3],
},
.num_samples = image->vk.samples,
.num_storage_samples = image->vk.samples,
.first_level = first_level,
.last_level = last_level,
.num_levels = image->vk.mip_levels,
.first_layer = first_layer,
.last_layer = last_layer,
.min_lod = min_lod,
.dcc_enabled = radv_dcc_enabled(image, first_level),
.tc_compat_htile_enabled = radv_image_is_tc_compat_htile(image),
.aniso_single_level = !instance->drirc.disable_aniso_single_level,
};
ac_build_texture_descriptor(&pdev->info, &tex_state, &state[0]);
/* Initialize the sampler view for FMASK. */
if (fmask_state) {
if (radv_image_has_fmask(image)) {
assert(image->plane_count == 1);
const struct ac_fmask_state ac_fmask_state = {
.surf = &image->planes[0].surface,
.va = radv_image_get_va(image, 0),
.width = width,
.height = height,
.depth = depth,
.type = radv_tex_dim(image->vk.image_type, view_type, image->vk.array_layers, 0, false, false),
.first_layer = first_layer,
.last_layer = last_layer,
.num_samples = image->vk.samples,
.num_storage_samples = image->vk.samples,
.tc_compat_cmask = radv_image_is_tc_compat_cmask(image),
};
ac_build_fmask_descriptor(pdev->info.gfx_level, &ac_fmask_state, &fmask_state[0]);
} else
memset(fmask_state, 0, 8 * 4);
}
}
void
radv_make_texture_descriptor(struct radv_device *device, struct radv_image *image, bool is_storage_image,
VkImageViewType view_type, VkFormat vk_format, const VkComponentMapping *mapping,
unsigned first_level, unsigned last_level, unsigned first_layer, unsigned last_layer,
unsigned width, unsigned height, unsigned depth, float min_lod, uint32_t *state,
uint32_t *fmask_state, const struct ac_surf_nbc_view *nbc_view,
const VkImageViewSlicedCreateInfoEXT *sliced_3d)
{
const struct radv_physical_device *pdev = radv_device_physical(device);
if (pdev->info.gfx_level >= GFX10) {
gfx10_make_texture_descriptor(device, image, is_storage_image, view_type, vk_format, mapping, first_level,
last_level, first_layer, last_layer, width, height, depth, min_lod, state,
fmask_state, nbc_view, sliced_3d);
} else {
gfx6_make_texture_descriptor(device, image, is_storage_image, view_type, vk_format, mapping, first_level,
last_level, first_layer, last_layer, width, height, depth, min_lod, state,
fmask_state);
}
}
static inline void
compute_non_block_compressed_view(struct radv_device *device, const struct radv_image_view *iview,
struct ac_surf_nbc_view *nbc_view)
{
const struct radv_physical_device *pdev = radv_device_physical(device);
const struct radv_image *image = iview->image;
const struct radeon_surf *surf = &image->planes[0].surface;
struct ac_surf_info surf_info = radv_get_ac_surf_info(device, image);
ac_surface_compute_nbc_view(pdev->addrlib, &pdev->info, surf, &surf_info, iview->vk.base_mip_level,
iview->vk.base_array_layer, nbc_view);
}
static void
radv_image_view_make_descriptor(struct radv_image_view *iview, struct radv_device *device, VkFormat vk_format,
const VkComponentMapping *components, bool is_storage_image, bool disable_compression,
bool enable_compression, unsigned plane_id, unsigned descriptor_plane_id,
const VkImageViewSlicedCreateInfoEXT *sliced_3d)
{
const struct radv_physical_device *pdev = radv_device_physical(device);
struct radv_image *image = iview->image;
struct radv_image_plane *plane = &image->planes[plane_id];
bool is_stencil = iview->vk.aspects == VK_IMAGE_ASPECT_STENCIL_BIT;
unsigned first_layer = iview->vk.base_array_layer;
uint32_t blk_w;
union radv_descriptor *descriptor;
uint32_t hw_level = iview->vk.base_mip_level;
bool force_zero_base_mip = false;
uint64_t offset = 0;
if (is_storage_image) {
descriptor = &iview->storage_descriptor;
} else {
descriptor = &iview->descriptor;
}
assert(vk_format_get_plane_count(vk_format) == 1);
assert(plane->surface.blk_w % vk_format_get_blockwidth(plane->format) == 0);
blk_w = plane->surface.blk_w / vk_format_get_blockwidth(plane->format) * vk_format_get_blockwidth(vk_format);
VkExtent3D extent = {
.width = iview->extent.width,
.height = iview->extent.height,
.depth = iview->extent.depth,
};
if (pdev->info.gfx_level >= GFX9) {
if (iview->nbc_view.valid) {
hw_level = iview->nbc_view.level;
/* Clear the base array layer because addrlib adds it as part of the base addr offset. */
first_layer = 0;
} else {
/* Video decode target uses custom height alignment. */
if (image->vk.usage & VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR) {
assert(image->planes[plane_id].surface.u.gfx9.swizzle_mode == 0);
offset += first_layer * image->planes[plane_id].surface.u.gfx9.surf_slice_size;
first_layer = 0;
}
}
} else {
/* On GFX6-8, there are some cases where the view must use mip0 and minified image sizes:
* - storage descriptors
* - block compressed images
* - depth view of a depth/stencil image (ie. depth/stencil pitch adjustments)
* - 2d view of a 3d image
*/
if (is_storage_image) {
force_zero_base_mip = true;
} else if (vk_format_is_block_compressed(image->planes[plane_id].format)) {
force_zero_base_mip = true;
} else if (iview->vk.aspects == VK_IMAGE_ASPECT_DEPTH_BIT &&
(iview->image->vk.aspects == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT))) {
force_zero_base_mip = true;
} else if (iview->image->vk.create_flags & VK_IMAGE_CREATE_2D_VIEW_COMPATIBLE_BIT_EXT &&
iview->vk.view_type == VK_IMAGE_VIEW_TYPE_2D) {
force_zero_base_mip = true;
}
if (force_zero_base_mip) {
hw_level = 0;
} else {
extent.width = image->vk.extent.width;
extent.height = image->vk.extent.height;
extent.depth = image->vk.extent.depth;
}
/* Video decode target uses custom height alignment. */
if (image->vk.usage & VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR) {
offset += first_layer * image->planes[plane_id].surface.u.legacy.level[0].slice_size_dw * 4;
first_layer = 0;
}
}
radv_make_texture_descriptor(
device, image, is_storage_image, iview->vk.view_type, vk_format, components, hw_level,
hw_level + iview->vk.level_count - 1, first_layer, iview->vk.base_array_layer + iview->vk.layer_count - 1,
vk_format_get_plane_width(image->vk.format, plane_id, extent.width),
vk_format_get_plane_height(image->vk.format, plane_id, extent.height), extent.depth, iview->vk.min_lod,
descriptor->plane_descriptors[descriptor_plane_id],
descriptor_plane_id || is_storage_image ? NULL : descriptor->fmask_descriptor, &iview->nbc_view, sliced_3d);
const struct legacy_surf_level *base_level_info = NULL;
if (pdev->info.gfx_level <= GFX8) {
if (is_stencil)
base_level_info =
&plane->surface.u.legacy.zs.stencil_level[force_zero_base_mip ? iview->vk.base_mip_level : 0];
else
base_level_info = &plane->surface.u.legacy.level[force_zero_base_mip ? iview->vk.base_mip_level : 0];
}
bool enable_write_compression = radv_image_use_dcc_image_stores(device, image);
if (is_storage_image && !(enable_write_compression || enable_compression))
disable_compression = true;
radv_set_mutable_tex_desc_fields(device, image, base_level_info, plane_id,
force_zero_base_mip ? iview->vk.base_mip_level : 0, iview->vk.base_mip_level, blk_w,
is_stencil, is_storage_image, disable_compression, enable_write_compression,
descriptor->plane_descriptors[descriptor_plane_id], &iview->nbc_view, offset);
}
/**
* Determine if the given image view can be fast cleared.
*/
static bool
radv_image_view_can_fast_clear(const struct radv_device *device, const struct radv_image_view *iview)
{
struct radv_image *image;
if (!iview)
return false;
image = iview->image;
/* Only fast clear if the image itself can be fast cleared. */
if (!radv_image_can_fast_clear(device, image))
return false;
/* Only fast clear if all layers are bound. */
if (iview->vk.base_array_layer > 0 || iview->vk.layer_count != image->vk.array_layers)
return false;
/* Only fast clear if the view covers the whole image. */
if (!radv_image_extent_compare(image, &iview->extent))
return false;
return true;
}
void
radv_image_view_init(struct radv_image_view *iview, struct radv_device *device,
const VkImageViewCreateInfo *pCreateInfo,
const struct radv_image_view_extra_create_info *extra_create_info)
{
VK_FROM_HANDLE(radv_image, image, pCreateInfo->image);
const struct radv_physical_device *pdev = radv_device_physical(device);
uint32_t plane_count = 1;
const struct VkImageViewSlicedCreateInfoEXT *sliced_3d =
vk_find_struct_const(pCreateInfo->pNext, IMAGE_VIEW_SLICED_CREATE_INFO_EXT);
bool from_client = extra_create_info && extra_create_info->from_client;
vk_image_view_init(&device->vk, &iview->vk, !from_client, pCreateInfo);
iview->image = image;
iview->plane_id = radv_plane_from_aspect(pCreateInfo->subresourceRange.aspectMask);
iview->nbc_view.valid = false;
/* If the image has an Android external format, pCreateInfo->format will be
* VK_FORMAT_UNDEFINED. */
if (iview->vk.format == VK_FORMAT_UNDEFINED) {
iview->vk.format = image->vk.format;
iview->vk.view_format = image->vk.format;
}
/* Split out the right aspect. Note that for internal meta code we sometimes
* use an equivalent color format for the aspect so we first have to check
* if we actually got depth/stencil formats. */
if (iview->vk.aspects == VK_IMAGE_ASPECT_STENCIL_BIT) {
if (vk_format_has_stencil(iview->vk.view_format))
iview->vk.view_format = vk_format_stencil_only(iview->vk.view_format);
} else if (iview->vk.aspects == VK_IMAGE_ASPECT_DEPTH_BIT) {
if (vk_format_has_depth(iview->vk.view_format))
iview->vk.view_format = vk_format_depth_only(iview->vk.view_format);
}
if (vk_format_get_plane_count(image->vk.format) > 1 &&
pCreateInfo->subresourceRange.aspectMask == VK_IMAGE_ASPECT_COLOR_BIT) {
plane_count = vk_format_get_plane_count(iview->vk.format);
}
/* when the view format is emulated, redirect the view to the hidden plane 1 */
if (radv_is_format_emulated(pdev, iview->vk.format)) {
assert(radv_is_format_emulated(pdev, image->vk.format));
iview->plane_id = 1;
iview->vk.view_format = image->planes[iview->plane_id].format;
iview->vk.format = image->planes[iview->plane_id].format;
plane_count = 1;
}
if (pdev->info.gfx_level >= GFX9) {
iview->extent = (VkExtent3D){
.width = image->vk.extent.width,
.height = image->vk.extent.height,
.depth = image->vk.extent.depth,
};
} else {
/* On GFX6-8, CB/DS surfaces use minified images sizes because the mip level can't be
* specified in registers.
*/
iview->extent = vk_image_mip_level_extent(&image->vk, iview->vk.base_mip_level);
}
if (iview->vk.format != image->planes[iview->plane_id].format) {
const struct radv_image_plane *plane = &image->planes[iview->plane_id];
unsigned view_bw = vk_format_get_blockwidth(iview->vk.format);
unsigned view_bh = vk_format_get_blockheight(iview->vk.format);
unsigned plane_bw = vk_format_get_blockwidth(plane->format);
unsigned plane_bh = vk_format_get_blockheight(plane->format);
iview->extent.width = DIV_ROUND_UP(iview->extent.width * view_bw, plane_bw);
iview->extent.height = DIV_ROUND_UP(iview->extent.height * view_bh, plane_bh);
/* Comment ported from amdvlk -
* If we have the following image:
* Uncompressed pixels Compressed block sizes (4x4)
* mip0: 22 x 22 6 x 6
* mip1: 11 x 11 3 x 3
* mip2: 5 x 5 2 x 2
* mip3: 2 x 2 1 x 1
* mip4: 1 x 1 1 x 1
*
* On GFX9 the descriptor is always programmed with the WIDTH and HEIGHT of the base level and
* the HW is calculating the degradation of the block sizes down the mip-chain as follows
* (straight-up divide-by-two integer math): mip0: 6x6 mip1: 3x3 mip2: 1x1 mip3: 1x1
*
* This means that mip2 will be missing texels.
*
* Fix this by calculating the base mip's width and height, then convert
* that, and round it back up to get the level 0 size. Clamp the
* converted size between the original values, and the physical extent
* of the base mipmap.
*
* On GFX10 we have to take care to not go over the physical extent
* of the base mipmap as otherwise the GPU computes a different layout.
* Note that the GPU does use the same base-mip dimensions for both a
* block compatible format and the compressed format, so even if we take
* the plain converted dimensions the physical layout is correct.
*/
if (pdev->info.gfx_level >= GFX9 && vk_format_is_block_compressed(plane->format) &&
!vk_format_is_block_compressed(iview->vk.format)) {
/* If we have multiple levels in the view we should ideally take the last level,
* but the mip calculation has a max(..., 1) so walking back to the base mip in an
* useful way is hard. */
if (iview->vk.level_count > 1) {
iview->extent.width = plane->surface.u.gfx9.base_mip_width;
iview->extent.height = plane->surface.u.gfx9.base_mip_height;
} else {
unsigned lvl_width = u_minify(image->vk.extent.width, iview->vk.base_mip_level);
unsigned lvl_height = u_minify(image->vk.extent.height, iview->vk.base_mip_level);
lvl_width = DIV_ROUND_UP(lvl_width * view_bw, plane_bw);
lvl_height = DIV_ROUND_UP(lvl_height * view_bh, plane_bh);
iview->extent.width =
CLAMP(lvl_width << iview->vk.base_mip_level, iview->extent.width, plane->surface.u.gfx9.base_mip_width);
iview->extent.height = CLAMP(lvl_height << iview->vk.base_mip_level, iview->extent.height,
plane->surface.u.gfx9.base_mip_height);
/* If the hardware-computed extent is still be too small, on GFX10
* we can attempt another workaround provided by addrlib that
* changes the descriptor's base level, and adjusts the address and
* extents accordingly.
*/
if (pdev->info.gfx_level >= GFX10 &&
(u_minify(iview->extent.width, iview->vk.base_mip_level) < lvl_width ||
u_minify(iview->extent.height, iview->vk.base_mip_level) < lvl_height) &&
iview->vk.layer_count == 1) {
compute_non_block_compressed_view(device, iview, &iview->nbc_view);
if (iview->nbc_view.valid) {
iview->extent.width = iview->nbc_view.width;
iview->extent.height = iview->nbc_view.height;
}
}
}
}
}
iview->support_fast_clear = radv_image_view_can_fast_clear(device, iview);
iview->disable_dcc_mrt = extra_create_info ? extra_create_info->disable_dcc_mrt : false;
bool disable_compression = extra_create_info ? extra_create_info->disable_compression : false;
bool enable_compression = extra_create_info ? extra_create_info->enable_compression : false;
for (unsigned i = 0; i < plane_count; ++i) {
VkFormat format = vk_format_get_plane_format(iview->vk.view_format, i);
radv_image_view_make_descriptor(iview, device, format, &pCreateInfo->components, false, disable_compression,
enable_compression, iview->plane_id + i, i, NULL);
radv_image_view_make_descriptor(iview, device, format, &pCreateInfo->components, true, disable_compression,
enable_compression, iview->plane_id + i, i, sliced_3d);
}
}
void
radv_image_view_finish(struct radv_image_view *iview)
{
vk_image_view_finish(&iview->vk);
}
VKAPI_ATTR VkResult VKAPI_CALL
radv_CreateImageView(VkDevice _device, const VkImageViewCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator, VkImageView *pView)
{
VK_FROM_HANDLE(radv_device, device, _device);
struct radv_image_view *view;
view = vk_alloc2(&device->vk.alloc, pAllocator, sizeof(*view), 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
if (view == NULL)
return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY);
radv_image_view_init(view, device, pCreateInfo, &(struct radv_image_view_extra_create_info){.from_client = true});
*pView = radv_image_view_to_handle(view);
return VK_SUCCESS;
}
VKAPI_ATTR void VKAPI_CALL
radv_DestroyImageView(VkDevice _device, VkImageView _iview, const VkAllocationCallbacks *pAllocator)
{
VK_FROM_HANDLE(radv_device, device, _device);
VK_FROM_HANDLE(radv_image_view, iview, _iview);
if (!iview)
return;
radv_image_view_finish(iview);
vk_free2(&device->vk.alloc, pAllocator, iview);
}