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
pvr_image_init_memlayout(struct pvr_image * image)43 static 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
pvr_image_init_physical_extent(struct pvr_image * image)62 static 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
pvr_image_setup_mip_levels(struct pvr_image * image)83 static 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 VkExtent3D extent =
89 vk_image_extent_to_elements(&image->vk, image->physical_extent);
90
91 /* Mip-mapped textures that are non-dword aligned need dword-aligned levels
92 * so they can be TQd from.
93 */
94 const uint32_t level_alignment = image->vk.mip_levels > 1 ? 4 : 1;
95
96 assert(image->vk.mip_levels <= ARRAY_SIZE(image->mip_levels));
97
98 image->layer_size = 0;
99
100 for (uint32_t i = 0; i < image->vk.mip_levels; i++) {
101 struct pvr_mip_level *mip_level = &image->mip_levels[i];
102
103 mip_level->pitch = cpp * ALIGN(extent.width, extent_alignment);
104 mip_level->height_pitch = ALIGN(extent.height, extent_alignment);
105 mip_level->size = image->vk.samples * mip_level->pitch *
106 mip_level->height_pitch *
107 ALIGN(extent.depth, extent_alignment);
108 mip_level->size = ALIGN(mip_level->size, level_alignment);
109 mip_level->offset = image->layer_size;
110
111 image->layer_size += mip_level->size;
112
113 extent.height = u_minify(extent.height, 1);
114 extent.width = u_minify(extent.width, 1);
115 extent.depth = u_minify(extent.depth, 1);
116 }
117
118 /* The hw calculates layer strides as if a full mip chain up until 1x1x1
119 * were present so we need to account for that in the `layer_size`.
120 */
121 while (extent.height != 1 || extent.width != 1 || extent.depth != 1) {
122 const uint32_t height_pitch = ALIGN(extent.height, extent_alignment);
123 const uint32_t pitch = cpp * ALIGN(extent.width, extent_alignment);
124
125 image->layer_size += image->vk.samples * pitch * height_pitch *
126 ALIGN(extent.depth, extent_alignment);
127
128 extent.height = u_minify(extent.height, 1);
129 extent.width = u_minify(extent.width, 1);
130 extent.depth = u_minify(extent.depth, 1);
131 }
132
133 /* TODO: It might be useful to store the alignment in the image so it can be
134 * checked (via an assert?) when setting
135 * RGX_CR_TPU_TAG_CEM_4K_FACE_PACKING_EN, assuming this is where the
136 * requirement comes from.
137 */
138 if (image->vk.array_layers > 1)
139 image->layer_size = align64(image->layer_size, image->alignment);
140
141 image->size = image->layer_size * image->vk.array_layers;
142 }
143
pvr_CreateImage(VkDevice _device,const VkImageCreateInfo * pCreateInfo,const VkAllocationCallbacks * pAllocator,VkImage * pImage)144 VkResult pvr_CreateImage(VkDevice _device,
145 const VkImageCreateInfo *pCreateInfo,
146 const VkAllocationCallbacks *pAllocator,
147 VkImage *pImage)
148 {
149 PVR_FROM_HANDLE(pvr_device, device, _device);
150 struct pvr_image *image;
151
152 image =
153 vk_image_create(&device->vk, pCreateInfo, pAllocator, sizeof(*image));
154 if (!image)
155 return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY);
156
157 /* All images aligned to 4k, in case of arrays/CEM.
158 * Refer: pvr_GetImageMemoryRequirements for further details.
159 */
160 image->alignment = 4096U;
161
162 /* Initialize the image using the saved information from pCreateInfo */
163 pvr_image_init_memlayout(image);
164 pvr_image_init_physical_extent(image);
165 pvr_image_setup_mip_levels(image);
166
167 *pImage = pvr_image_to_handle(image);
168
169 return VK_SUCCESS;
170 }
171
pvr_DestroyImage(VkDevice _device,VkImage _image,const VkAllocationCallbacks * pAllocator)172 void pvr_DestroyImage(VkDevice _device,
173 VkImage _image,
174 const VkAllocationCallbacks *pAllocator)
175 {
176 PVR_FROM_HANDLE(pvr_device, device, _device);
177 PVR_FROM_HANDLE(pvr_image, image, _image);
178
179 if (!image)
180 return;
181
182 if (image->vma)
183 pvr_unbind_memory(device, image->vma);
184
185 vk_image_destroy(&device->vk, pAllocator, &image->vk);
186 }
187
188 /* clang-format off */
189 /* Consider a 4 page buffer object.
190 * _________________________________________
191 * | | | | |
192 * |_________|__________|_________|__________|
193 * |
194 * \__ offset (0.5 page size)
195 *
196 * |___size(2 pages)____|
197 *
198 * |__VMA size required (3 pages)__|
199 *
200 * |
201 * \__ returned dev_addr = vma + offset % page_size
202 *
203 * VMA size = align(size + offset % page_size, page_size);
204 *
205 * Note: the above handling is currently divided between generic
206 * driver code and winsys layer. Given are the details of how this is
207 * being handled.
208 * * As winsys vma allocation interface does not have offset information,
209 * it can not calculate the extra size needed to adjust for the unaligned
210 * offset. So generic code is responsible for allocating a VMA that has
211 * extra space to deal with the above scenario.
212 * * Remaining work of mapping the vma to bo is done by vma_map interface,
213 * as it contains offset information, we don't need to do any adjustments
214 * in the generic code for this part.
215 *
216 * TODO: Look into merging heap_alloc and vma_map into single interface.
217 */
218 /* clang-format on */
219
pvr_BindImageMemory2(VkDevice _device,uint32_t bindInfoCount,const VkBindImageMemoryInfo * pBindInfos)220 VkResult pvr_BindImageMemory2(VkDevice _device,
221 uint32_t bindInfoCount,
222 const VkBindImageMemoryInfo *pBindInfos)
223 {
224 PVR_FROM_HANDLE(pvr_device, device, _device);
225 uint32_t i;
226
227 for (i = 0; i < bindInfoCount; i++) {
228 PVR_FROM_HANDLE(pvr_device_memory, mem, pBindInfos[i].memory);
229 PVR_FROM_HANDLE(pvr_image, image, pBindInfos[i].image);
230
231 VkResult result = pvr_bind_memory(device,
232 mem,
233 pBindInfos[i].memoryOffset,
234 image->size,
235 image->alignment,
236 &image->vma,
237 &image->dev_addr);
238 if (result != VK_SUCCESS) {
239 while (i--) {
240 PVR_FROM_HANDLE(pvr_image, image, pBindInfos[i].image);
241
242 pvr_unbind_memory(device, image->vma);
243 }
244
245 return result;
246 }
247 }
248
249 return VK_SUCCESS;
250 }
251
pvr_get_image_subresource_layout(const struct pvr_image * image,const VkImageSubresource * subresource,VkSubresourceLayout * layout)252 void pvr_get_image_subresource_layout(const struct pvr_image *image,
253 const VkImageSubresource *subresource,
254 VkSubresourceLayout *layout)
255 {
256 const struct pvr_mip_level *mip_level =
257 &image->mip_levels[subresource->mipLevel];
258
259 pvr_assert(subresource->mipLevel < image->vk.mip_levels);
260 pvr_assert(subresource->arrayLayer < image->vk.array_layers);
261
262 layout->offset =
263 subresource->arrayLayer * image->layer_size + mip_level->offset;
264 layout->rowPitch = mip_level->pitch;
265 layout->depthPitch = mip_level->pitch * mip_level->height_pitch;
266 layout->arrayPitch = image->layer_size;
267 layout->size = mip_level->size;
268 }
269
pvr_GetImageSubresourceLayout(VkDevice device,VkImage _image,const VkImageSubresource * subresource,VkSubresourceLayout * layout)270 void pvr_GetImageSubresourceLayout(VkDevice device,
271 VkImage _image,
272 const VkImageSubresource *subresource,
273 VkSubresourceLayout *layout)
274 {
275 PVR_FROM_HANDLE(pvr_image, image, _image);
276
277 pvr_get_image_subresource_layout(image, subresource, layout);
278 }
279
pvr_CreateImageView(VkDevice _device,const VkImageViewCreateInfo * pCreateInfo,const VkAllocationCallbacks * pAllocator,VkImageView * pView)280 VkResult pvr_CreateImageView(VkDevice _device,
281 const VkImageViewCreateInfo *pCreateInfo,
282 const VkAllocationCallbacks *pAllocator,
283 VkImageView *pView)
284 {
285 PVR_FROM_HANDLE(pvr_device, device, _device);
286 struct pvr_texture_state_info info;
287 unsigned char input_swizzle[4];
288 const uint8_t *format_swizzle;
289 const struct pvr_image *image;
290 struct pvr_image_view *iview;
291 VkResult result;
292
293 iview = vk_image_view_create(&device->vk,
294 false /* driver_internal */,
295 pCreateInfo,
296 pAllocator,
297 sizeof(*iview));
298 if (!iview)
299 return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY);
300
301 image = pvr_image_view_get_image(iview);
302
303 info.type = iview->vk.view_type;
304 info.base_level = iview->vk.base_mip_level;
305 info.mip_levels = iview->vk.level_count;
306 info.extent = image->vk.extent;
307 info.aspect_mask = image->vk.aspects;
308 info.is_cube = (info.type == VK_IMAGE_VIEW_TYPE_CUBE ||
309 info.type == VK_IMAGE_VIEW_TYPE_CUBE_ARRAY);
310 info.array_size = iview->vk.layer_count;
311 info.offset = iview->vk.base_array_layer * image->layer_size +
312 image->mip_levels[info.base_level].offset;
313 info.mipmaps_present = (image->vk.mip_levels > 1) ? true : false;
314 info.stride = image->physical_extent.width;
315 info.tex_state_type = PVR_TEXTURE_STATE_SAMPLE;
316 info.mem_layout = image->memlayout;
317 info.flags = 0;
318 info.sample_count = image->vk.samples;
319 info.addr = image->dev_addr;
320
321 info.format = pCreateInfo->format;
322
323 vk_component_mapping_to_pipe_swizzle(iview->vk.swizzle, input_swizzle);
324 format_swizzle = pvr_get_format_swizzle(info.format);
325 util_format_compose_swizzles(format_swizzle, input_swizzle, info.swizzle);
326
327 result = pvr_pack_tex_state(device,
328 &info,
329 iview->texture_state[info.tex_state_type]);
330 if (result != VK_SUCCESS)
331 goto err_vk_image_view_destroy;
332
333 /* Create an additional texture state for cube type if storage
334 * usage flag is set.
335 */
336 if (info.is_cube && image->vk.usage & VK_IMAGE_USAGE_STORAGE_BIT) {
337 info.tex_state_type = PVR_TEXTURE_STATE_STORAGE;
338
339 result = pvr_pack_tex_state(device,
340 &info,
341 iview->texture_state[info.tex_state_type]);
342 if (result != VK_SUCCESS)
343 goto err_vk_image_view_destroy;
344 }
345
346 if (image->vk.usage & VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT) {
347 /* Attachment state is created as if the mipmaps are not supported, so the
348 * baselevel is set to zero and num_mip_levels is set to 1. Which gives an
349 * impression that this is the only level in the image. This also requires
350 * that width, height and depth be adjusted as well. Given
351 * iview->vk.extent is already adjusted for base mip map level we use it
352 * here.
353 */
354 /* TODO: Investigate and document the reason for above approach. */
355 info.extent = iview->vk.extent;
356
357 info.mip_levels = 1;
358 info.mipmaps_present = false;
359 info.stride = u_minify(image->physical_extent.width, info.base_level);
360 info.base_level = 0;
361 info.tex_state_type = PVR_TEXTURE_STATE_ATTACHMENT;
362
363 if (image->vk.image_type == VK_IMAGE_TYPE_3D &&
364 iview->vk.view_type == VK_IMAGE_VIEW_TYPE_2D) {
365 info.type = VK_IMAGE_VIEW_TYPE_3D;
366 } else {
367 info.type = iview->vk.view_type;
368 }
369
370 result = pvr_pack_tex_state(device,
371 &info,
372 iview->texture_state[info.tex_state_type]);
373 if (result != VK_SUCCESS)
374 goto err_vk_image_view_destroy;
375 }
376
377 *pView = pvr_image_view_to_handle(iview);
378
379 return VK_SUCCESS;
380
381 err_vk_image_view_destroy:
382 vk_image_view_destroy(&device->vk, pAllocator, &iview->vk);
383
384 return result;
385 }
386
pvr_DestroyImageView(VkDevice _device,VkImageView _iview,const VkAllocationCallbacks * pAllocator)387 void pvr_DestroyImageView(VkDevice _device,
388 VkImageView _iview,
389 const VkAllocationCallbacks *pAllocator)
390 {
391 PVR_FROM_HANDLE(pvr_device, device, _device);
392 PVR_FROM_HANDLE(pvr_image_view, iview, _iview);
393
394 if (!iview)
395 return;
396
397 vk_image_view_destroy(&device->vk, pAllocator, &iview->vk);
398 }
399
pvr_CreateBufferView(VkDevice _device,const VkBufferViewCreateInfo * pCreateInfo,const VkAllocationCallbacks * pAllocator,VkBufferView * pView)400 VkResult pvr_CreateBufferView(VkDevice _device,
401 const VkBufferViewCreateInfo *pCreateInfo,
402 const VkAllocationCallbacks *pAllocator,
403 VkBufferView *pView)
404 {
405 PVR_FROM_HANDLE(pvr_buffer, buffer, pCreateInfo->buffer);
406 PVR_FROM_HANDLE(pvr_device, device, _device);
407 struct pvr_texture_state_info info;
408 const uint8_t *format_swizzle;
409 struct pvr_buffer_view *bview;
410 VkResult result;
411
412 bview = vk_object_alloc(&device->vk,
413 pAllocator,
414 sizeof(*bview),
415 VK_OBJECT_TYPE_BUFFER_VIEW);
416 if (!bview)
417 return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY);
418
419 bview->format = pCreateInfo->format;
420 bview->range =
421 vk_buffer_range(&buffer->vk, pCreateInfo->offset, pCreateInfo->range);
422
423 /* If the remaining size of the buffer is not a multiple of the element
424 * size of the format, the nearest smaller multiple is used.
425 */
426 bview->range -= bview->range % vk_format_get_blocksize(bview->format);
427
428 /* The range of the buffer view shouldn't be smaller than one texel. */
429 assert(bview->range >= vk_format_get_blocksize(bview->format));
430
431 info.base_level = 0U;
432 info.mip_levels = 1U;
433 info.mipmaps_present = false;
434 info.extent.width = 8192U;
435 info.extent.height = bview->range / vk_format_get_blocksize(bview->format);
436 info.extent.height = DIV_ROUND_UP(info.extent.height, info.extent.width);
437 info.extent.depth = 0U;
438 info.sample_count = 1U;
439 info.stride = info.extent.width;
440 info.offset = 0U;
441 info.addr = PVR_DEV_ADDR_OFFSET(buffer->dev_addr, pCreateInfo->offset);
442 info.mem_layout = PVR_MEMLAYOUT_LINEAR;
443 info.is_cube = false;
444 info.type = VK_IMAGE_VIEW_TYPE_2D;
445 info.tex_state_type = PVR_TEXTURE_STATE_SAMPLE;
446 info.format = bview->format;
447 info.flags = PVR_TEXFLAGS_INDEX_LOOKUP;
448 info.aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT;
449
450 if (PVR_HAS_FEATURE(&device->pdevice->dev_info, tpu_array_textures))
451 info.array_size = 1U;
452
453 format_swizzle = pvr_get_format_swizzle(info.format);
454 memcpy(info.swizzle, format_swizzle, sizeof(info.swizzle));
455
456 result = pvr_pack_tex_state(device, &info, bview->texture_state);
457 if (result != VK_SUCCESS)
458 goto err_vk_buffer_view_destroy;
459
460 *pView = pvr_buffer_view_to_handle(bview);
461
462 return VK_SUCCESS;
463
464 err_vk_buffer_view_destroy:
465 vk_object_free(&device->vk, pAllocator, bview);
466
467 return result;
468 }
469
pvr_DestroyBufferView(VkDevice _device,VkBufferView bufferView,const VkAllocationCallbacks * pAllocator)470 void pvr_DestroyBufferView(VkDevice _device,
471 VkBufferView bufferView,
472 const VkAllocationCallbacks *pAllocator)
473 {
474 PVR_FROM_HANDLE(pvr_buffer_view, bview, bufferView);
475 PVR_FROM_HANDLE(pvr_device, device, _device);
476
477 if (!bview)
478 return;
479
480 vk_object_free(&device->vk, pAllocator, bview);
481 }
482