diff options
-rw-r--r-- | src/freedreno/vulkan/tu_image.c | 100 | ||||
-rw-r--r-- | src/freedreno/vulkan/tu_private.h | 13 | ||||
-rw-r--r-- | src/freedreno/vulkan/vk_format.h | 22 |
3 files changed, 135 insertions, 0 deletions
diff --git a/src/freedreno/vulkan/tu_image.c b/src/freedreno/vulkan/tu_image.c index 26e3d94e7c4..db7462d65c0 100644 --- a/src/freedreno/vulkan/tu_image.c +++ b/src/freedreno/vulkan/tu_image.c @@ -31,6 +31,101 @@ #include "vk_format.h" #include "vk_util.h" + +static inline bool +image_level_linear(struct tu_image *image, int level) +{ + unsigned w = u_minify(image->extent.width, level); + return w < 16; +} + +/* indexed by cpp: */ +static const struct { + unsigned pitchalign; + unsigned heightalign; +} tile_alignment[] = { + [1] = { 128, 32 }, + [2] = { 128, 16 }, + [3] = { 128, 16 }, + [4] = { 64, 16 }, + [8] = { 64, 16 }, + [12] = { 64, 16 }, + [16] = { 64, 16 }, +}; + +static void +setup_slices(struct tu_image *image, const VkImageCreateInfo *pCreateInfo) +{ + enum vk_format_layout layout = vk_format_description(pCreateInfo->format)->layout; + uint32_t layer_size = 0; + uint32_t width = pCreateInfo->extent.width; + uint32_t height = pCreateInfo->extent.height; + uint32_t depth = pCreateInfo->extent.depth; + bool layer_first = pCreateInfo->imageType != VK_IMAGE_TYPE_3D; + uint32_t alignment = pCreateInfo->imageType == VK_IMAGE_TYPE_3D ? 4096 : 1; + uint32_t cpp = vk_format_get_blocksize(pCreateInfo->format); + + uint32_t heightalign = tile_alignment[cpp].heightalign; + + for (unsigned level = 0; level < pCreateInfo->mipLevels; level++) { + struct tu_image_level *slice = &image->levels[level]; + bool linear_level = image_level_linear(image, level); + uint32_t aligned_height = height; + uint32_t blocks; + uint32_t pitchalign; + + if (image->tile_mode && !linear_level) { + pitchalign = tile_alignment[cpp].pitchalign; + aligned_height = align(aligned_height, heightalign); + } else { + pitchalign = 64; + + /* The blits used for mem<->gmem work at a granularity of + * 32x32, which can cause faults due to over-fetch on the + * last level. The simple solution is to over-allocate a + * bit the last level to ensure any over-fetch is harmless. + * The pitch is already sufficiently aligned, but height + * may not be: + */ + if ((level + 1 == pCreateInfo->mipLevels)) + aligned_height = align(aligned_height, 32); + } + + if (layout == VK_FORMAT_LAYOUT_ASTC) + slice->pitch = + util_align_npot(width, pitchalign * vk_format_get_blockwidth(pCreateInfo->format)); + else + slice->pitch = align(width, pitchalign); + + slice->offset = layer_size; + blocks = vk_format_get_block_count(pCreateInfo->format, slice->pitch, aligned_height); + + /* 1d array and 2d array textures must all have the same layer size + * for each miplevel on a3xx. 3d textures can have different layer + * sizes for high levels, but the hw auto-sizer is buggy (or at least + * different than what this code does), so as soon as the layer size + * range gets into range, we stop reducing it. + */ + if (pCreateInfo->imageType == VK_IMAGE_TYPE_3D && ( + level == 1 || + (level > 1 && image->levels[level - 1].size > 0xf000))) + slice->size = align(blocks * cpp, alignment); + else if (level == 0 || layer_first || alignment == 1) + slice->size = align(blocks * cpp, alignment); + else + slice->size = image->levels[level - 1].size; + + layer_size += slice->size * depth; + + width = u_minify(width, 1); + height = u_minify(height, 1); + depth = u_minify(depth, 1); + } + + image->layer_size = layer_size; +} + + VkResult tu_image_create(VkDevice _device, const struct tu_image_create_info *create_info, @@ -63,6 +158,7 @@ tu_image_create(VkDevice _device, image->tiling = pCreateInfo->tiling; image->usage = pCreateInfo->usage; image->flags = pCreateInfo->flags; + image->extent = pCreateInfo->extent; image->exclusive = pCreateInfo->sharingMode == VK_SHARING_MODE_EXCLUSIVE; if (pCreateInfo->sharingMode == VK_SHARING_MODE_CONCURRENT) { @@ -79,6 +175,10 @@ tu_image_create(VkDevice _device, vk_find_struct_const(pCreateInfo->pNext, EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR) != NULL; + image->tile_mode = pCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ? 3 : 0; + setup_slices(image, pCreateInfo); + + image->size = image->layer_size * pCreateInfo->arrayLayers; *pImage = tu_image_to_handle(image); return VK_SUCCESS; diff --git a/src/freedreno/vulkan/tu_private.h b/src/freedreno/vulkan/tu_private.h index d6271f3465e..5b5315ed15e 100644 --- a/src/freedreno/vulkan/tu_private.h +++ b/src/freedreno/vulkan/tu_private.h @@ -904,6 +904,13 @@ tu_is_colorbuffer_format_supported(VkFormat format, bool *blendable); bool tu_dcc_formats_compatible(VkFormat format1, VkFormat format2); + +struct tu_image_level { + VkDeviceSize offset; + VkDeviceSize size; + uint32_t pitch; +}; + struct tu_image { VkImageType type; @@ -915,10 +922,16 @@ struct tu_image VkImageUsageFlags usage; /**< Superset of VkImageCreateInfo::usage. */ VkImageTiling tiling; /** VkImageCreateInfo::tiling */ VkImageCreateFlags flags; /** VkImageCreateInfo::flags */ + VkExtent3D extent; VkDeviceSize size; uint32_t alignment; + /* memory layout */ + VkDeviceSize layer_size; + struct tu_image_level levels[15]; + unsigned tile_mode; + unsigned queue_family_mask; bool exclusive; bool shareable; diff --git a/src/freedreno/vulkan/vk_format.h b/src/freedreno/vulkan/vk_format.h index 75b7fbd05b3..55ad379f8f3 100644 --- a/src/freedreno/vulkan/vk_format.h +++ b/src/freedreno/vulkan/vk_format.h @@ -203,6 +203,28 @@ vk_format_get_blockheight(VkFormat format) return desc->block.height; } +static inline unsigned +vk_format_get_block_count_width(VkFormat format, unsigned width) +{ + unsigned blockwidth = vk_format_get_blockwidth(format); + return (width + blockwidth - 1) / blockwidth; +} + +static inline unsigned +vk_format_get_block_count_height(VkFormat format, unsigned height) +{ + unsigned blockheight = vk_format_get_blockheight(format); + return (height + blockheight - 1) / blockheight; +} + +static inline unsigned +vk_format_get_block_count(VkFormat format, unsigned width, unsigned height) +{ + return vk_format_get_block_count_width(format, width) * + vk_format_get_block_count_height(format, height); +} + + /** * Return the index of the first non-void channel * -1 if no non-void channels |