/* * Copyright © 2021 Collabora Ltd. * * Derived from tu_formats.c which is: * Copyright © 2016 Red Hat. * Copyright © 2016 Bas Nieuwenhuizen * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "panvk_private.h" #include "util/format_r11g11b10f.h" #include "util/format_srgb.h" #include "util/half_float.h" #include "vulkan/util/vk_format.h" #include "vk_format.h" #include "vk_util.h" #include "panfrost/lib/pan_texture.h" static void get_format_properties(struct panvk_physical_device *physical_device, VkFormat format, VkFormatProperties *out_properties) { struct panfrost_device *pdev = &physical_device->pdev; VkFormatFeatureFlags tex = 0, buffer = 0; enum pipe_format pfmt = vk_format_to_pipe_format(format); const struct panfrost_format fmt = pdev->formats[pfmt]; if (!pfmt || !fmt.hw) goto end; /* 3byte formats are not supported by the buffer <-> image copy helpers. */ if (util_format_get_blocksize(pfmt) == 3) goto end; /* We don't support compressed formats yet: this is causing trouble when * doing a vkCmdCopyImage() between a compressed and a non-compressed format * on a tiled/AFBC resource. */ if (util_format_is_compressed(pfmt)) goto end; buffer |= VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT; if (fmt.bind & PIPE_BIND_VERTEX_BUFFER) buffer |= VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT; if (fmt.bind & PIPE_BIND_SAMPLER_VIEW) { tex |= VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT | VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT; /* Integer formats only support nearest filtering */ if (!util_format_is_scaled(pfmt) && !util_format_is_pure_integer(pfmt)) tex |= VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT; buffer |= VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT; tex |= VK_FORMAT_FEATURE_BLIT_SRC_BIT; } if (fmt.bind & PIPE_BIND_RENDER_TARGET) { tex |= VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT; tex |= VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT; buffer |= VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT; /* Can always blend via blend shaders */ tex |= VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT; } if (fmt.bind & PIPE_BIND_DEPTH_STENCIL) tex |= VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT; end: out_properties->linearTilingFeatures = tex; out_properties->optimalTilingFeatures = tex; out_properties->bufferFeatures = buffer; } void panvk_GetPhysicalDeviceFormatProperties(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties *pFormatProperties) { VK_FROM_HANDLE(panvk_physical_device, physical_device, physicalDevice); get_format_properties(physical_device, format, pFormatProperties); } void panvk_GetPhysicalDeviceFormatProperties2(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2 *pFormatProperties) { VK_FROM_HANDLE(panvk_physical_device, physical_device, physicalDevice); get_format_properties(physical_device, format, &pFormatProperties->formatProperties); VkDrmFormatModifierPropertiesListEXT *list = vk_find_struct(pFormatProperties->pNext, DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT); if (list) { VK_OUTARRAY_MAKE(out, list->pDrmFormatModifierProperties, &list->drmFormatModifierCount); vk_outarray_append(&out, mod_props) { mod_props->drmFormatModifier = DRM_FORMAT_MOD_LINEAR; mod_props->drmFormatModifierPlaneCount = 1; } } } static VkResult get_image_format_properties(struct panvk_physical_device *physical_device, const VkPhysicalDeviceImageFormatInfo2 *info, VkImageFormatProperties *pImageFormatProperties, VkFormatFeatureFlags *p_feature_flags) { VkFormatProperties format_props; VkFormatFeatureFlags format_feature_flags; VkExtent3D maxExtent; uint32_t maxMipLevels; uint32_t maxArraySize; VkSampleCountFlags sampleCounts = VK_SAMPLE_COUNT_1_BIT; enum pipe_format format = vk_format_to_pipe_format(info->format); get_format_properties(physical_device, info->format, &format_props); switch (info->tiling) { case VK_IMAGE_TILING_LINEAR: format_feature_flags = format_props.linearTilingFeatures; break; case VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT: /* The only difference between optimal and linear is currently whether * depth/stencil attachments are allowed on depth/stencil formats. * There's no reason to allow importing depth/stencil textures, so just * disallow it and then this annoying edge case goes away. * * TODO: If anyone cares, we could enable this by looking at the * modifier and checking if it's LINEAR or not. */ if (util_format_is_depth_or_stencil(format)) goto unsupported; assert(format_props.optimalTilingFeatures == format_props.linearTilingFeatures); /* fallthrough */ case VK_IMAGE_TILING_OPTIMAL: format_feature_flags = format_props.optimalTilingFeatures; break; default: unreachable("bad VkPhysicalDeviceImageFormatInfo2"); } if (format_feature_flags == 0) goto unsupported; if (info->type != VK_IMAGE_TYPE_2D && util_format_is_depth_or_stencil(format)) goto unsupported; switch (info->type) { default: unreachable("bad vkimage type"); case VK_IMAGE_TYPE_1D: maxExtent.width = 16384; maxExtent.height = 1; maxExtent.depth = 1; maxMipLevels = 15; /* log2(maxWidth) + 1 */ maxArraySize = 2048; break; case VK_IMAGE_TYPE_2D: maxExtent.width = 16384; maxExtent.height = 16384; maxExtent.depth = 1; maxMipLevels = 15; /* log2(maxWidth) + 1 */ maxArraySize = 2048; break; case VK_IMAGE_TYPE_3D: maxExtent.width = 2048; maxExtent.height = 2048; maxExtent.depth = 2048; maxMipLevels = 12; /* log2(maxWidth) + 1 */ maxArraySize = 1; break; } if (info->tiling == VK_IMAGE_TILING_OPTIMAL && info->type == VK_IMAGE_TYPE_2D && (format_feature_flags & (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)) && !(info->flags & VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT) && !(info->usage & VK_IMAGE_USAGE_STORAGE_BIT)) { sampleCounts |= VK_SAMPLE_COUNT_4_BIT; } if (info->usage & VK_IMAGE_USAGE_SAMPLED_BIT) { if (!(format_feature_flags & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) { goto unsupported; } } if (info->usage & VK_IMAGE_USAGE_STORAGE_BIT) { if (!(format_feature_flags & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT)) { goto unsupported; } } if (info->usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) { if (!(format_feature_flags & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)) { goto unsupported; } } if (info->usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { if (!(format_feature_flags & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)) { goto unsupported; } } *pImageFormatProperties = (VkImageFormatProperties) { .maxExtent = maxExtent, .maxMipLevels = maxMipLevels, .maxArrayLayers = maxArraySize, .sampleCounts = sampleCounts, /* FINISHME: Accurately calculate * VkImageFormatProperties::maxResourceSize. */ .maxResourceSize = UINT32_MAX, }; if (p_feature_flags) *p_feature_flags = format_feature_flags; return VK_SUCCESS; unsupported: *pImageFormatProperties = (VkImageFormatProperties) { .maxExtent = { 0, 0, 0 }, .maxMipLevels = 0, .maxArrayLayers = 0, .sampleCounts = 0, .maxResourceSize = 0, }; return VK_ERROR_FORMAT_NOT_SUPPORTED; } VkResult panvk_GetPhysicalDeviceImageFormatProperties(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags createFlags, VkImageFormatProperties *pImageFormatProperties) { VK_FROM_HANDLE(panvk_physical_device, physical_device, physicalDevice); const VkPhysicalDeviceImageFormatInfo2 info = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2, .pNext = NULL, .format = format, .type = type, .tiling = tiling, .usage = usage, .flags = createFlags, }; return get_image_format_properties(physical_device, &info, pImageFormatProperties, NULL); } static VkResult panvk_get_external_image_format_properties(const struct panvk_physical_device *physical_device, const VkPhysicalDeviceImageFormatInfo2 *pImageFormatInfo, VkExternalMemoryHandleTypeFlagBits handleType, VkExternalMemoryProperties *external_properties) { VkExternalMemoryFeatureFlagBits flags = 0; VkExternalMemoryHandleTypeFlags export_flags = 0; VkExternalMemoryHandleTypeFlags compat_flags = 0; /* From the Vulkan 1.1.98 spec: * * If handleType is not compatible with the format, type, tiling, * usage, and flags specified in VkPhysicalDeviceImageFormatInfo2, * then vkGetPhysicalDeviceImageFormatProperties2 returns * VK_ERROR_FORMAT_NOT_SUPPORTED. */ switch (handleType) { case VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT: case VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT: switch (pImageFormatInfo->type) { case VK_IMAGE_TYPE_2D: flags = VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT | VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT | VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT; compat_flags = export_flags = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT | VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT; break; default: return vk_errorf(physical_device, VK_ERROR_FORMAT_NOT_SUPPORTED, "VkExternalMemoryTypeFlagBits(0x%x) unsupported for VkImageType(%d)", handleType, pImageFormatInfo->type); } break; case VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT: flags = VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT; compat_flags = VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT; break; default: return vk_errorf(physical_device, VK_ERROR_FORMAT_NOT_SUPPORTED, "VkExternalMemoryTypeFlagBits(0x%x) unsupported", handleType); } *external_properties = (VkExternalMemoryProperties) { .externalMemoryFeatures = flags, .exportFromImportedHandleTypes = export_flags, .compatibleHandleTypes = compat_flags, }; return VK_SUCCESS; } VkResult panvk_GetPhysicalDeviceImageFormatProperties2(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2 *base_info, VkImageFormatProperties2 *base_props) { VK_FROM_HANDLE(panvk_physical_device, physical_device, physicalDevice); const VkPhysicalDeviceExternalImageFormatInfo *external_info = NULL; const VkPhysicalDeviceImageViewImageFormatInfoEXT *image_view_info = NULL; VkExternalImageFormatProperties *external_props = NULL; VkFilterCubicImageViewImageFormatPropertiesEXT *cubic_props = NULL; VkFormatFeatureFlags format_feature_flags; VkSamplerYcbcrConversionImageFormatProperties *ycbcr_props = NULL; VkResult result; result = get_image_format_properties(physical_device, base_info, &base_props->imageFormatProperties, &format_feature_flags); if (result != VK_SUCCESS) return result; /* Extract input structs */ vk_foreach_struct_const(s, base_info->pNext) { switch (s->sType) { case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO: external_info = (const void *) s; break; case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_VIEW_IMAGE_FORMAT_INFO_EXT: image_view_info = (const void *) s; break; default: break; } } /* Extract output structs */ vk_foreach_struct(s, base_props->pNext) { switch (s->sType) { case VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES: external_props = (void *) s; break; case VK_STRUCTURE_TYPE_FILTER_CUBIC_IMAGE_VIEW_IMAGE_FORMAT_PROPERTIES_EXT: cubic_props = (void *) s; break; case VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_IMAGE_FORMAT_PROPERTIES: ycbcr_props = (void *) s; break; default: break; } } /* From the Vulkan 1.0.42 spec: * * If handleType is 0, vkGetPhysicalDeviceImageFormatProperties2 will * behave as if VkPhysicalDeviceExternalImageFormatInfo was not * present and VkExternalImageFormatProperties will be ignored. */ if (external_info && external_info->handleType != 0) { result = panvk_get_external_image_format_properties(physical_device, base_info, external_info->handleType, &external_props->externalMemoryProperties); if (result != VK_SUCCESS) goto fail; } if (cubic_props) { /* note: blob only allows cubic filtering for 2D and 2D array views * its likely we can enable it for 1D and CUBE, needs testing however */ if ((image_view_info->imageViewType == VK_IMAGE_VIEW_TYPE_2D || image_view_info->imageViewType == VK_IMAGE_VIEW_TYPE_2D_ARRAY) && (format_feature_flags & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_EXT)) { cubic_props->filterCubic = true; cubic_props->filterCubicMinmax = true; } else { cubic_props->filterCubic = false; cubic_props->filterCubicMinmax = false; } } if (ycbcr_props) ycbcr_props->combinedImageSamplerDescriptorCount = 1; return VK_SUCCESS; fail: if (result == VK_ERROR_FORMAT_NOT_SUPPORTED) { /* From the Vulkan 1.0.42 spec: * * If the combination of parameters to * vkGetPhysicalDeviceImageFormatProperties2 is not supported by * the implementation for use in vkCreateImage, then all members of * imageFormatProperties will be filled with zero. */ base_props->imageFormatProperties = (VkImageFormatProperties) {}; } return result; } void panvk_GetPhysicalDeviceSparseImageFormatProperties(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, uint32_t samples, VkImageUsageFlags usage, VkImageTiling tiling, uint32_t *pNumProperties, VkSparseImageFormatProperties *pProperties) { panvk_stub(); } void panvk_GetPhysicalDeviceSparseImageFormatProperties2(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2 *pFormatInfo, uint32_t *pPropertyCount, VkSparseImageFormatProperties2 *pProperties) { panvk_stub(); } void panvk_GetPhysicalDeviceExternalBufferProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalBufferInfo *pExternalBufferInfo, VkExternalBufferProperties *pExternalBufferProperties) { panvk_stub(); }