1/* 2 * Copyright © 2022 Imagination Technologies Ltd. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a copy 5 * of this software and associated documentation files (the "Software"), to deal 6 * in the Software without restriction, including without limitation the rights 7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 * copies of the Software, and to permit persons to whom the Software is 9 * furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 * SOFTWARE. 22 */ 23 24#include <assert.h> 25#include <stdbool.h> 26#include <stdint.h> 27#include <string.h> 28 29#include "pvr_csb.h" 30#include "pvr_device_info.h" 31#include "pvr_formats.h" 32#include "pvr_private.h" 33#include "pvr_tex_state.h" 34#include "util/macros.h" 35#include "util/u_math.h" 36#include "vk_format.h" 37#include "vk_image.h" 38#include "vk_log.h" 39#include "vk_object.h" 40#include "vk_util.h" 41#include "wsi_common.h" 42 43static void pvr_image_init_memlayout(struct pvr_image *image) 44{ 45 switch (image->vk.tiling) { 46 default: 47 unreachable("bad VkImageTiling"); 48 case VK_IMAGE_TILING_OPTIMAL: 49 if (image->vk.wsi_legacy_scanout) 50 image->memlayout = PVR_MEMLAYOUT_LINEAR; 51 else if (image->vk.image_type == VK_IMAGE_TYPE_3D) 52 image->memlayout = PVR_MEMLAYOUT_3DTWIDDLED; 53 else 54 image->memlayout = PVR_MEMLAYOUT_TWIDDLED; 55 break; 56 case VK_IMAGE_TILING_LINEAR: 57 image->memlayout = PVR_MEMLAYOUT_LINEAR; 58 break; 59 } 60} 61 62static void pvr_image_init_physical_extent(struct pvr_image *image) 63{ 64 assert(image->memlayout != PVR_MEMLAYOUT_UNDEFINED); 65 66 /* clang-format off */ 67 if (image->vk.mip_levels > 1 || 68 image->memlayout == PVR_MEMLAYOUT_TWIDDLED || 69 image->memlayout == PVR_MEMLAYOUT_3DTWIDDLED) { 70 /* clang-format on */ 71 image->physical_extent.width = 72 util_next_power_of_two(image->vk.extent.width); 73 image->physical_extent.height = 74 util_next_power_of_two(image->vk.extent.height); 75 image->physical_extent.depth = 76 util_next_power_of_two(image->vk.extent.depth); 77 } else { 78 assert(image->memlayout == PVR_MEMLAYOUT_LINEAR); 79 image->physical_extent = image->vk.extent; 80 } 81} 82 83static void pvr_image_setup_mip_levels(struct pvr_image *image) 84{ 85 const uint32_t extent_alignment = 86 image->vk.image_type == VK_IMAGE_TYPE_3D ? 4 : 1; 87 const unsigned int cpp = vk_format_get_blocksize(image->vk.format); 88 89 /* Mip-mapped textures that are non-dword aligned need dword-aligned levels 90 * so they can be TQd from. 91 */ 92 const uint32_t level_alignment = image->vk.mip_levels > 1 ? 4 : 1; 93 94 assert(image->vk.mip_levels <= ARRAY_SIZE(image->mip_levels)); 95 96 image->layer_size = 0; 97 98 for (uint32_t i = 0; i < image->vk.mip_levels; i++) { 99 const uint32_t height = u_minify(image->physical_extent.height, i); 100 const uint32_t width = u_minify(image->physical_extent.width, i); 101 const uint32_t depth = u_minify(image->physical_extent.depth, i); 102 struct pvr_mip_level *mip_level = &image->mip_levels[i]; 103 104 mip_level->pitch = cpp * ALIGN(width, extent_alignment); 105 mip_level->height_pitch = ALIGN(height, extent_alignment); 106 mip_level->size = image->vk.samples * mip_level->pitch * 107 mip_level->height_pitch * 108 ALIGN(depth, extent_alignment); 109 mip_level->size = ALIGN(mip_level->size, level_alignment); 110 mip_level->offset = image->layer_size; 111 112 image->layer_size += mip_level->size; 113 } 114 115 /* TODO: It might be useful to store the alignment in the image so it can be 116 * checked (via an assert?) when setting 117 * RGX_CR_TPU_TAG_CEM_4K_FACE_PACKING_EN, assuming this is where the 118 * requirement comes from. 119 */ 120 if (image->vk.array_layers > 1) 121 image->layer_size = ALIGN(image->layer_size, image->alignment); 122 123 image->size = image->layer_size * image->vk.array_layers; 124} 125 126VkResult pvr_CreateImage(VkDevice _device, 127 const VkImageCreateInfo *pCreateInfo, 128 const VkAllocationCallbacks *pAllocator, 129 VkImage *pImage) 130{ 131 PVR_FROM_HANDLE(pvr_device, device, _device); 132 struct pvr_image *image; 133 134 pvr_finishme("Review whether all inputs are handled\n"); 135 136 image = 137 vk_image_create(&device->vk, pCreateInfo, pAllocator, sizeof(*image)); 138 if (!image) 139 return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); 140 141 /* All images aligned to 4k, in case of arrays/CEM. 142 * Refer: pvr_GetImageMemoryRequirements for further details. 143 */ 144 image->alignment = 4096U; 145 146 /* Initialize the image using the saved information from pCreateInfo */ 147 pvr_image_init_memlayout(image); 148 pvr_image_init_physical_extent(image); 149 pvr_image_setup_mip_levels(image); 150 151 *pImage = pvr_image_to_handle(image); 152 153 return VK_SUCCESS; 154} 155 156void pvr_DestroyImage(VkDevice _device, 157 VkImage _image, 158 const VkAllocationCallbacks *pAllocator) 159{ 160 PVR_FROM_HANDLE(pvr_device, device, _device); 161 PVR_FROM_HANDLE(pvr_image, image, _image); 162 163 if (!image) 164 return; 165 166 if (image->vma) 167 pvr_unbind_memory(device, image->vma); 168 169 vk_image_destroy(&device->vk, pAllocator, &image->vk); 170} 171 172/* clang-format off */ 173/* Consider a 4 page buffer object. 174 * _________________________________________ 175 * | | | | | 176 * |_________|__________|_________|__________| 177 * | 178 * \__ offset (0.5 page size) 179 * 180 * |___size(2 pages)____| 181 * 182 * |__VMA size required (3 pages)__| 183 * 184 * | 185 * \__ returned dev_addr = vma + offset % page_size 186 * 187 * VMA size = align(size + offset % page_size, page_size); 188 * 189 * Note: the above handling is currently divided between generic 190 * driver code and winsys layer. Given are the details of how this is 191 * being handled. 192 * * As winsys vma allocation interface does not have offset information, 193 * it can not calculate the extra size needed to adjust for the unaligned 194 * offset. So generic code is responsible for allocating a VMA that has 195 * extra space to deal with the above scenario. 196 * * Remaining work of mapping the vma to bo is done by vma_map interface, 197 * as it contains offset information, we don't need to do any adjustments 198 * in the generic code for this part. 199 * 200 * TODO: Look into merging heap_alloc and vma_map into single interface. 201 */ 202/* clang-format on */ 203 204VkResult pvr_BindImageMemory2(VkDevice _device, 205 uint32_t bindInfoCount, 206 const VkBindImageMemoryInfo *pBindInfos) 207{ 208 PVR_FROM_HANDLE(pvr_device, device, _device); 209 uint32_t i; 210 211 for (i = 0; i < bindInfoCount; i++) { 212 PVR_FROM_HANDLE(pvr_device_memory, mem, pBindInfos[i].memory); 213 PVR_FROM_HANDLE(pvr_image, image, pBindInfos[i].image); 214 215 VkResult result = pvr_bind_memory(device, 216 mem, 217 pBindInfos[i].memoryOffset, 218 image->size, 219 image->alignment, 220 &image->vma, 221 &image->dev_addr); 222 if (result != VK_SUCCESS) { 223 while (i--) { 224 PVR_FROM_HANDLE(pvr_image, image, pBindInfos[i].image); 225 226 pvr_unbind_memory(device, image->vma); 227 } 228 229 return result; 230 } 231 } 232 233 return VK_SUCCESS; 234} 235 236void pvr_GetImageSubresourceLayout(VkDevice device, 237 VkImage _image, 238 const VkImageSubresource *subresource, 239 VkSubresourceLayout *layout) 240{ 241 PVR_FROM_HANDLE(pvr_image, image, _image); 242 const struct pvr_mip_level *mip_level = 243 &image->mip_levels[subresource->mipLevel]; 244 245 pvr_assert(subresource->mipLevel < image->vk.mip_levels); 246 pvr_assert(subresource->arrayLayer < image->vk.array_layers); 247 248 layout->offset = 249 subresource->arrayLayer * image->layer_size + mip_level->offset; 250 layout->rowPitch = mip_level->pitch; 251 layout->depthPitch = mip_level->pitch * mip_level->height_pitch; 252 layout->arrayPitch = image->layer_size; 253 layout->size = mip_level->size; 254} 255 256VkResult pvr_CreateImageView(VkDevice _device, 257 const VkImageViewCreateInfo *pCreateInfo, 258 const VkAllocationCallbacks *pAllocator, 259 VkImageView *pView) 260{ 261 PVR_FROM_HANDLE(pvr_image, image, pCreateInfo->image); 262 PVR_FROM_HANDLE(pvr_device, device, _device); 263 struct pvr_texture_state_info info; 264 unsigned char input_swizzle[4]; 265 const uint8_t *format_swizzle; 266 struct pvr_image_view *iview; 267 VkResult result; 268 269 iview = vk_image_view_create(&device->vk, 270 false /* driver_internal */, 271 pCreateInfo, 272 pAllocator, 273 sizeof(*iview)); 274 if (!iview) 275 return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); 276 277 iview->image = image; 278 279 info.type = iview->vk.view_type; 280 info.base_level = iview->vk.base_mip_level; 281 info.mip_levels = iview->vk.level_count; 282 info.extent = image->vk.extent; 283 info.is_cube = (info.type == VK_IMAGE_VIEW_TYPE_CUBE || 284 info.type == VK_IMAGE_VIEW_TYPE_CUBE_ARRAY); 285 info.array_size = iview->vk.layer_count; 286 info.offset = iview->vk.base_array_layer * image->layer_size + 287 image->mip_levels[info.base_level].offset; 288 info.mipmaps_present = (image->vk.mip_levels > 1) ? true : false; 289 info.stride = image->physical_extent.width; 290 info.tex_state_type = PVR_TEXTURE_STATE_SAMPLE; 291 info.mem_layout = image->memlayout; 292 info.flags = 0; 293 info.sample_count = image->vk.samples; 294 info.addr = image->dev_addr; 295 296 /* TODO: if ERN_46863 is supported, Depth and stencil are sampled separately 297 * from images with combined depth+stencil. Add logic here to handle it. 298 */ 299 info.format = iview->vk.format; 300 301 vk_component_mapping_to_pipe_swizzle(iview->vk.swizzle, input_swizzle); 302 format_swizzle = pvr_get_format_swizzle(info.format); 303 util_format_compose_swizzles(format_swizzle, input_swizzle, info.swizzle); 304 305 result = pvr_pack_tex_state(device, 306 &info, 307 iview->texture_state[info.tex_state_type]); 308 if (result != VK_SUCCESS) 309 goto err_vk_image_view_destroy; 310 311 /* Create an additional texture state for cube type if storage 312 * usage flat is set. 313 */ 314 if (info.is_cube && image->vk.usage & VK_IMAGE_USAGE_STORAGE_BIT) { 315 info.tex_state_type = PVR_TEXTURE_STATE_STORAGE; 316 result = pvr_pack_tex_state(device, 317 &info, 318 iview->texture_state[info.tex_state_type]); 319 if (result != VK_SUCCESS) 320 goto err_vk_image_view_destroy; 321 } 322 323 /* Attachment state is created as if the mipmaps are not supported, so the 324 * baselevel is set to zero and num_mip_levels is set to 1. Which gives an 325 * impression that this is the only level in the image. This also requires 326 * that width, height and depth be adjusted as well. Given iview->vk.extent 327 * is already adjusted for base mip map level we use it here. 328 */ 329 /* TODO: Investigate and document the reason for above approach. */ 330 info.extent = iview->vk.extent; 331 332 info.mip_levels = 1; 333 info.mipmaps_present = false; 334 info.stride = u_minify(image->physical_extent.width, info.base_level); 335 info.base_level = 0; 336 info.tex_state_type = PVR_TEXTURE_STATE_ATTACHMENT; 337 338 result = pvr_pack_tex_state(device, 339 &info, 340 iview->texture_state[info.tex_state_type]); 341 if (result != VK_SUCCESS) 342 goto err_vk_image_view_destroy; 343 344 *pView = pvr_image_view_to_handle(iview); 345 346 return VK_SUCCESS; 347 348err_vk_image_view_destroy: 349 vk_image_view_destroy(&device->vk, pAllocator, &iview->vk); 350 351 return result; 352} 353 354void pvr_DestroyImageView(VkDevice _device, 355 VkImageView _iview, 356 const VkAllocationCallbacks *pAllocator) 357{ 358 PVR_FROM_HANDLE(pvr_device, device, _device); 359 PVR_FROM_HANDLE(pvr_image_view, iview, _iview); 360 361 if (!iview) 362 return; 363 364 vk_image_view_destroy(&device->vk, pAllocator, &iview->vk); 365} 366 367VkResult pvr_CreateBufferView(VkDevice _device, 368 const VkBufferViewCreateInfo *pCreateInfo, 369 const VkAllocationCallbacks *pAllocator, 370 VkBufferView *pView) 371{ 372 PVR_FROM_HANDLE(pvr_buffer, buffer, pCreateInfo->buffer); 373 PVR_FROM_HANDLE(pvr_device, device, _device); 374 struct pvr_texture_state_info info; 375 const uint8_t *format_swizzle; 376 struct pvr_buffer_view *bview; 377 VkResult result; 378 379 bview = vk_object_alloc(&device->vk, 380 pAllocator, 381 sizeof(*bview), 382 VK_OBJECT_TYPE_BUFFER_VIEW); 383 if (!bview) 384 return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); 385 386 bview->format = pCreateInfo->format; 387 bview->range = 388 vk_buffer_range(&buffer->vk, pCreateInfo->offset, pCreateInfo->range); 389 390 /* If the remaining size of the buffer is not a multiple of the element 391 * size of the format, the nearest smaller multiple is used. 392 */ 393 bview->range -= bview->range % vk_format_get_blocksize(bview->format); 394 395 /* The range of the buffer view shouldn't be smaller than one texel. */ 396 assert(bview->range >= vk_format_get_blocksize(bview->format)); 397 398 info.base_level = 0U; 399 info.mip_levels = 1U; 400 info.mipmaps_present = false; 401 info.extent.width = 8192U; 402 info.extent.height = bview->range / vk_format_get_blocksize(bview->format); 403 info.extent.height = DIV_ROUND_UP(info.extent.height, info.extent.width); 404 info.extent.depth = 0U; 405 info.sample_count = 1U; 406 info.stride = info.extent.width; 407 info.offset = 0U; 408 info.addr = PVR_DEV_ADDR_OFFSET(buffer->dev_addr, pCreateInfo->offset); 409 info.mem_layout = PVR_MEMLAYOUT_LINEAR; 410 info.is_cube = false; 411 info.tex_state_type = PVR_TEXTURE_STATE_SAMPLE; 412 info.format = bview->format; 413 info.flags = PVR_TEXFLAGS_INDEX_LOOKUP; 414 415 if (PVR_HAS_FEATURE(&device->pdevice->dev_info, tpu_array_textures)) 416 info.array_size = 1U; 417 418 format_swizzle = pvr_get_format_swizzle(info.format); 419 memcpy(info.swizzle, format_swizzle, sizeof(info.swizzle)); 420 421 result = pvr_pack_tex_state(device, &info, bview->texture_state); 422 if (result != VK_SUCCESS) 423 goto err_vk_buffer_view_destroy; 424 425 *pView = pvr_buffer_view_to_handle(bview); 426 427 return VK_SUCCESS; 428 429err_vk_buffer_view_destroy: 430 vk_object_free(&device->vk, pAllocator, bview); 431 432 return result; 433} 434 435void pvr_DestroyBufferView(VkDevice _device, 436 VkBufferView bufferView, 437 const VkAllocationCallbacks *pAllocator) 438{ 439 PVR_FROM_HANDLE(pvr_buffer_view, bview, bufferView); 440 PVR_FROM_HANDLE(pvr_device, device, _device); 441 442 if (!bview) 443 return; 444 445 vk_object_free(&device->vk, pAllocator, bview); 446} 447