diff --git a/src/panfrost/vulkan/panvk_image.c b/src/panfrost/vulkan/panvk_image.c index 3e863fd6b65..9ff58eb8006 100644 --- a/src/panfrost/vulkan/panvk_image.c +++ b/src/panfrost/vulkan/panvk_image.c @@ -1,4 +1,5 @@ /* + * Copyright © 2025 Arm Ltd. * Copyright © 2021 Collabora Ltd. * * Derived from tu_image.c which is: @@ -254,6 +255,18 @@ panvk_image_can_use_mod(struct panvk_image *image, if ((image->vk.create_flags & VK_IMAGE_CREATE_ALIAS_BIT) && (image->vk.create_flags & VK_IMAGE_CREATE_DISJOINT_BIT)) return false; + + /* No ms with AFBC, but we need to create multisampled images in the + * background for which the view formats need to be compatible to avoid + * headaches when copying --> disable afbc for the base image as well. + * When copying the depth plane block sizes aren't matching between + * utiled and afbc, thus the views created for the ms images are + * invalid. + */ + if (image->vk.create_flags & + VK_IMAGE_CREATE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_BIT_EXT) { + return false; + } } if (mod == DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED) { @@ -604,6 +617,57 @@ panvk_image_plane_bind_addr(struct panvk_device *dev, plane->plane.base = addr; } +static void +create_ms_images(struct panvk_device *dev, struct panvk_image *img, + const VkImageCreateInfo *pCreateInfo, + const VkAllocationCallbacks *pAllocator) +{ + struct panvk_physical_device *pdev = + to_panvk_physical_device(dev->vk.physical); + + VkPhysicalDeviceImageFormatInfo2 info = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2, + .format = pCreateInfo->format, + .type = pCreateInfo->imageType, + .tiling = pCreateInfo->tiling, + .usage = pCreateInfo->usage, + .flags = pCreateInfo->flags, + }; + VkImageFormatProperties2 properties = {}; + panvk_GetPhysicalDeviceImageFormatProperties2( + vk_physical_device_to_handle(&pdev->vk), &info, &properties); + + VkImageCreateInfo ms_img_info = *pCreateInfo; + + assert(ms_img_info.flags & + VK_IMAGE_CREATE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_BIT_EXT); + ms_img_info.flags &= + ~VK_IMAGE_CREATE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_BIT_EXT; + + for (uint32_t msaa_idx = 0; msaa_idx < ARRAY_SIZE(img->ms_imgs); + ++msaa_idx) { + VkSampleCountFlagBits msaa = 1 << (msaa_idx + 1); + + if ((properties.imageFormatProperties.sampleCounts & msaa) == 0) { + img->ms_imgs[msaa_idx] = VK_NULL_HANDLE; + continue; + } + + ms_img_info.samples = msaa; + + panvk_CreateImage(panvk_device_to_handle(dev), &ms_img_info, pAllocator, + &img->ms_imgs[msaa_idx]); + + struct panvk_image *res = panvk_image_from_handle(img->ms_imgs[msaa_idx]); + assert(res->vk.format = img->vk.format); + assert(res->plane_count = img->plane_count); + for (uint32_t i = 0; i < res->plane_count; ++i) { + assert(res->planes[i].image.props.format == + img->planes[i].image.props.format); + } + } +} + VKAPI_ATTR VkResult VKAPI_CALL panvk_CreateImage(VkDevice device, const VkImageCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkImage *pImage) @@ -677,6 +741,10 @@ panvk_CreateImage(VkDevice device, const VkImageCreateInfo *pCreateInfo, } } + if (pCreateInfo->flags & + VK_IMAGE_CREATE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_BIT_EXT) + create_ms_images(dev, image, pCreateInfo, pAllocator); + *pImage = panvk_image_to_handle(image); return VK_SUCCESS; @@ -700,6 +768,13 @@ panvk_DestroyImage(VkDevice _device, VkImage _image, if (!image) return; + if (image->vk.create_flags & + VK_IMAGE_CREATE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_BIT_EXT) { + for (uint32_t i = 0; i < ARRAY_SIZE(image->ms_imgs); ++i) { + panvk_DestroyImage(_device, image->ms_imgs[i], pAllocator); + } + } + if (image->vk.create_flags & VK_IMAGE_CREATE_SPARSE_BINDING_BIT) { uint64_t va_range = panvk_image_get_sparse_size(image); @@ -811,6 +886,24 @@ panvk_image_get_sparse_binding_granularity(struct panvk_image *image) return panvk_get_gpu_page_size(dev); } +static void +append_ms_to_ss_memory_reqs(VkMemoryRequirements2 *pMemoryRequirements, + const VkMemoryRequirements2 *append) +{ + pMemoryRequirements->memoryRequirements.alignment = + MAX2(pMemoryRequirements->memoryRequirements.alignment, + append->memoryRequirements.alignment); + /* After the previous images, align this images start properly. */ + pMemoryRequirements->memoryRequirements.size = + align(pMemoryRequirements->memoryRequirements.size, + append->memoryRequirements.alignment); + pMemoryRequirements->memoryRequirements.size += + append->memoryRequirements.size; + pMemoryRequirements->memoryRequirements.memoryTypeBits &= + append->memoryRequirements.memoryTypeBits; + assert(pMemoryRequirements->memoryRequirements.memoryTypeBits != 0); +} + VKAPI_ATTR void VKAPI_CALL panvk_GetImageMemoryRequirements2(VkDevice device, const VkImageMemoryRequirementsInfo2 *pInfo, @@ -861,6 +954,24 @@ panvk_GetImageMemoryRequirements2(VkDevice device, break; } } + + if (image->vk.create_flags & + VK_IMAGE_CREATE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_BIT_EXT) { + for (uint32_t i = 0; i < ARRAY_SIZE(image->ms_imgs); ++i) { + if (image->ms_imgs[i] == VK_NULL_HANDLE) + continue; + + VkImageMemoryRequirementsInfo2 info = *pInfo; + info.image = image->ms_imgs[i]; + + VkMemoryRequirements2 sub_reqs_2 = { + .sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, + .memoryRequirements = {}, + }; + panvk_GetImageMemoryRequirements2(device, &info, &sub_reqs_2); + append_ms_to_ss_memory_reqs(pMemoryRequirements, &sub_reqs_2); + } + } } VKAPI_ATTR void VKAPI_CALL @@ -870,9 +981,16 @@ panvk_GetDeviceImageMemoryRequirements(VkDevice device, { VK_FROM_HANDLE(panvk_device, dev, device); + /* Make a copy so we can turn off the ms2ss flag. */ + VkDeviceImageMemoryRequirements info = *pInfo; + VkImageCreateInfo create_info = *(pInfo->pCreateInfo); + create_info.flags &= + ~VK_IMAGE_CREATE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_BIT_EXT; + info.pCreateInfo = &create_info; + struct panvk_image image; - vk_image_init(&dev->vk, &image.vk, pInfo->pCreateInfo); - panvk_image_init(&image, pInfo->pCreateInfo); + vk_image_init(&dev->vk, &image.vk, &create_info); + panvk_image_init(&image, &create_info); VkImageMemoryRequirementsInfo2 info2 = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2, @@ -880,6 +998,24 @@ panvk_GetDeviceImageMemoryRequirements(VkDevice device, }; panvk_GetImageMemoryRequirements2(device, &info2, pMemoryRequirements); vk_image_finish(&image.vk); + + if (pInfo->pCreateInfo->flags & + VK_IMAGE_CREATE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_BIT_EXT) { + for (uint32_t msaa_idx = 0; msaa_idx < ARRAY_SIZE(image.ms_imgs); + ++msaa_idx) { + /* idx 0 has sample count 2, 1 has sample count 4, ... */ + create_info.samples = 1 << (msaa_idx + 1); + create_info.flags &= + ~VK_IMAGE_CREATE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_BIT_EXT; + + VkMemoryRequirements2 msaa_reqs = { + .sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, + .memoryRequirements = {}, + }; + panvk_GetDeviceImageMemoryRequirements(device, &info, &msaa_reqs); + append_ms_to_ss_memory_reqs(pMemoryRequirements, &msaa_reqs); + } + } } /* See Vulkan spec, 35.4.3. Standard Sparse Image Block Shapes for details */ @@ -1016,6 +1152,93 @@ panvk_GetDeviceImageSparseMemoryRequirements(VkDevice device, vk_image_finish(&image.vk); } +static VkResult panvk_image_bind(struct panvk_device *dev, + const VkBindImageMemoryInfo *bind_info); + +static void +bind_ms_images(struct panvk_device *dev, const VkBindImageMemoryInfo *bind_info) +{ + VK_FROM_HANDLE(panvk_image, image, bind_info->image); + + uint64_t total_size = 0; + { + VkImageMemoryRequirementsInfo2 reqs_info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2, + .image = bind_info->image, + }; + VkMemoryRequirements2 reqs2 = { + .sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, + .memoryRequirements = {}, + }; + panvk_GetImageMemoryRequirements2(panvk_device_to_handle(dev), &reqs_info, + &reqs2); + + total_size = reqs2.memoryRequirements.size; + } + + uint64_t sub_sz[ARRAY_SIZE(image->ms_imgs)]; + uint64_t sub_al[ARRAY_SIZE(image->ms_imgs)]; + + for (uint32_t i = 0; i < ARRAY_SIZE(image->ms_imgs); ++i) { + if (image->ms_imgs[i] == VK_NULL_HANDLE) { + sub_sz[i] = 0; + sub_al[i] = 1; + continue; + } + + VkImageMemoryRequirementsInfo2 reqs_info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2, + .image = image->ms_imgs[i], + }; + VkMemoryRequirements2 reqs2 = { + .sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, + .memoryRequirements = {}, + }; + panvk_GetImageMemoryRequirements2(panvk_device_to_handle(dev), &reqs_info, + &reqs2); + + sub_sz[i] = reqs2.memoryRequirements.size; + sub_al[i] = reqs2.memoryRequirements.alignment; + } + + /* + * sub_imgs_aligned_size + * ---------------------------- + * [ base, sub_0, sub_1, sub_2, sub_3 ] + * -->-> -->-> ... ------> + * sz a sz a size only + */ + uint64_t sub_imgs_aligned_size = 0; + for (uint32_t i = 0; i < ARRAY_SIZE(image->ms_imgs); ++i) { + sub_imgs_aligned_size += sub_sz[i]; + if (i < ARRAY_SIZE(image->ms_imgs) - 1) { + sub_imgs_aligned_size = align(sub_imgs_aligned_size, sub_al[i + 1]); + } + } + + uint64_t sub_image_offset = + bind_info->memoryOffset + total_size - sub_imgs_aligned_size; + + for (uint32_t i = 0; i < ARRAY_SIZE(image->ms_imgs); ++i) { + if (image->ms_imgs[i] == VK_NULL_HANDLE) + continue; + + sub_image_offset = align(sub_image_offset, sub_al[i]); + + VkBindImageMemoryInfo sub_bind_info = { + .sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO, + .image = image->ms_imgs[i], + .memory = bind_info->memory, + .memoryOffset = sub_image_offset, + }; + + const VkResult res = panvk_image_bind(dev, &sub_bind_info); + assert(res == VK_SUCCESS); + + sub_image_offset += sub_sz[i]; + } +} + static VkResult panvk_image_bind(struct panvk_device *dev, const VkBindImageMemoryInfo *bind_info) @@ -1057,6 +1280,10 @@ panvk_image_bind(struct panvk_device *dev, panvk_image_plane_bind_mem(dev, &image->planes[plane], mem, offset); } + if (!!(image->vk.create_flags & + VK_IMAGE_CREATE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_BIT_EXT)) + bind_ms_images(dev, bind_info); + return VK_SUCCESS; } diff --git a/src/panfrost/vulkan/panvk_image.h b/src/panfrost/vulkan/panvk_image.h index 4afa97fa0b0..9d15884a428 100644 --- a/src/panfrost/vulkan/panvk_image.h +++ b/src/panfrost/vulkan/panvk_image.h @@ -40,6 +40,9 @@ struct panvk_image { uint8_t plane_count; struct panvk_image_plane planes[PANVK_MAX_PLANES]; + + /* One image each for 2x 4x 8x 16x. We don't support more than 16x. */ + VkImage ms_imgs[4]; }; VK_DEFINE_NONDISP_HANDLE_CASTS(panvk_image, vk.base, VkImage, diff --git a/src/panfrost/vulkan/panvk_image_view.h b/src/panfrost/vulkan/panvk_image_view.h index ef84dae3f63..e4f6c9c4d2d 100644 --- a/src/panfrost/vulkan/panvk_image_view.h +++ b/src/panfrost/vulkan/panvk_image_view.h @@ -43,6 +43,9 @@ struct panvk_image_view { struct mali_attribute_buffer_packed img_attrib_buf[2]; #endif } descs; + + /* One image each for 2x 4x 8x 16x. We don't support more than 16x. */ + VkImageView ms_views[4]; }; VK_DEFINE_NONDISP_HANDLE_CASTS(panvk_image_view, vk.base, VkImageView, diff --git a/src/panfrost/vulkan/panvk_vX_image_view.c b/src/panfrost/vulkan/panvk_vX_image_view.c index 218f5700f41..b052e95439e 100644 --- a/src/panfrost/vulkan/panvk_vX_image_view.c +++ b/src/panfrost/vulkan/panvk_vX_image_view.c @@ -1,4 +1,5 @@ /* + * Copyright © 2025 Arm Ltd. * Copyright © 2021 Collabora Ltd. * * Derived from tu_image.c which is: @@ -286,6 +287,34 @@ prepare_attr_buf_descs(struct panvk_image_view *view) } #endif +static void +create_ms_views(struct panvk_device *dev, struct panvk_image_view *view, + const VkImageViewCreateInfo *pCreateInfo, + const VkAllocationCallbacks *pAllocator) +{ + struct panvk_image *source_img = + panvk_image_from_handle(vk_image_to_handle(view->vk.image)); + const VkImage *target_images = source_img->ms_imgs; + + VkImageViewCreateInfo create_info = *pCreateInfo; + + for (uint32_t idx = 0; idx < ARRAY_SIZE(source_img->ms_imgs); ++idx) { + if (target_images[idx] == VK_NULL_HANDLE) + continue; + + create_info.image = target_images[idx]; + + assert(vk_image_from_handle(create_info.image)->format == + source_img->vk.format); + assert(view->vk.format == create_info.format); + assert(!(vk_image_from_handle(create_info.image)->create_flags & + VK_IMAGE_CREATE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_BIT_EXT)); + + panvk_per_arch(CreateImageView)(panvk_device_to_handle(dev), &create_info, + pAllocator, &view->ms_views[idx]); + } +} + VKAPI_ATTR VkResult VKAPI_CALL panvk_per_arch(CreateImageView)(VkDevice _device, const VkImageViewCreateInfo *pCreateInfo, @@ -386,6 +415,10 @@ panvk_per_arch(CreateImageView)(VkDevice _device, prepare_attr_buf_descs(view); #endif + if (view->vk.image->create_flags & + VK_IMAGE_CREATE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_BIT_EXT) + create_ms_views(device, view, pCreateInfo, pAllocator); + *pView = panvk_image_view_to_handle(view); return VK_SUCCESS; @@ -404,6 +437,14 @@ panvk_per_arch(DestroyImageView)(VkDevice _device, VkImageView _view, if (!view) return; + if (view->vk.image->create_flags & + VK_IMAGE_CREATE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_BIT_EXT) { + for (uint32_t i = 0; i < ARRAY_SIZE(view->ms_views); ++i) { + panvk_per_arch(DestroyImageView)(_device, view->ms_views[i], + pAllocator); + } + } + panvk_pool_free_mem(&view->mem); vk_image_view_destroy(&device->vk, pAllocator, &view->vk); }