mesa/src/panfrost/vulkan/panvk_physical_device.c

1331 lines
49 KiB
C

/*
* Copyright © 2021 Collabora Ltd.
*
* Derived from tu_device.c which is:
* Copyright © 2016 Red Hat.
* Copyright © 2016 Bas Nieuwenhuizen
* Copyright © 2015 Intel Corporation
*
* SPDX-License-Identifier: MIT
*/
#include <sys/sysinfo.h>
#include "util/disk_cache.h"
#include "git_sha1.h"
#include "vk_device.h"
#include "vk_drm_syncobj.h"
#include "vk_format.h"
#include "vk_log.h"
#include "vk_util.h"
#include "panvk_device.h"
#include "panvk_entrypoints.h"
#include "panvk_instance.h"
#include "panvk_physical_device.h"
#include "panvk_wsi.h"
#include "pan_format.h"
#include "pan_props.h"
#include "genxml/gen_macros.h"
#define ARM_VENDOR_ID 0x13b5
#define MAX_VIEWPORTS 1
#define MAX_PUSH_DESCRIPTORS 32
/* We reserve one ubo for push constant, one for sysvals and one per-set for the
* descriptor metadata */
#define RESERVED_UBO_COUNT 6
#define MAX_INLINE_UNIFORM_BLOCK_DESCRIPTORS 32 - RESERVED_UBO_COUNT
#define MAX_INLINE_UNIFORM_BLOCK_SIZE (1 << 16)
static int
get_cache_uuid(uint16_t family, void *uuid)
{
uint32_t mesa_timestamp;
uint16_t f = family;
if (!disk_cache_get_function_timestamp(get_cache_uuid, &mesa_timestamp))
return -1;
memset(uuid, 0, VK_UUID_SIZE);
memcpy(uuid, &mesa_timestamp, 4);
memcpy((char *)uuid + 4, &f, 2);
snprintf((char *)uuid + 6, VK_UUID_SIZE - 10, "pan");
return 0;
}
static void
get_driver_uuid(void *uuid)
{
memset(uuid, 0, VK_UUID_SIZE);
snprintf(uuid, VK_UUID_SIZE, "panfrost");
}
static void
get_device_uuid(void *uuid)
{
memset(uuid, 0, VK_UUID_SIZE);
}
static void
get_device_extensions(const struct panvk_physical_device *device,
struct vk_device_extension_table *ext)
{
*ext = (struct vk_device_extension_table){
.KHR_copy_commands2 = true,
.KHR_shader_expect_assume = true,
.KHR_storage_buffer_storage_class = true,
.KHR_descriptor_update_template = true,
.KHR_driver_properties = true,
.KHR_push_descriptor = true,
#ifdef PANVK_USE_WSI_PLATFORM
.KHR_swapchain = true,
#endif
.KHR_synchronization2 = true,
.KHR_variable_pointers = true,
.EXT_custom_border_color = true,
.EXT_index_type_uint8 = true,
.EXT_vertex_attribute_divisor = true,
};
}
static void
get_features(const struct panvk_physical_device *device,
struct vk_features *features)
{
unsigned arch = pan_arch(device->kmod.props.gpu_prod_id);
*features = (struct vk_features){
/* Vulkan 1.0 */
.robustBufferAccess = true,
.fullDrawIndexUint32 = true,
.independentBlend = true,
.logicOp = true,
.wideLines = true,
.largePoints = true,
.textureCompressionETC2 = true,
.textureCompressionASTC_LDR = true,
.shaderUniformBufferArrayDynamicIndexing = true,
.shaderSampledImageArrayDynamicIndexing = true,
.shaderStorageBufferArrayDynamicIndexing = true,
.shaderStorageImageArrayDynamicIndexing = true,
/* Vulkan 1.1 */
.storageBuffer16BitAccess = false,
.uniformAndStorageBuffer16BitAccess = false,
.storagePushConstant16 = false,
.storageInputOutput16 = false,
.multiview = false,
.multiviewGeometryShader = false,
.multiviewTessellationShader = false,
.variablePointersStorageBuffer = true,
.variablePointers = true,
.protectedMemory = false,
.samplerYcbcrConversion = false,
.shaderDrawParameters = false,
/* Vulkan 1.2 */
.samplerMirrorClampToEdge = false,
.drawIndirectCount = false,
.storageBuffer8BitAccess = false,
.uniformAndStorageBuffer8BitAccess = false,
.storagePushConstant8 = false,
.shaderBufferInt64Atomics = false,
.shaderSharedInt64Atomics = false,
.shaderFloat16 = false,
.shaderInt8 = false,
.descriptorIndexing = false,
.shaderInputAttachmentArrayDynamicIndexing = false,
.shaderUniformTexelBufferArrayDynamicIndexing = false,
.shaderStorageTexelBufferArrayDynamicIndexing = false,
.shaderUniformBufferArrayNonUniformIndexing = false,
.shaderSampledImageArrayNonUniformIndexing = false,
.shaderStorageBufferArrayNonUniformIndexing = false,
.shaderStorageImageArrayNonUniformIndexing = false,
.shaderInputAttachmentArrayNonUniformIndexing = false,
.shaderUniformTexelBufferArrayNonUniformIndexing = false,
.shaderStorageTexelBufferArrayNonUniformIndexing = false,
.descriptorBindingUniformBufferUpdateAfterBind = false,
.descriptorBindingSampledImageUpdateAfterBind = false,
.descriptorBindingStorageImageUpdateAfterBind = false,
.descriptorBindingStorageBufferUpdateAfterBind = false,
.descriptorBindingUniformTexelBufferUpdateAfterBind = false,
.descriptorBindingStorageTexelBufferUpdateAfterBind = false,
.descriptorBindingUpdateUnusedWhilePending = false,
.descriptorBindingPartiallyBound = false,
.descriptorBindingVariableDescriptorCount = false,
.runtimeDescriptorArray = false,
.samplerFilterMinmax = false,
.scalarBlockLayout = false,
.imagelessFramebuffer = false,
.uniformBufferStandardLayout = false,
.shaderSubgroupExtendedTypes = false,
.separateDepthStencilLayouts = false,
.hostQueryReset = false,
.timelineSemaphore = false,
.bufferDeviceAddress = true,
.bufferDeviceAddressCaptureReplay = false,
.bufferDeviceAddressMultiDevice = false,
.vulkanMemoryModel = false,
.vulkanMemoryModelDeviceScope = false,
.vulkanMemoryModelAvailabilityVisibilityChains = false,
.shaderOutputViewportIndex = false,
.shaderOutputLayer = false,
.subgroupBroadcastDynamicId = false,
/* Vulkan 1.3 */
.robustImageAccess = false,
.inlineUniformBlock = false,
.descriptorBindingInlineUniformBlockUpdateAfterBind = false,
.pipelineCreationCacheControl = false,
.privateData = true,
.shaderDemoteToHelperInvocation = false,
.shaderTerminateInvocation = false,
.subgroupSizeControl = false,
.computeFullSubgroups = false,
.synchronization2 = true,
.textureCompressionASTC_HDR = false,
.shaderZeroInitializeWorkgroupMemory = false,
.dynamicRendering = false,
.shaderIntegerDotProduct = false,
.maintenance4 = false,
/* VK_EXT_index_type_uint8 */
.indexTypeUint8 = true,
/* VK_EXT_vertex_attribute_divisor */
.vertexAttributeInstanceRateDivisor = true,
.vertexAttributeInstanceRateZeroDivisor = true,
/* VK_EXT_depth_clip_enable */
.depthClipEnable = true,
/* VK_EXT_4444_formats */
.formatA4R4G4B4 = true,
.formatA4B4G4R4 = true,
/* VK_EXT_custom_border_color */
.customBorderColors = true,
/* v7 doesn't support AFBC(BGR). We need to tweak the texture swizzle to
* make it work, which forces us to apply the same swizzle on the border
* color, meaning we need to know the format when preparing the border
* color.
*/
.customBorderColorWithoutFormat = arch != 7,
/* VK_KHR_shader_expect_assume */
.shaderExpectAssume = true,
};
}
static void
get_device_properties(const struct panvk_physical_device *device,
struct vk_properties *properties)
{
/* HW supports MSAA 4, 8 and 16, but we limit ourselves to MSAA 4 for now. */
VkSampleCountFlags sample_counts =
VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT;
uint64_t os_page_size = 4096;
os_get_page_size(&os_page_size);
*properties = (struct vk_properties){
.apiVersion = panvk_get_vk_version(),
.driverVersion = vk_get_driver_version(),
.vendorID = ARM_VENDOR_ID,
/* Collect arch_major, arch_minor, arch_rev and product_major,
* as done by the Arm driver.
*/
.deviceID = device->kmod.props.gpu_prod_id << 16,
.deviceType = VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU,
/* Vulkan 1.0 limits */
/* Maximum texture dimension is 2^16. */
.maxImageDimension1D = (1 << 16),
.maxImageDimension2D = (1 << 16),
.maxImageDimension3D = (1 << 16),
.maxImageDimensionCube = (1 << 16),
.maxImageArrayLayers = (1 << 16),
/* Currently limited by the 1D texture size, which is 2^16.
* TODO: If we expose buffer views as 2D textures, we can increase the
* limit.
*/
.maxTexelBufferElements = (1 << 16),
/* Each uniform entry is 16-byte and the number of entries is encoded in a
* 12-bit field, with the minus(1) modifier, which gives 2^20.
*/
.maxUniformBufferRange = 1 << 20,
/* Storage buffer access is lowered to globals, so there's no limit here,
* except for the SW-descriptor we use to encode storage buffer
* descriptors, where the size is a 32-bit field.
*/
.maxStorageBufferRange = UINT32_MAX,
/* 128 bytes of push constants, so we're aligned with the minimum Vulkan
* requirements.
*/
.maxPushConstantsSize = 128,
/* There's no HW limit here. Should we advertize something smaller? */
.maxMemoryAllocationCount = UINT32_MAX,
/* Again, no hardware limit, but most drivers seem to advertive 64k. */
.maxSamplerAllocationCount = 64 * 1024,
/* A cache line. */
.bufferImageGranularity = 64,
/* Sparse binding not supported yet. */
.sparseAddressSpaceSize = 0,
/* Software limit. Pick the minimum required by Vulkan, because Bifrost
* GPUs don't have unified descriptor tables, which forces us to
* agregatte all descriptors from all sets and dispatch them to per-type
* descriptor tables emitted at draw/dispatch time.
* The more sets we support the more copies we are likely to have to do
* at draw time.
*/
.maxBoundDescriptorSets = 4,
/* MALI_RENDERER_STATE::sampler_count is 16-bit. */
.maxPerStageDescriptorSamplers = UINT16_MAX,
.maxDescriptorSetSamplers = UINT16_MAX,
/* MALI_RENDERER_STATE::uniform_buffer_count is 8-bit. We reserve 32 slots
* for our internal UBOs.
*/
.maxPerStageDescriptorUniformBuffers = UINT8_MAX - 32,
.maxDescriptorSetUniformBuffers = UINT8_MAX - 32,
/* SSBOs are limited by the size of a uniform buffer which contains our
* panvk_ssbo_desc objects.
* panvk_ssbo_desc is 16-byte, and each uniform entry in the Mali UBO is
* 16-byte too. The number of entries is encoded in a 12-bit field, with
* a minus(1) modifier, which gives a maximum of 2^12 SSBO
* descriptors.
*/
.maxPerStageDescriptorStorageBuffers = 1 << 12,
.maxDescriptorSetStorageBuffers = 1 << 12,
/* MALI_RENDERER_STATE::sampler_count is 16-bit. */
.maxPerStageDescriptorSampledImages = UINT16_MAX,
.maxDescriptorSetSampledImages = UINT16_MAX,
/* MALI_ATTRIBUTE::buffer_index is 9-bit, and each image takes two
* MALI_ATTRIBUTE_BUFFER slots, which gives a maximum of (1 << 8) images.
*/
.maxPerStageDescriptorStorageImages = 1 << 8,
.maxDescriptorSetStorageImages = 1 << 8,
/* A maximum of 8 color render targets, and one depth-stencil render
* target.
*/
.maxPerStageDescriptorInputAttachments = 9,
.maxDescriptorSetInputAttachments = 9,
/* Could be the sum of all maxPerStageXxx values, but we limit ourselves
* to 2^16 to make things simpler.
*/
.maxPerStageResources = 1 << 16,
/* Software limits to keep VkCommandBuffer tracking sane. */
.maxDescriptorSetUniformBuffersDynamic = 16,
.maxDescriptorSetStorageBuffersDynamic = 8,
/* Software limit to keep VkCommandBuffer tracking sane. The HW supports
* up to 2^9 vertex attributes.
*/
.maxVertexInputAttributes = 16,
.maxVertexInputBindings = 16,
/* MALI_ATTRIBUTE::offset is 32-bit. */
.maxVertexInputAttributeOffset = UINT32_MAX,
/* MALI_ATTRIBUTE_BUFFER::stride is 32-bit. */
.maxVertexInputBindingStride = UINT32_MAX,
/* 32 vec4 varyings. */
.maxVertexOutputComponents = 128,
/* Tesselation shaders not supported. */
.maxTessellationGenerationLevel = 0,
.maxTessellationPatchSize = 0,
.maxTessellationControlPerVertexInputComponents = 0,
.maxTessellationControlPerVertexOutputComponents = 0,
.maxTessellationControlPerPatchOutputComponents = 0,
.maxTessellationControlTotalOutputComponents = 0,
.maxTessellationEvaluationInputComponents = 0,
.maxTessellationEvaluationOutputComponents = 0,
/* Geometry shaders not supported. */
.maxGeometryShaderInvocations = 0,
.maxGeometryInputComponents = 0,
.maxGeometryOutputComponents = 0,
.maxGeometryOutputVertices = 0,
.maxGeometryTotalOutputComponents = 0,
/* 32 vec4 varyings. */
.maxFragmentInputComponents = 128,
/* 8 render targets. */
.maxFragmentOutputAttachments = 8,
/* We don't support dual source blending yet. */
.maxFragmentDualSrcAttachments = 0,
/* 8 render targets, 2^12 storage buffers and 2^8 storage images (see
* above).
*/
.maxFragmentCombinedOutputResources = 8 + (1 << 12) + (1 << 8),
/* MALI_LOCAL_STORAGE::wls_size_{base,scale} allows us to have up to
* (7 << 30) bytes of shared memory, but we cap it to 32K as it doesn't
* really make sense to expose this amount of memory, especially since
* it's backed by global memory anyway.
*/
.maxComputeSharedMemorySize = 32768,
/* Software limit to meet Vulkan 1.0 requirements. We split the
* dispatch in several jobs if it's too big.
*/
.maxComputeWorkGroupCount = {65535, 65535, 65535},
/* We have 10 bits to encode the local-size, and there's a minus(1)
* modifier, so, a size of 1 takes no bit.
*/
.maxComputeWorkGroupInvocations = 1 << 10,
.maxComputeWorkGroupSize = {1 << 10, 1 << 10, 1 << 10},
/* 8-bit subpixel precision. */
.subPixelPrecisionBits = 8,
.subTexelPrecisionBits = 8,
.mipmapPrecisionBits = 8,
/* Software limit. */
.maxDrawIndexedIndexValue = UINT32_MAX,
/* Make it one for now. */
.maxDrawIndirectCount = 1,
.maxSamplerLodBias = 255,
.maxSamplerAnisotropy = 16,
.maxViewports = 1,
/* Same as the framebuffer limit. */
.maxViewportDimensions = {(1 << 14), (1 << 14)},
/* Encoded in a 16-bit signed integer. */
.viewportBoundsRange = {INT16_MIN, INT16_MAX},
.viewportSubPixelBits = 0,
/* Align on a page. */
.minMemoryMapAlignment = os_page_size,
/* Some compressed texture formats require 128-byte alignment. */
.minTexelBufferOffsetAlignment = 64,
/* Always aligned on a uniform slot (vec4). */
.minUniformBufferOffsetAlignment = 16,
/* Lowered to global accesses, which happen at the 32-bit granularity. */
.minStorageBufferOffsetAlignment = 4,
/* Signed 4-bit value. */
.minTexelOffset = -8,
.maxTexelOffset = 7,
.minTexelGatherOffset = -8,
.maxTexelGatherOffset = 7,
.minInterpolationOffset = -0.5,
.maxInterpolationOffset = 0.5,
.subPixelInterpolationOffsetBits = 8,
.maxFramebufferWidth = (1 << 14),
.maxFramebufferHeight = (1 << 14),
.maxFramebufferLayers = 256,
.framebufferColorSampleCounts = sample_counts,
.framebufferDepthSampleCounts = sample_counts,
.framebufferStencilSampleCounts = sample_counts,
.framebufferNoAttachmentsSampleCounts = sample_counts,
.maxColorAttachments = 8,
.sampledImageColorSampleCounts = sample_counts,
.sampledImageIntegerSampleCounts = VK_SAMPLE_COUNT_1_BIT,
.sampledImageDepthSampleCounts = sample_counts,
.sampledImageStencilSampleCounts = sample_counts,
.storageImageSampleCounts = VK_SAMPLE_COUNT_1_BIT,
.maxSampleMaskWords = 1,
.timestampComputeAndGraphics = false,
.timestampPeriod = 0,
.maxClipDistances = 0,
.maxCullDistances = 0,
.maxCombinedClipAndCullDistances = 0,
.discreteQueuePriorities = 1,
.pointSizeRange = {0.125, 4095.9375},
.lineWidthRange = {0.0, 7.9921875},
.pointSizeGranularity = (1.0 / 16.0),
.lineWidthGranularity = (1.0 / 128.0),
.strictLines = false,
.standardSampleLocations = true,
.optimalBufferCopyOffsetAlignment = 64,
.optimalBufferCopyRowPitchAlignment = 64,
.nonCoherentAtomSize = 64,
/* Vulkan 1.0 sparse properties */
.sparseResidencyNonResidentStrict = false,
.sparseResidencyAlignedMipSize = false,
.sparseResidencyStandard2DBlockShape = false,
.sparseResidencyStandard2DMultisampleBlockShape = false,
.sparseResidencyStandard3DBlockShape = false,
/* Vulkan 1.1 properties */
/* XXX: 1.1 support */
.subgroupSize = 8,
.subgroupSupportedStages = VK_SHADER_STAGE_ALL,
.subgroupSupportedOperations =
VK_SUBGROUP_FEATURE_ARITHMETIC_BIT | VK_SUBGROUP_FEATURE_BALLOT_BIT |
VK_SUBGROUP_FEATURE_BASIC_BIT | VK_SUBGROUP_FEATURE_CLUSTERED_BIT |
VK_SUBGROUP_FEATURE_QUAD_BIT | VK_SUBGROUP_FEATURE_SHUFFLE_BIT |
VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT |
VK_SUBGROUP_FEATURE_VOTE_BIT,
.subgroupQuadOperationsInAllStages = false,
.pointClippingBehavior = VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES,
.maxMultiviewViewCount = 0,
.maxMultiviewInstanceIndex = 0,
.protectedNoFault = false,
/* Make sure everything is addressable by a signed 32-bit int, and
* our largest descriptors are 96 bytes. */
.maxPerSetDescriptors = (1ull << 31) / 96,
/* Our buffer size fields allow only this much */
.maxMemoryAllocationSize = UINT32_MAX,
/* Vulkan 1.2 properties */
/* XXX: 1.2 support */
/* XXX: VK_KHR_depth_stencil_resolve */
.supportedDepthResolveModes = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT,
.supportedStencilResolveModes = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT,
.independentResolveNone = true,
.independentResolve = true,
/* VK_KHR_driver_properties */
.driverID = VK_DRIVER_ID_MESA_PANVK,
.conformanceVersion = (VkConformanceVersion){0, 0, 0, 0},
/* XXX: VK_KHR_shader_float_controls */
.denormBehaviorIndependence = VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL,
.roundingModeIndependence = VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL,
.shaderSignedZeroInfNanPreserveFloat16 = true,
.shaderSignedZeroInfNanPreserveFloat32 = true,
.shaderSignedZeroInfNanPreserveFloat64 = false,
.shaderDenormPreserveFloat16 = true,
.shaderDenormPreserveFloat32 = true,
.shaderDenormPreserveFloat64 = false,
.shaderDenormFlushToZeroFloat16 = true,
.shaderDenormFlushToZeroFloat32 = true,
.shaderDenormFlushToZeroFloat64 = false,
.shaderRoundingModeRTEFloat16 = true,
.shaderRoundingModeRTEFloat32 = true,
.shaderRoundingModeRTEFloat64 = false,
.shaderRoundingModeRTZFloat16 = true,
.shaderRoundingModeRTZFloat32 = true,
.shaderRoundingModeRTZFloat64 = false,
/* XXX: VK_EXT_descriptor_indexing */
.maxUpdateAfterBindDescriptorsInAllPools = 0,
.shaderUniformBufferArrayNonUniformIndexingNative = false,
.shaderSampledImageArrayNonUniformIndexingNative = false,
.shaderStorageBufferArrayNonUniformIndexingNative = false,
.shaderStorageImageArrayNonUniformIndexingNative = false,
.shaderInputAttachmentArrayNonUniformIndexingNative = false,
.robustBufferAccessUpdateAfterBind = false,
.quadDivergentImplicitLod = false,
.maxPerStageDescriptorUpdateAfterBindSamplers = 0,
.maxPerStageDescriptorUpdateAfterBindUniformBuffers = 0,
.maxPerStageDescriptorUpdateAfterBindStorageBuffers = 0,
.maxPerStageDescriptorUpdateAfterBindSampledImages = 0,
.maxPerStageDescriptorUpdateAfterBindStorageImages = 0,
.maxPerStageDescriptorUpdateAfterBindInputAttachments = 0,
.maxPerStageDescriptorUpdateAfterBindInputAttachments = 0,
.maxPerStageUpdateAfterBindResources = 0,
.maxDescriptorSetUpdateAfterBindSamplers = 0,
.maxDescriptorSetUpdateAfterBindUniformBuffers = 0,
.maxDescriptorSetUpdateAfterBindUniformBuffersDynamic = 0,
.maxDescriptorSetUpdateAfterBindStorageBuffers = 0,
.maxDescriptorSetUpdateAfterBindStorageBuffersDynamic = 0,
.maxDescriptorSetUpdateAfterBindSampledImages = 0,
.maxDescriptorSetUpdateAfterBindStorageImages = 0,
.maxDescriptorSetUpdateAfterBindInputAttachments = 0,
/* XXX: VK_EXT_sampler_filter_minmax */
.filterMinmaxSingleComponentFormats = false,
.filterMinmaxImageComponentMapping = false,
/* XXX: VK_KHR_timeline_semaphore */
.maxTimelineSemaphoreValueDifference = INT64_MAX,
.framebufferIntegerColorSampleCounts = sample_counts,
/* Vulkan 1.3 properties */
/* XXX: 1.3 support */
/* XXX: VK_EXT_subgroup_size_control */
.minSubgroupSize = 8,
.maxSubgroupSize = 8,
.maxComputeWorkgroupSubgroups = 48,
.requiredSubgroupSizeStages = VK_SHADER_STAGE_ALL,
/* XXX: VK_EXT_inline_uniform_block */
.maxInlineUniformBlockSize = MAX_INLINE_UNIFORM_BLOCK_SIZE,
.maxPerStageDescriptorInlineUniformBlocks =
MAX_INLINE_UNIFORM_BLOCK_DESCRIPTORS,
.maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks =
MAX_INLINE_UNIFORM_BLOCK_DESCRIPTORS,
.maxDescriptorSetInlineUniformBlocks =
MAX_INLINE_UNIFORM_BLOCK_DESCRIPTORS,
.maxDescriptorSetUpdateAfterBindInlineUniformBlocks =
MAX_INLINE_UNIFORM_BLOCK_DESCRIPTORS,
.maxInlineUniformTotalSize =
MAX_INLINE_UNIFORM_BLOCK_DESCRIPTORS * MAX_INLINE_UNIFORM_BLOCK_SIZE,
/* XXX: VK_KHR_shader_integer_dot_product */
.integerDotProduct8BitUnsignedAccelerated = true,
.integerDotProduct8BitSignedAccelerated = true,
.integerDotProduct4x8BitPackedUnsignedAccelerated = true,
.integerDotProduct4x8BitPackedSignedAccelerated = true,
/* XXX: VK_EXT_texel_buffer_alignment */
.storageTexelBufferOffsetAlignmentBytes = 64,
.storageTexelBufferOffsetSingleTexelAlignment = false,
.uniformTexelBufferOffsetAlignmentBytes = 4,
.uniformTexelBufferOffsetSingleTexelAlignment = true,
/* XXX: VK_KHR_maintenance4 */
.maxBufferSize = 1 << 30,
/* VK_EXT_custom_border_color */
.maxCustomBorderColorSamplers = 32768,
/* VK_KHR_vertex_attribute_divisor */
/* We will have to restrict this a bit for multiview */
.maxVertexAttribDivisor = UINT32_MAX,
.supportsNonZeroFirstInstance = false,
/* VK_KHR_push_descriptor */
.maxPushDescriptors = MAX_PUSH_DESCRIPTORS,
};
snprintf(properties->deviceName, sizeof(properties->deviceName), "%s",
device->name);
memcpy(properties->pipelineCacheUUID, device->cache_uuid, VK_UUID_SIZE);
memcpy(properties->driverUUID, device->driver_uuid, VK_UUID_SIZE);
memcpy(properties->deviceUUID, device->device_uuid, VK_UUID_SIZE);
snprintf(properties->driverName, VK_MAX_DRIVER_NAME_SIZE, "panvk");
snprintf(properties->driverInfo, VK_MAX_DRIVER_INFO_SIZE,
"Mesa " PACKAGE_VERSION MESA_GIT_SHA1);
}
void
panvk_physical_device_finish(struct panvk_physical_device *device)
{
panvk_wsi_finish(device);
pan_kmod_dev_destroy(device->kmod.dev);
if (device->master_fd != -1)
close(device->master_fd);
vk_physical_device_finish(&device->vk);
}
VkResult
panvk_physical_device_init(struct panvk_physical_device *device,
struct panvk_instance *instance,
drmDevicePtr drm_device)
{
const char *path = drm_device->nodes[DRM_NODE_RENDER];
VkResult result = VK_SUCCESS;
drmVersionPtr version;
int fd;
int master_fd = -1;
if (!getenv("PAN_I_WANT_A_BROKEN_VULKAN_DRIVER")) {
return vk_errorf(
instance, VK_ERROR_INCOMPATIBLE_DRIVER,
"WARNING: panvk is not a conformant vulkan implementation, "
"pass PAN_I_WANT_A_BROKEN_VULKAN_DRIVER=1 if you know what you're doing.");
}
fd = open(path, O_RDWR | O_CLOEXEC);
if (fd < 0) {
return vk_errorf(instance, VK_ERROR_INCOMPATIBLE_DRIVER,
"failed to open device %s", path);
}
version = drmGetVersion(fd);
if (!version) {
close(fd);
return vk_errorf(instance, VK_ERROR_INCOMPATIBLE_DRIVER,
"failed to query kernel driver version for device %s",
path);
}
if (strcmp(version->name, "panfrost")) {
drmFreeVersion(version);
close(fd);
return vk_errorf(instance, VK_ERROR_INCOMPATIBLE_DRIVER,
"device %s does not use the panfrost kernel driver",
path);
}
drmFreeVersion(version);
if (instance->debug_flags & PANVK_DEBUG_STARTUP)
vk_logi(VK_LOG_NO_OBJS(instance), "Found compatible device '%s'.", path);
device->kmod.dev = pan_kmod_dev_create(fd, PAN_KMOD_DEV_FLAG_OWNS_FD,
&instance->kmod.allocator);
pan_kmod_dev_query_props(device->kmod.dev, &device->kmod.props);
unsigned arch = pan_arch(device->kmod.props.gpu_prod_id);
if (arch <= 5 || arch >= 8) {
result = vk_errorf(instance, VK_ERROR_INCOMPATIBLE_DRIVER,
"%s not supported", device->model->name);
goto fail;
}
if (instance->vk.enabled_extensions.KHR_display) {
master_fd = open(drm_device->nodes[DRM_NODE_PRIMARY], O_RDWR | O_CLOEXEC);
if (master_fd >= 0) {
/* TODO: free master_fd is accel is not working? */
}
}
device->master_fd = master_fd;
device->model = panfrost_get_model(device->kmod.props.gpu_prod_id,
device->kmod.props.gpu_variant);
device->formats.all = panfrost_format_table(arch);
device->formats.blendable = panfrost_blendable_format_table(arch);
memset(device->name, 0, sizeof(device->name));
sprintf(device->name, "%s", device->model->name);
if (get_cache_uuid(device->kmod.props.gpu_prod_id, device->cache_uuid)) {
result = vk_errorf(instance, VK_ERROR_INITIALIZATION_FAILED,
"cannot generate UUID");
goto fail;
}
vk_warn_non_conformant_implementation("panvk");
get_driver_uuid(&device->driver_uuid);
get_device_uuid(&device->device_uuid);
device->drm_syncobj_type = vk_drm_syncobj_get_type(device->kmod.dev->fd);
/* We don't support timelines in the uAPI yet and we don't want it getting
* suddenly turned on by vk_drm_syncobj_get_type() without us adding panvk
* code for it first.
*/
device->drm_syncobj_type.features &= ~VK_SYNC_FEATURE_TIMELINE;
struct vk_device_extension_table supported_extensions;
get_device_extensions(device, &supported_extensions);
struct vk_features supported_features;
get_features(device, &supported_features);
struct vk_properties properties;
get_device_properties(device, &properties);
struct vk_physical_device_dispatch_table dispatch_table;
vk_physical_device_dispatch_table_from_entrypoints(
&dispatch_table, &panvk_physical_device_entrypoints, true);
vk_physical_device_dispatch_table_from_entrypoints(
&dispatch_table, &wsi_physical_device_entrypoints, false);
result = vk_physical_device_init(&device->vk, &instance->vk,
&supported_extensions, &supported_features,
&properties, &dispatch_table);
if (result != VK_SUCCESS) {
vk_error(instance, result);
goto fail;
}
device->sync_types[0] = &device->drm_syncobj_type;
device->sync_types[1] = NULL;
device->vk.supported_sync_types = device->sync_types;
result = panvk_wsi_init(device);
if (result != VK_SUCCESS) {
vk_error(instance, result);
goto fail;
}
return VK_SUCCESS;
fail:
if (device->vk.instance)
vk_physical_device_finish(&device->vk);
if (device->kmod.dev)
pan_kmod_dev_destroy(device->kmod.dev);
if (fd != -1)
close(fd);
if (master_fd != -1)
close(master_fd);
return result;
}
static const VkQueueFamilyProperties panvk_queue_family_properties = {
.queueFlags =
VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT,
.queueCount = 1,
.timestampValidBits = 0,
.minImageTransferGranularity = {1, 1, 1},
};
VKAPI_ATTR void VKAPI_CALL
panvk_GetPhysicalDeviceQueueFamilyProperties2(
VkPhysicalDevice physicalDevice, uint32_t *pQueueFamilyPropertyCount,
VkQueueFamilyProperties2 *pQueueFamilyProperties)
{
VK_OUTARRAY_MAKE_TYPED(VkQueueFamilyProperties2, out, pQueueFamilyProperties,
pQueueFamilyPropertyCount);
vk_outarray_append_typed(VkQueueFamilyProperties2, &out, p)
{
p->queueFamilyProperties = panvk_queue_family_properties;
}
}
static uint64_t
get_system_heap_size()
{
struct sysinfo info;
sysinfo(&info);
uint64_t total_ram = (uint64_t)info.totalram * info.mem_unit;
/* We don't want to burn too much ram with the GPU. If the user has 4GiB
* or less, we use at most half. If they have more than 4GiB, we use 3/4.
*/
uint64_t available_ram;
if (total_ram <= 4ull * 1024 * 1024 * 1024)
available_ram = total_ram / 2;
else
available_ram = total_ram * 3 / 4;
return available_ram;
}
VKAPI_ATTR void VKAPI_CALL
panvk_GetPhysicalDeviceMemoryProperties2(
VkPhysicalDevice physicalDevice,
VkPhysicalDeviceMemoryProperties2 *pMemoryProperties)
{
pMemoryProperties->memoryProperties = (VkPhysicalDeviceMemoryProperties){
.memoryHeapCount = 1,
.memoryHeaps[0].size = get_system_heap_size(),
.memoryHeaps[0].flags = VK_MEMORY_HEAP_DEVICE_LOCAL_BIT,
.memoryTypeCount = 1,
.memoryTypes[0].propertyFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
.memoryTypes[0].heapIndex = 0,
};
}
VKAPI_ATTR void VKAPI_CALL
panvk_GetPhysicalDeviceExternalSemaphoreProperties(
VkPhysicalDevice physicalDevice,
const VkPhysicalDeviceExternalSemaphoreInfo *pExternalSemaphoreInfo,
VkExternalSemaphoreProperties *pExternalSemaphoreProperties)
{
if ((pExternalSemaphoreInfo->handleType ==
VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT ||
pExternalSemaphoreInfo->handleType ==
VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT)) {
pExternalSemaphoreProperties->exportFromImportedHandleTypes =
VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT |
VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
pExternalSemaphoreProperties->compatibleHandleTypes =
VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT |
VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
pExternalSemaphoreProperties->externalSemaphoreFeatures =
VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT |
VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT;
} else {
pExternalSemaphoreProperties->exportFromImportedHandleTypes = 0;
pExternalSemaphoreProperties->compatibleHandleTypes = 0;
pExternalSemaphoreProperties->externalSemaphoreFeatures = 0;
}
}
VKAPI_ATTR void VKAPI_CALL
panvk_GetPhysicalDeviceExternalFenceProperties(
VkPhysicalDevice physicalDevice,
const VkPhysicalDeviceExternalFenceInfo *pExternalFenceInfo,
VkExternalFenceProperties *pExternalFenceProperties)
{
pExternalFenceProperties->exportFromImportedHandleTypes = 0;
pExternalFenceProperties->compatibleHandleTypes = 0;
pExternalFenceProperties->externalFenceFeatures = 0;
}
#define DEVICE_PER_ARCH_FUNCS(_ver) \
VkResult panvk_v##_ver##_create_device( \
struct panvk_physical_device *physical_device, \
const VkDeviceCreateInfo *pCreateInfo, \
const VkAllocationCallbacks *pAllocator, VkDevice *pDevice); \
\
void panvk_v##_ver##_destroy_device( \
struct panvk_device *device, const VkAllocationCallbacks *pAllocator)
DEVICE_PER_ARCH_FUNCS(6);
DEVICE_PER_ARCH_FUNCS(7);
VKAPI_ATTR VkResult VKAPI_CALL
panvk_CreateDevice(VkPhysicalDevice physicalDevice,
const VkDeviceCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator, VkDevice *pDevice)
{
VK_FROM_HANDLE(panvk_physical_device, physical_device, physicalDevice);
unsigned arch = pan_arch(physical_device->kmod.props.gpu_prod_id);
VkResult result = VK_ERROR_INITIALIZATION_FAILED;
panvk_arch_dispatch_ret(arch, create_device, result, physical_device,
pCreateInfo, pAllocator, pDevice);
return result;
}
VKAPI_ATTR void VKAPI_CALL
panvk_DestroyDevice(VkDevice _device, const VkAllocationCallbacks *pAllocator)
{
VK_FROM_HANDLE(panvk_device, device, _device);
struct panvk_physical_device *physical_device =
to_panvk_physical_device(device->vk.physical);
unsigned arch = pan_arch(physical_device->kmod.props.gpu_prod_id);
panvk_arch_dispatch(arch, destroy_device, device, pAllocator);
}
static void
get_format_properties(struct panvk_physical_device *physical_device,
VkFormat format, VkFormatProperties *out_properties)
{
VkFormatFeatureFlags tex = 0, buffer = 0;
enum pipe_format pfmt = vk_format_to_pipe_format(format);
const struct panfrost_format fmt = physical_device->formats.all[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;
/* Reject sRGB formats (see
* https://github.com/KhronosGroup/Vulkan-Docs/issues/2214).
*/
if ((fmt.bind & PAN_BIND_VERTEX_BUFFER) && !util_format_is_srgb(pfmt))
buffer |= VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT;
if (fmt.bind & PAN_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;
}
/* SNORM rendering isn't working yet, disable */
if (fmt.bind & PAN_BIND_RENDER_TARGET && !util_format_is_snorm(pfmt)) {
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 & PAN_BIND_DEPTH_STENCIL)
tex |= VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
end:
out_properties->linearTilingFeatures = tex;
out_properties->optimalTilingFeatures = tex;
out_properties->bufferFeatures = buffer;
}
VKAPI_ATTR void VKAPI_CALL
panvk_GetPhysicalDeviceFormatProperties(VkPhysicalDevice physicalDevice,
VkFormat format,
VkFormatProperties *pFormatProperties)
{
VK_FROM_HANDLE(panvk_physical_device, physical_device, physicalDevice);
get_format_properties(physical_device, format, pFormatProperties);
}
VKAPI_ATTR void VKAPI_CALL
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_TYPED(VkDrmFormatModifierPropertiesEXT, out,
list->pDrmFormatModifierProperties,
&list->drmFormatModifierCount);
vk_outarray_append_typed(VkDrmFormatModifierProperties2EXT, &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;
}
VKAPI_ATTR VkResult VKAPI_CALL
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;
}
VKAPI_ATTR VkResult VKAPI_CALL
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) {
VkExternalImageFormatProperties fallback_external_props;
if (!external_props) {
memset(&fallback_external_props, 0, sizeof(fallback_external_props));
external_props = &fallback_external_props;
}
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;
}
VKAPI_ATTR void VKAPI_CALL
panvk_GetPhysicalDeviceSparseImageFormatProperties(
VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type,
VkSampleCountFlagBits samples, VkImageUsageFlags usage, VkImageTiling tiling,
uint32_t *pNumProperties, VkSparseImageFormatProperties *pProperties)
{
/* Sparse images are not yet supported. */
*pNumProperties = 0;
}
VKAPI_ATTR void VKAPI_CALL
panvk_GetPhysicalDeviceSparseImageFormatProperties2(
VkPhysicalDevice physicalDevice,
const VkPhysicalDeviceSparseImageFormatInfo2 *pFormatInfo,
uint32_t *pPropertyCount, VkSparseImageFormatProperties2 *pProperties)
{
/* Sparse images are not yet supported. */
*pPropertyCount = 0;
}
VKAPI_ATTR void VKAPI_CALL
panvk_GetPhysicalDeviceExternalBufferProperties(
VkPhysicalDevice physicalDevice,
const VkPhysicalDeviceExternalBufferInfo *pExternalBufferInfo,
VkExternalBufferProperties *pExternalBufferProperties)
{
panvk_stub();
}