From 7421b3852145663343bc86699d38f5e7698c3989 Mon Sep 17 00:00:00 2001 From: Caterina Shablia Date: Thu, 20 Nov 2025 17:39:04 +0100 Subject: [PATCH] panvk: sparse partially-resident image -related queries Reviewed-by: Boris Brezillon Part-of: --- src/panfrost/vulkan/panvk_image.c | 140 +++++++++++++++++++- src/panfrost/vulkan/panvk_image.h | 16 +++ src/panfrost/vulkan/panvk_physical_device.c | 96 ++++++++++++-- 3 files changed, 233 insertions(+), 19 deletions(-) diff --git a/src/panfrost/vulkan/panvk_image.c b/src/panfrost/vulkan/panvk_image.c index 46837534c13..09853d6fb55 100644 --- a/src/panfrost/vulkan/panvk_image.c +++ b/src/panfrost/vulkan/panvk_image.c @@ -796,12 +796,24 @@ panvk_GetDeviceImageSubresourceLayoutKHR( vk_image_finish(&image.vk); } +static uint64_t +panvk_image_get_sparse_binding_granularity(struct panvk_image *image) +{ + struct panvk_device *dev = to_panvk_device(image->vk.base.device); + + assert(image->vk.create_flags & VK_IMAGE_CREATE_SPARSE_BINDING_BIT); + + if (image->vk.create_flags & VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT) + return panvk_get_sparse_block_desc(image->vk.image_type, image->vk.format).size_B; + + return panvk_get_gpu_page_size(dev); +} + VKAPI_ATTR void VKAPI_CALL panvk_GetImageMemoryRequirements2(VkDevice device, const VkImageMemoryRequirementsInfo2 *pInfo, VkMemoryRequirements2 *pMemoryRequirements) { - VK_FROM_HANDLE(panvk_device, dev, device); VK_FROM_HANDLE(panvk_image, image, pInfo->image); /* For sparse resources alignment specifies binding granularity, rather than @@ -810,7 +822,7 @@ panvk_GetImageMemoryRequirements2(VkDevice device, */ const uint64_t alignment = image->vk.create_flags & VK_IMAGE_CREATE_SPARSE_BINDING_BIT - ? panvk_get_gpu_page_size(dev) + ? panvk_image_get_sparse_binding_granularity(image) : 4096; const VkImagePlaneMemoryRequirementsInfo *plane_info = vk_find_struct_const(pInfo->pNext, IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO); @@ -823,7 +835,7 @@ panvk_GetImageMemoryRequirements2(VkDevice device, panvk_image_get_total_size(image); const uint64_t size = image->vk.create_flags & VK_IMAGE_CREATE_SPARSE_BINDING_BIT - ? align64(size_non_sparse, panvk_get_gpu_page_size(dev)) + ? align64(size_non_sparse, alignment) : size_non_sparse; pMemoryRequirements->memoryRequirements.memoryTypeBits = 1; @@ -865,14 +877,117 @@ panvk_GetDeviceImageMemoryRequirements(VkDevice device, vk_image_finish(&image.vk); } +/* See Vulkan spec, 35.4.3. Standard Sparse Image Block Shapes for details */ +enum { + STANDARD_SPARSE_BLOCK_SIZE_B = 65536, +}; + +/* Sparse block extents, in texel blocks, single sample. + * Indexed by log2(texel block size in bytes). + * See Vulkan spec, 35.4.3. Standard Sparse Image Block Shapes for details. */ +static struct VkExtent3D standard_sparse_2d_blocks[] = { + /* 1 */ {256, 256, 1}, + /* 2 */ {256, 128, 1}, + /* 4 */ {128, 128, 1}, + /* 8 */ {128, 64, 1}, + /* 16 */ {64, 64, 1}, +}; + +struct panvk_sparse_block_desc +panvk_get_sparse_block_desc(VkImageType type, VkFormat format) +{ + const struct util_format_description *fmt_desc = vk_format_description(format); + + uint32_t texel_block_size_B = fmt_desc->block.bits / 8; + + switch (type) { + case VK_IMAGE_TYPE_2D: { + if (!util_is_power_of_two_nonzero(texel_block_size_B)) + break; + + uint32_t texel_block_size_B_log2 = util_logbase2(texel_block_size_B); + if (texel_block_size_B_log2 >= ARRAY_SIZE(standard_sparse_2d_blocks)) + break; + struct VkExtent3D extent = standard_sparse_2d_blocks[texel_block_size_B_log2]; + + assert(extent.width * extent.height * extent.depth * texel_block_size_B == STANDARD_SPARSE_BLOCK_SIZE_B); + + extent.width *= fmt_desc->block.width; + extent.height *= fmt_desc->block.height; + extent.depth *= fmt_desc->block.depth; + + return (struct panvk_sparse_block_desc){ + .extent = extent, + .size_B = STANDARD_SPARSE_BLOCK_SIZE_B, + .standard = true, + }; + } + + default: + break; + } + + return (struct panvk_sparse_block_desc){}; +} + +VkSparseImageFormatProperties +panvk_get_sparse_image_fmt_props(VkImageType type, VkFormat format) +{ + struct panvk_sparse_block_desc sblock_desc = panvk_get_sparse_block_desc(type, format); + if (!panvk_sparse_block_is_valid(sblock_desc)) + return (VkSparseImageFormatProperties){}; + + VkSparseImageFormatProperties fmt_props = {}; + fmt_props.aspectMask = vk_format_aspects(format); + fmt_props.imageGranularity = sblock_desc.extent; + if (!sblock_desc.standard) + fmt_props.flags |= VK_SPARSE_IMAGE_FORMAT_NONSTANDARD_BLOCK_SIZE_BIT; + return fmt_props; +} + VKAPI_ATTR void VKAPI_CALL panvk_GetImageSparseMemoryRequirements2( VkDevice device, const VkImageSparseMemoryRequirementsInfo2 *pInfo, uint32_t *pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2 *pSparseMemoryRequirements) { - /* Sparse images are not yet supported. */ - *pSparseMemoryRequirementCount = 0; + VK_FROM_HANDLE(panvk_image, image, pInfo->image); + VK_OUTARRAY_MAKE_TYPED(VkSparseImageMemoryRequirements2, out, pSparseMemoryRequirements, + pSparseMemoryRequirementCount); + + if (!(image->vk.create_flags & VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT)) + return; + + /* We only support single-plane images right now. See + * https://gitlab.freedesktop.org/panfrost/mesa/-/issues/243 details. */ + unsigned plane_idx = 0; + + struct panvk_sparse_block_desc sblock = panvk_get_sparse_block_desc(image->vk.image_type, image->vk.format); + assert(panvk_sparse_block_is_valid(sblock)); + + struct panvk_image_plane *plane = &image->planes[plane_idx]; + + unsigned mip_tail_first_lod = 0; + uint64_t mip_tail_begin = 0; + for (unsigned level = 0; level < plane->image.props.nr_slices; level++) { + uint64_t offset = plane->plane.layout.slices[level].offset_B; + if (!util_is_aligned(offset, sblock.size_B)) + break; + mip_tail_first_lod = level; + mip_tail_begin = offset; + } + + uint64_t mip_tail_end = plane->plane.layout.array_stride_B; + + vk_outarray_append_typed(VkSparseImageMemoryRequirements2, &out, p) { + p->memoryRequirements = (VkSparseImageMemoryRequirements){ + .formatProperties = panvk_get_sparse_image_fmt_props(image->vk.image_type, image->vk.format), + .imageMipTailFirstLod = mip_tail_first_lod, + .imageMipTailSize = mip_tail_end - mip_tail_begin, + .imageMipTailOffset = mip_tail_begin, + .imageMipTailStride = plane->plane.layout.array_stride_B, + }; + } } VKAPI_ATTR void VKAPI_CALL @@ -881,8 +996,19 @@ panvk_GetDeviceImageSparseMemoryRequirements(VkDevice device, uint32_t *pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2 *pSparseMemoryRequirements) { - /* Sparse images are not yet supported. */ - *pSparseMemoryRequirementCount = 0; + VK_FROM_HANDLE(panvk_device, dev, device); + + struct panvk_image image; + vk_image_init(&dev->vk, &image.vk, pInfo->pCreateInfo); + panvk_image_init(&image, pInfo->pCreateInfo); + + VkImageSparseMemoryRequirementsInfo2 info2 = { + .sType = VK_STRUCTURE_TYPE_IMAGE_SPARSE_MEMORY_REQUIREMENTS_INFO_2, + .image = panvk_image_to_handle(&image), + }; + panvk_GetImageSparseMemoryRequirements2(device, &info2, + pSparseMemoryRequirementCount, pSparseMemoryRequirements); + vk_image_finish(&image.vk); } static VkResult diff --git a/src/panfrost/vulkan/panvk_image.h b/src/panfrost/vulkan/panvk_image.h index 15d6a4b3ba6..4afa97fa0b0 100644 --- a/src/panfrost/vulkan/panvk_image.h +++ b/src/panfrost/vulkan/panvk_image.h @@ -105,4 +105,20 @@ panvk_image_stencil_only_pfmt(const struct panvk_image *image) VkResult panvk_image_init(struct panvk_image *image, const VkImageCreateInfo *pCreateInfo); +struct panvk_sparse_block_desc { + struct VkExtent3D extent; + uint64_t size_B; + + /* Whether this is a standard (as defined by Vulkan) sparse block size, for + * the given arguments */ + bool standard; +}; + +struct panvk_sparse_block_desc panvk_get_sparse_block_desc(VkImageType type, VkFormat format); + +static inline bool +panvk_sparse_block_is_valid(struct panvk_sparse_block_desc desc) { return desc.size_B > 0; } + +VkSparseImageFormatProperties panvk_get_sparse_image_fmt_props(VkImageType type, VkFormat format); + #endif diff --git a/src/panfrost/vulkan/panvk_physical_device.c b/src/panfrost/vulkan/panvk_physical_device.c index 43782e8ac56..1ee21fa0fa2 100644 --- a/src/panfrost/vulkan/panvk_physical_device.c +++ b/src/panfrost/vulkan/panvk_physical_device.c @@ -916,6 +916,59 @@ get_image_format_properties(struct panvk_physical_device *physical_device, const struct vk_format_ycbcr_info *ycbcr_info = vk_format_get_ycbcr_info(info->format); + if (info->flags & VK_IMAGE_CREATE_SPARSE_BINDING_BIT) { + if (!physical_device->vk.supported_features.sparseBinding) + goto unsupported; + + /* + * Sparse only manipulates device mappings and we implement host copies on + * host. Purely hypotetically, we could implement host copies for sparse + * images in one of, but not limited to, the following ways: + * + * * submitting a device copy and immediately waiting on it + * + * * mirror sparse binds' modifications to device mappings on host + * + * * share a single address space and thus mappings between host and device + * + * but realistically speaking, the set of people, apps and tests in the + * CTS that expect a driver to implement host copies on sparse images is + * exactly empty, so let's just not bother. + */ + if (info->usage & VK_IMAGE_USAGE_HOST_TRANSFER_BIT) + goto unsupported; + } + + if (info->flags & VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT) { + if (!((info->type == VK_IMAGE_TYPE_2D && + physical_device->vk.supported_features.sparseResidencyImage2D) || + (info->type == VK_IMAGE_TYPE_3D && + physical_device->vk.supported_features.sparseResidencyImage3D))) + goto unsupported; + + /* Only single aspect (thus single plane) stuff is supported for now */ + if (util_bitcount(vk_format_aspects(info->format)) != 1) + goto unsupported; + + if (info->tiling != VK_IMAGE_TILING_OPTIMAL) + goto unsupported; + + struct panvk_sparse_block_desc sblock_desc = panvk_get_sparse_block_desc(info->type, info->format); + if (!panvk_sparse_block_is_valid(sblock_desc)) + goto unsupported; + + VkImageUsageFlags allowed_usages = + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | + VK_IMAGE_USAGE_TRANSFER_DST_BIT | + VK_IMAGE_USAGE_SAMPLED_BIT | + VK_IMAGE_USAGE_STORAGE_BIT | + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | + VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | + VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT; + if (all_usage & ~allowed_usages) + goto unsupported; + } + switch (info->tiling) { case VK_IMAGE_TILING_LINEAR: case VK_IMAGE_TILING_OPTIMAL: @@ -1325,24 +1378,43 @@ fail: return result; } -VKAPI_ATTR void VKAPI_CALL -panvk_GetPhysicalDeviceSparseImageFormatProperties( - VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, - VkSampleCountFlagBits samples, VkImageUsageFlags usage, VkImageTiling tiling, - uint32_t *pNumProperties, VkSparseImageFormatProperties *pProperties) -{ - /* Sparse images are not yet supported. */ - *pNumProperties = 0; -} - VKAPI_ATTR void VKAPI_CALL panvk_GetPhysicalDeviceSparseImageFormatProperties2( VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2 *pFormatInfo, uint32_t *pPropertyCount, VkSparseImageFormatProperties2 *pProperties) { - /* Sparse images are not yet supported. */ - *pPropertyCount = 0; + VK_OUTARRAY_MAKE_TYPED(VkSparseImageFormatProperties2, out, pProperties, pPropertyCount); + + VkPhysicalDeviceImageFormatInfo2 img_info = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2, + .format = pFormatInfo->format, + .type = pFormatInfo->type, + .tiling = pFormatInfo->tiling, + .usage = pFormatInfo->usage, + .flags = VK_IMAGE_CREATE_SPARSE_BINDING_BIT | + VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT, + }; + VkImageFormatProperties2 img_props = {}; + if (panvk_GetPhysicalDeviceImageFormatProperties2(physicalDevice, &img_info, &img_props) != VK_SUCCESS) + return; + + if (!(img_props.imageFormatProperties.sampleCounts & pFormatInfo->samples)) + return; + + /* + * We don't support multisampled sparse partially-resident images for now. + * Weirdly enough, banning it the obvious way by making + * get_image_format_properties report sampleCounts of 1 when flags includes + * SPARSE_RESIDENCY causes "required sample counts not supported" CTS fails, + * so we ban them here. + */ + if (pFormatInfo->samples != 1) + return; + + vk_outarray_append_typed(VkSparseImageFormatProperties2, &out, prop) { + prop->properties = panvk_get_sparse_image_fmt_props(pFormatInfo->type, pFormatInfo->format); + } } VKAPI_ATTR void VKAPI_CALL