| /* |
| * Copyright © 2021 Collabora Ltd. |
| * |
| * Derived from tu_device.c which is: |
| * Copyright © 2016 Red Hat. |
| * Copyright © 2016 Bas Nieuwenhuizen |
| * Copyright © 2015 Intel Corporation |
| * |
| * 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 "pan_bo.h" |
| #include "pan_encoder.h" |
| #include "pan_util.h" |
| #include "vk_common_entrypoints.h" |
| #include "vk_cmd_enqueue_entrypoints.h" |
| |
| #include <fcntl.h> |
| #include <libsync.h> |
| #include <stdbool.h> |
| #include <string.h> |
| #include <sys/mman.h> |
| #include <sys/sysinfo.h> |
| #include <unistd.h> |
| #include <xf86drm.h> |
| |
| #include "drm-uapi/panfrost_drm.h" |
| |
| #include "util/u_debug.h" |
| #include "util/disk_cache.h" |
| #include "util/strtod.h" |
| #include "vk_format.h" |
| #include "vk_drm_syncobj.h" |
| #include "vk_util.h" |
| |
| #ifdef VK_USE_PLATFORM_WAYLAND_KHR |
| #include <wayland-client.h> |
| #include "wayland-drm-client-protocol.h" |
| #endif |
| |
| #include "panvk_cs.h" |
| |
| VkResult |
| _panvk_device_set_lost(struct panvk_device *device, |
| const char *file, int line, |
| const char *msg, ...) |
| { |
| /* Set the flag indicating that waits should return in finite time even |
| * after device loss. |
| */ |
| p_atomic_inc(&device->_lost); |
| |
| /* TODO: Report the log message through VkDebugReportCallbackEXT instead */ |
| fprintf(stderr, "%s:%d: ", file, line); |
| va_list ap; |
| va_start(ap, msg); |
| vfprintf(stderr, msg, ap); |
| va_end(ap); |
| |
| if (debug_get_bool_option("PANVK_ABORT_ON_DEVICE_LOSS", false)) |
| abort(); |
| |
| return VK_ERROR_DEVICE_LOST; |
| } |
| |
| static int |
| panvk_device_get_cache_uuid(uint16_t family, void *uuid) |
| { |
| uint32_t mesa_timestamp; |
| uint16_t f = family; |
| |
| if (!disk_cache_get_function_timestamp(panvk_device_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 |
| panvk_get_driver_uuid(void *uuid) |
| { |
| memset(uuid, 0, VK_UUID_SIZE); |
| snprintf(uuid, VK_UUID_SIZE, "panfrost"); |
| } |
| |
| static void |
| panvk_get_device_uuid(void *uuid) |
| { |
| memset(uuid, 0, VK_UUID_SIZE); |
| } |
| |
| static const struct debug_control panvk_debug_options[] = { |
| { "startup", PANVK_DEBUG_STARTUP }, |
| { "nir", PANVK_DEBUG_NIR }, |
| { "trace", PANVK_DEBUG_TRACE }, |
| { "sync", PANVK_DEBUG_SYNC }, |
| { "afbc", PANVK_DEBUG_AFBC }, |
| { "linear", PANVK_DEBUG_LINEAR }, |
| { "dump", PANVK_DEBUG_DUMP }, |
| { NULL, 0 } |
| }; |
| |
| #if defined(VK_USE_PLATFORM_WAYLAND_KHR) |
| #define PANVK_USE_WSI_PLATFORM |
| #endif |
| |
| #define PANVK_API_VERSION VK_MAKE_VERSION(1, 0, VK_HEADER_VERSION) |
| |
| VkResult |
| panvk_EnumerateInstanceVersion(uint32_t *pApiVersion) |
| { |
| *pApiVersion = PANVK_API_VERSION; |
| return VK_SUCCESS; |
| } |
| |
| static const struct vk_instance_extension_table panvk_instance_extensions = { |
| .KHR_get_physical_device_properties2 = true, |
| .EXT_debug_report = true, |
| .EXT_debug_utils = true, |
| |
| #ifdef PANVK_USE_WSI_PLATFORM |
| .KHR_surface = true, |
| #endif |
| #ifdef VK_USE_PLATFORM_WAYLAND_KHR |
| .KHR_wayland_surface = true, |
| #endif |
| }; |
| |
| static void |
| panvk_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_storage_buffer_storage_class = true, |
| .KHR_descriptor_update_template = 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, |
| }; |
| } |
| |
| VkResult panvk_physical_device_try_create(struct vk_instance *vk_instance, |
| struct _drmDevice *drm_device, |
| struct vk_physical_device **out); |
| |
| static void |
| panvk_physical_device_finish(struct panvk_physical_device *device) |
| { |
| panvk_wsi_finish(device); |
| |
| panvk_arch_dispatch(device->pdev.arch, meta_cleanup, device); |
| panfrost_close_device(&device->pdev); |
| if (device->master_fd != -1) |
| close(device->master_fd); |
| |
| vk_physical_device_finish(&device->vk); |
| } |
| |
| static void |
| panvk_destroy_physical_device(struct vk_physical_device *device) |
| { |
| panvk_physical_device_finish((struct panvk_physical_device *)device); |
| vk_free(&device->instance->alloc, device); |
| } |
| |
| VkResult |
| panvk_CreateInstance(const VkInstanceCreateInfo *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, |
| VkInstance *pInstance) |
| { |
| struct panvk_instance *instance; |
| VkResult result; |
| |
| assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO); |
| |
| pAllocator = pAllocator ? : vk_default_allocator(); |
| instance = vk_zalloc(pAllocator, sizeof(*instance), 8, |
| VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); |
| if (!instance) |
| return vk_error(NULL, VK_ERROR_OUT_OF_HOST_MEMORY); |
| |
| struct vk_instance_dispatch_table dispatch_table; |
| |
| vk_instance_dispatch_table_from_entrypoints(&dispatch_table, |
| &panvk_instance_entrypoints, |
| true); |
| vk_instance_dispatch_table_from_entrypoints(&dispatch_table, |
| &wsi_instance_entrypoints, |
| false); |
| result = vk_instance_init(&instance->vk, |
| &panvk_instance_extensions, |
| &dispatch_table, |
| pCreateInfo, |
| pAllocator); |
| if (result != VK_SUCCESS) { |
| vk_free(pAllocator, instance); |
| return vk_error(NULL, result); |
| } |
| |
| instance->vk.physical_devices.try_create_for_drm = |
| panvk_physical_device_try_create; |
| instance->vk.physical_devices.destroy = panvk_destroy_physical_device; |
| |
| instance->debug_flags = parse_debug_string(getenv("PANVK_DEBUG"), |
| panvk_debug_options); |
| |
| if (instance->debug_flags & PANVK_DEBUG_STARTUP) |
| panvk_logi("Created an instance"); |
| |
| VG(VALGRIND_CREATE_MEMPOOL(instance, 0, false)); |
| |
| *pInstance = panvk_instance_to_handle(instance); |
| |
| return VK_SUCCESS; |
| } |
| |
| void |
| panvk_DestroyInstance(VkInstance _instance, |
| const VkAllocationCallbacks *pAllocator) |
| { |
| VK_FROM_HANDLE(panvk_instance, instance, _instance); |
| |
| if (!instance) |
| return; |
| |
| vk_instance_finish(&instance->vk); |
| vk_free(&instance->vk.alloc, instance); |
| } |
| |
| static 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) |
| panvk_logi("Found compatible device '%s'.", path); |
| |
| struct vk_device_extension_table supported_extensions; |
| panvk_get_device_extensions(device, &supported_extensions); |
| |
| 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, |
| &dispatch_table); |
| |
| if (result != VK_SUCCESS) { |
| vk_error(instance, result); |
| goto fail; |
| } |
| |
| device->instance = instance; |
| assert(strlen(path) < ARRAY_SIZE(device->path)); |
| strncpy(device->path, path, ARRAY_SIZE(device->path)); |
| |
| 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; |
| if (instance->debug_flags & PANVK_DEBUG_TRACE) |
| device->pdev.debug |= PAN_DBG_TRACE; |
| |
| device->pdev.debug |= PAN_DBG_NO_CACHE; |
| panfrost_open_device(NULL, fd, &device->pdev); |
| fd = -1; |
| |
| if (device->pdev.arch <= 5) { |
| result = vk_errorf(instance, VK_ERROR_INCOMPATIBLE_DRIVER, |
| "%s not supported", |
| device->pdev.model->name); |
| goto fail; |
| } |
| |
| panvk_arch_dispatch(device->pdev.arch, meta_init, device); |
| |
| memset(device->name, 0, sizeof(device->name)); |
| sprintf(device->name, "%s", device->pdev.model->name); |
| |
| if (panvk_device_get_cache_uuid(device->pdev.gpu_id, device->cache_uuid)) { |
| result = vk_errorf(instance, VK_ERROR_INITIALIZATION_FAILED, |
| "cannot generate UUID"); |
| goto fail_close_device; |
| } |
| |
| vk_warn_non_conformant_implementation("panvk"); |
| |
| panvk_get_driver_uuid(&device->device_uuid); |
| panvk_get_device_uuid(&device->device_uuid); |
| |
| device->drm_syncobj_type = vk_drm_syncobj_get_type(device->pdev.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; |
| |
| 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_close_device; |
| } |
| |
| return VK_SUCCESS; |
| |
| fail_close_device: |
| panfrost_close_device(&device->pdev); |
| fail: |
| if (fd != -1) |
| close(fd); |
| if (master_fd != -1) |
| close(master_fd); |
| return result; |
| } |
| |
| VkResult |
| panvk_physical_device_try_create(struct vk_instance *vk_instance, |
| struct _drmDevice *drm_device, |
| struct vk_physical_device **out) |
| { |
| struct panvk_instance *instance = |
| container_of(vk_instance, struct panvk_instance, vk); |
| |
| if (!(drm_device->available_nodes & (1 << DRM_NODE_RENDER)) || |
| drm_device->bustype != DRM_BUS_PLATFORM) |
| return VK_ERROR_INCOMPATIBLE_DRIVER; |
| |
| struct panvk_physical_device *device = |
| vk_zalloc(&instance->vk.alloc, sizeof(*device), 8, |
| VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); |
| if (!device) |
| return vk_error(instance, VK_ERROR_OUT_OF_HOST_MEMORY); |
| |
| VkResult result = panvk_physical_device_init(device, instance, drm_device); |
| if (result != VK_SUCCESS) { |
| vk_free(&instance->vk.alloc, device); |
| return result; |
| } |
| |
| *out = &device->vk; |
| return VK_SUCCESS; |
| } |
| |
| void |
| panvk_GetPhysicalDeviceFeatures2(VkPhysicalDevice physicalDevice, |
| VkPhysicalDeviceFeatures2 *pFeatures) |
| { |
| pFeatures->features = (VkPhysicalDeviceFeatures) { |
| .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, |
| }; |
| |
| const VkPhysicalDeviceVulkan11Features core_1_1 = { |
| .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES, |
| .storageBuffer16BitAccess = false, |
| .uniformAndStorageBuffer16BitAccess = false, |
| .storagePushConstant16 = false, |
| .storageInputOutput16 = false, |
| .multiview = false, |
| .multiviewGeometryShader = false, |
| .multiviewTessellationShader = false, |
| .variablePointersStorageBuffer = true, |
| .variablePointers = true, |
| .protectedMemory = false, |
| .samplerYcbcrConversion = false, |
| .shaderDrawParameters = false, |
| }; |
| |
| const VkPhysicalDeviceVulkan12Features core_1_2 = { |
| .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, |
| .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 = false, |
| .bufferDeviceAddressCaptureReplay = false, |
| .bufferDeviceAddressMultiDevice = false, |
| .vulkanMemoryModel = false, |
| .vulkanMemoryModelDeviceScope = false, |
| .vulkanMemoryModelAvailabilityVisibilityChains = false, |
| .shaderOutputViewportIndex = false, |
| .shaderOutputLayer = false, |
| .subgroupBroadcastDynamicId = false, |
| }; |
| |
| const VkPhysicalDeviceVulkan13Features core_1_3 = { |
| .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES, |
| .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_foreach_struct(ext, pFeatures->pNext) |
| { |
| if (vk_get_physical_device_core_1_1_feature_ext(ext, &core_1_1)) |
| continue; |
| if (vk_get_physical_device_core_1_2_feature_ext(ext, &core_1_2)) |
| continue; |
| if (vk_get_physical_device_core_1_3_feature_ext(ext, &core_1_3)) |
| continue; |
| switch (ext->sType) { |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CONDITIONAL_RENDERING_FEATURES_EXT: { |
| VkPhysicalDeviceConditionalRenderingFeaturesEXT *features = |
| (VkPhysicalDeviceConditionalRenderingFeaturesEXT *) ext; |
| features->conditionalRendering = false; |
| features->inheritedConditionalRendering = false; |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT: { |
| VkPhysicalDeviceTransformFeedbackFeaturesEXT *features = |
| (VkPhysicalDeviceTransformFeedbackFeaturesEXT *) ext; |
| features->transformFeedback = false; |
| features->geometryStreams = false; |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES_EXT: { |
| VkPhysicalDeviceIndexTypeUint8FeaturesEXT *features = |
| (VkPhysicalDeviceIndexTypeUint8FeaturesEXT *)ext; |
| features->indexTypeUint8 = true; |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_EXT: { |
| VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT *features = |
| (VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT *)ext; |
| features->vertexAttributeInstanceRateDivisor = true; |
| features->vertexAttributeInstanceRateZeroDivisor = true; |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_ENABLE_FEATURES_EXT: { |
| VkPhysicalDeviceDepthClipEnableFeaturesEXT *features = |
| (VkPhysicalDeviceDepthClipEnableFeaturesEXT *)ext; |
| features->depthClipEnable = true; |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_4444_FORMATS_FEATURES_EXT: { |
| VkPhysicalDevice4444FormatsFeaturesEXT *features = (void *)ext; |
| features->formatA4R4G4B4 = true; |
| features->formatA4B4G4R4 = true; |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT: { |
| VkPhysicalDeviceCustomBorderColorFeaturesEXT *features = (void *) ext; |
| features->customBorderColors = true; |
| features->customBorderColorWithoutFormat = true; |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| } |
| |
| void |
| panvk_GetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice, |
| VkPhysicalDeviceProperties2 *pProperties) |
| { |
| VK_FROM_HANDLE(panvk_physical_device, pdevice, physicalDevice); |
| |
| VkSampleCountFlags sample_counts = |
| VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT; |
| |
| /* make sure that the entire descriptor set is addressable with a signed |
| * 32-bit int. So the sum of all limits scaled by descriptor size has to |
| * be at most 2 GiB. the combined image & samples object count as one of |
| * both. This limit is for the pipeline layout, not for the set layout, but |
| * there is no set limit, so we just set a pipeline limit. I don't think |
| * any app is going to hit this soon. */ |
| size_t max_descriptor_set_size = |
| ((1ull << 31) - 16 * MAX_DYNAMIC_BUFFERS) / |
| (32 /* uniform buffer, 32 due to potential space wasted on alignment */ + |
| 32 /* storage buffer, 32 due to potential space wasted on alignment */ + |
| 32 /* sampler, largest when combined with image */ + |
| 64 /* sampled image */ + 64 /* storage image */); |
| |
| const VkPhysicalDeviceLimits limits = { |
| .maxImageDimension1D = (1 << 14), |
| .maxImageDimension2D = (1 << 14), |
| .maxImageDimension3D = (1 << 11), |
| .maxImageDimensionCube = (1 << 14), |
| .maxImageArrayLayers = (1 << 11), |
| .maxTexelBufferElements = 128 * 1024 * 1024, |
| .maxUniformBufferRange = UINT32_MAX, |
| .maxStorageBufferRange = UINT32_MAX, |
| .maxPushConstantsSize = MAX_PUSH_CONSTANTS_SIZE, |
| .maxMemoryAllocationCount = UINT32_MAX, |
| .maxSamplerAllocationCount = 64 * 1024, |
| .bufferImageGranularity = 64, /* A cache line */ |
| .sparseAddressSpaceSize = 0xffffffffu, /* buffer max size */ |
| .maxBoundDescriptorSets = MAX_SETS, |
| .maxPerStageDescriptorSamplers = max_descriptor_set_size, |
| .maxPerStageDescriptorUniformBuffers = max_descriptor_set_size, |
| .maxPerStageDescriptorStorageBuffers = max_descriptor_set_size, |
| .maxPerStageDescriptorSampledImages = max_descriptor_set_size, |
| .maxPerStageDescriptorStorageImages = max_descriptor_set_size, |
| .maxPerStageDescriptorInputAttachments = max_descriptor_set_size, |
| .maxPerStageResources = max_descriptor_set_size, |
| .maxDescriptorSetSamplers = max_descriptor_set_size, |
| .maxDescriptorSetUniformBuffers = max_descriptor_set_size, |
| .maxDescriptorSetUniformBuffersDynamic = MAX_DYNAMIC_UNIFORM_BUFFERS, |
| .maxDescriptorSetStorageBuffers = max_descriptor_set_size, |
| .maxDescriptorSetStorageBuffersDynamic = MAX_DYNAMIC_STORAGE_BUFFERS, |
| .maxDescriptorSetSampledImages = max_descriptor_set_size, |
| .maxDescriptorSetStorageImages = max_descriptor_set_size, |
| .maxDescriptorSetInputAttachments = max_descriptor_set_size, |
| .maxVertexInputAttributes = 32, |
| .maxVertexInputBindings = 32, |
| .maxVertexInputAttributeOffset = 2047, |
| .maxVertexInputBindingStride = 2048, |
| .maxVertexOutputComponents = 128, |
| .maxTessellationGenerationLevel = 64, |
| .maxTessellationPatchSize = 32, |
| .maxTessellationControlPerVertexInputComponents = 128, |
| .maxTessellationControlPerVertexOutputComponents = 128, |
| .maxTessellationControlPerPatchOutputComponents = 120, |
| .maxTessellationControlTotalOutputComponents = 4096, |
| .maxTessellationEvaluationInputComponents = 128, |
| .maxTessellationEvaluationOutputComponents = 128, |
| .maxGeometryShaderInvocations = 127, |
| .maxGeometryInputComponents = 64, |
| .maxGeometryOutputComponents = 128, |
| .maxGeometryOutputVertices = 256, |
| .maxGeometryTotalOutputComponents = 1024, |
| .maxFragmentInputComponents = 128, |
| .maxFragmentOutputAttachments = 8, |
| .maxFragmentDualSrcAttachments = 1, |
| .maxFragmentCombinedOutputResources = MAX_RTS + max_descriptor_set_size * 2, |
| .maxComputeSharedMemorySize = 32768, |
| .maxComputeWorkGroupCount = { 65535, 65535, 65535 }, |
| .maxComputeWorkGroupInvocations = 2048, |
| .maxComputeWorkGroupSize = { 2048, 2048, 2048 }, |
| .subPixelPrecisionBits = 4 /* FIXME */, |
| .subTexelPrecisionBits = 4 /* FIXME */, |
| .mipmapPrecisionBits = 4 /* FIXME */, |
| .maxDrawIndexedIndexValue = UINT32_MAX, |
| .maxDrawIndirectCount = UINT32_MAX, |
| .maxSamplerLodBias = 16, |
| .maxSamplerAnisotropy = 16, |
| .maxViewports = MAX_VIEWPORTS, |
| .maxViewportDimensions = { (1 << 14), (1 << 14) }, |
| .viewportBoundsRange = { INT16_MIN, INT16_MAX }, |
| .viewportSubPixelBits = 8, |
| .minMemoryMapAlignment = 4096, /* A page */ |
| .minTexelBufferOffsetAlignment = 64, |
| .minUniformBufferOffsetAlignment = 16, |
| .minStorageBufferOffsetAlignment = 4, |
| .minTexelOffset = -32, |
| .maxTexelOffset = 31, |
| .minTexelGatherOffset = -32, |
| .maxTexelGatherOffset = 31, |
| .minInterpolationOffset = -2, |
| .maxInterpolationOffset = 2, |
| .subPixelInterpolationOffsetBits = 8, |
| .maxFramebufferWidth = (1 << 14), |
| .maxFramebufferHeight = (1 << 14), |
| .maxFramebufferLayers = (1 << 10), |
| .framebufferColorSampleCounts = sample_counts, |
| .framebufferDepthSampleCounts = sample_counts, |
| .framebufferStencilSampleCounts = sample_counts, |
| .framebufferNoAttachmentsSampleCounts = sample_counts, |
| .maxColorAttachments = MAX_RTS, |
| .sampledImageColorSampleCounts = sample_counts, |
| .sampledImageIntegerSampleCounts = VK_SAMPLE_COUNT_1_BIT, |
| .sampledImageDepthSampleCounts = sample_counts, |
| .sampledImageStencilSampleCounts = sample_counts, |
| .storageImageSampleCounts = VK_SAMPLE_COUNT_1_BIT, |
| .maxSampleMaskWords = 1, |
| .timestampComputeAndGraphics = true, |
| .timestampPeriod = 1, |
| .maxClipDistances = 8, |
| .maxCullDistances = 8, |
| .maxCombinedClipAndCullDistances = 8, |
| .discreteQueuePriorities = 1, |
| .pointSizeRange = { 0.125, 4095.9375 }, |
| .lineWidthRange = { 0.0, 7.9921875 }, |
| .pointSizeGranularity = (1.0 / 16.0), |
| .lineWidthGranularity = (1.0 / 128.0), |
| .strictLines = false, /* FINISHME */ |
| .standardSampleLocations = true, |
| .optimalBufferCopyOffsetAlignment = 128, |
| .optimalBufferCopyRowPitchAlignment = 128, |
| .nonCoherentAtomSize = 64, |
| }; |
| |
| pProperties->properties = (VkPhysicalDeviceProperties) { |
| .apiVersion = PANVK_API_VERSION, |
| .driverVersion = vk_get_driver_version(), |
| .vendorID = 0, /* TODO */ |
| .deviceID = 0, |
| .deviceType = VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU, |
| .limits = limits, |
| .sparseProperties = { 0 }, |
| }; |
| |
| strcpy(pProperties->properties.deviceName, pdevice->name); |
| memcpy(pProperties->properties.pipelineCacheUUID, pdevice->cache_uuid, VK_UUID_SIZE); |
| |
| VkPhysicalDeviceVulkan11Properties core_1_1 = { |
| .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES, |
| .deviceLUIDValid = 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 = 0xFFFFFFFFull, |
| }; |
| memcpy(core_1_1.driverUUID, pdevice->driver_uuid, VK_UUID_SIZE); |
| memcpy(core_1_1.deviceUUID, pdevice->device_uuid, VK_UUID_SIZE); |
| |
| const VkPhysicalDeviceVulkan12Properties core_1_2 = { |
| .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES, |
| }; |
| |
| const VkPhysicalDeviceVulkan13Properties core_1_3 = { |
| .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_PROPERTIES, |
| }; |
| |
| vk_foreach_struct(ext, pProperties->pNext) |
| { |
| if (vk_get_physical_device_core_1_1_property_ext(ext, &core_1_1)) |
| continue; |
| if (vk_get_physical_device_core_1_2_property_ext(ext, &core_1_2)) |
| continue; |
| if (vk_get_physical_device_core_1_3_property_ext(ext, &core_1_3)) |
| continue; |
| |
| switch (ext->sType) { |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR: { |
| VkPhysicalDevicePushDescriptorPropertiesKHR *properties = (VkPhysicalDevicePushDescriptorPropertiesKHR *)ext; |
| properties->maxPushDescriptors = MAX_PUSH_DESCRIPTORS; |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT: { |
| VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT *properties = |
| (VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT *)ext; |
| /* We have to restrict this a bit for multiview */ |
| properties->maxVertexAttribDivisor = UINT32_MAX / (16 * 2048); |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| } |
| |
| static const VkQueueFamilyProperties panvk_queue_family_properties = { |
| .queueFlags = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT, |
| .queueCount = 1, |
| .timestampValidBits = 64, |
| .minImageTransferGranularity = { 1, 1, 1 }, |
| }; |
| |
| void |
| 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 |
| panvk_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; |
| } |
| |
| void |
| panvk_GetPhysicalDeviceMemoryProperties2(VkPhysicalDevice physicalDevice, |
| VkPhysicalDeviceMemoryProperties2 *pMemoryProperties) |
| { |
| pMemoryProperties->memoryProperties = (VkPhysicalDeviceMemoryProperties) { |
| .memoryHeapCount = 1, |
| .memoryHeaps[0].size = panvk_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, |
| }; |
| } |
| |
| static VkResult |
| panvk_queue_init(struct panvk_device *device, |
| struct panvk_queue *queue, |
| int idx, |
| const VkDeviceQueueCreateInfo *create_info) |
| { |
| const struct panfrost_device *pdev = &device->physical_device->pdev; |
| |
| VkResult result = vk_queue_init(&queue->vk, &device->vk, create_info, idx); |
| if (result != VK_SUCCESS) |
| return result; |
| queue->device = device; |
| |
| struct drm_syncobj_create create = { |
| .flags = DRM_SYNCOBJ_CREATE_SIGNALED, |
| }; |
| |
| int ret = drmIoctl(pdev->fd, DRM_IOCTL_SYNCOBJ_CREATE, &create); |
| if (ret) { |
| vk_queue_finish(&queue->vk); |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| |
| switch (pdev->arch) { |
| case 6: queue->vk.driver_submit = panvk_v6_queue_submit; break; |
| case 7: queue->vk.driver_submit = panvk_v7_queue_submit; break; |
| default: unreachable("Invalid arch"); |
| } |
| |
| queue->sync = create.handle; |
| return VK_SUCCESS; |
| } |
| |
| static void |
| panvk_queue_finish(struct panvk_queue *queue) |
| { |
| vk_queue_finish(&queue->vk); |
| } |
| |
| VkResult |
| panvk_CreateDevice(VkPhysicalDevice physicalDevice, |
| const VkDeviceCreateInfo *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, |
| VkDevice *pDevice) |
| { |
| VK_FROM_HANDLE(panvk_physical_device, physical_device, physicalDevice); |
| VkResult result; |
| struct panvk_device *device; |
| |
| device = vk_zalloc2(&physical_device->instance->vk.alloc, pAllocator, |
| sizeof(*device), 8, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE); |
| if (!device) |
| return vk_error(physical_device, VK_ERROR_OUT_OF_HOST_MEMORY); |
| |
| const struct vk_device_entrypoint_table *dev_entrypoints; |
| struct vk_device_dispatch_table dispatch_table; |
| |
| switch (physical_device->pdev.arch) { |
| case 6: |
| dev_entrypoints = &panvk_v6_device_entrypoints; |
| break; |
| case 7: |
| dev_entrypoints = &panvk_v7_device_entrypoints; |
| break; |
| default: |
| unreachable("Unsupported architecture"); |
| } |
| |
| /* For secondary command buffer support, overwrite any command entrypoints |
| * in the main device-level dispatch table with |
| * vk_cmd_enqueue_unless_primary_Cmd*. |
| */ |
| vk_device_dispatch_table_from_entrypoints(&dispatch_table, |
| &vk_cmd_enqueue_unless_primary_device_entrypoints, |
| true); |
| |
| vk_device_dispatch_table_from_entrypoints(&dispatch_table, |
| dev_entrypoints, |
| false); |
| vk_device_dispatch_table_from_entrypoints(&dispatch_table, |
| &panvk_device_entrypoints, |
| false); |
| vk_device_dispatch_table_from_entrypoints(&dispatch_table, |
| &wsi_device_entrypoints, |
| false); |
| |
| /* Populate our primary cmd_dispatch table. */ |
| vk_device_dispatch_table_from_entrypoints(&device->cmd_dispatch, |
| dev_entrypoints, |
| true); |
| vk_device_dispatch_table_from_entrypoints(&device->cmd_dispatch, |
| &panvk_device_entrypoints, |
| false); |
| vk_device_dispatch_table_from_entrypoints(&device->cmd_dispatch, |
| &vk_common_device_entrypoints, |
| false); |
| |
| result = vk_device_init(&device->vk, &physical_device->vk, &dispatch_table, |
| pCreateInfo, pAllocator); |
| if (result != VK_SUCCESS) { |
| vk_free(&device->vk.alloc, device); |
| return result; |
| } |
| |
| /* Must be done after vk_device_init() because this function memset(0) the |
| * whole struct. |
| */ |
| device->vk.command_dispatch_table = &device->cmd_dispatch; |
| |
| device->instance = physical_device->instance; |
| device->physical_device = physical_device; |
| |
| const struct panfrost_device *pdev = &physical_device->pdev; |
| vk_device_set_drm_fd(&device->vk, pdev->fd); |
| |
| for (unsigned i = 0; i < pCreateInfo->queueCreateInfoCount; i++) { |
| const VkDeviceQueueCreateInfo *queue_create = |
| &pCreateInfo->pQueueCreateInfos[i]; |
| uint32_t qfi = queue_create->queueFamilyIndex; |
| device->queues[qfi] = |
| vk_alloc(&device->vk.alloc, |
| queue_create->queueCount * sizeof(struct panvk_queue), |
| 8, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE); |
| if (!device->queues[qfi]) { |
| result = VK_ERROR_OUT_OF_HOST_MEMORY; |
| goto fail; |
| } |
| |
| memset(device->queues[qfi], 0, |
| queue_create->queueCount * sizeof(struct panvk_queue)); |
| |
| device->queue_count[qfi] = queue_create->queueCount; |
| |
| for (unsigned q = 0; q < queue_create->queueCount; q++) { |
| result = panvk_queue_init(device, &device->queues[qfi][q], q, |
| queue_create); |
| if (result != VK_SUCCESS) |
| goto fail; |
| } |
| } |
| |
| *pDevice = panvk_device_to_handle(device); |
| return VK_SUCCESS; |
| |
| fail: |
| for (unsigned i = 0; i < PANVK_MAX_QUEUE_FAMILIES; i++) { |
| for (unsigned q = 0; q < device->queue_count[i]; q++) |
| panvk_queue_finish(&device->queues[i][q]); |
| if (device->queue_count[i]) |
| vk_object_free(&device->vk, NULL, device->queues[i]); |
| } |
| |
| vk_free(&device->vk.alloc, device); |
| return result; |
| } |
| |
| void |
| panvk_DestroyDevice(VkDevice _device, const VkAllocationCallbacks *pAllocator) |
| { |
| VK_FROM_HANDLE(panvk_device, device, _device); |
| |
| if (!device) |
| return; |
| |
| for (unsigned i = 0; i < PANVK_MAX_QUEUE_FAMILIES; i++) { |
| for (unsigned q = 0; q < device->queue_count[i]; q++) |
| panvk_queue_finish(&device->queues[i][q]); |
| if (device->queue_count[i]) |
| vk_object_free(&device->vk, NULL, device->queues[i]); |
| } |
| |
| vk_free(&device->vk.alloc, device); |
| } |
| |
| VkResult |
| panvk_EnumerateInstanceLayerProperties(uint32_t *pPropertyCount, |
| VkLayerProperties *pProperties) |
| { |
| *pPropertyCount = 0; |
| return VK_SUCCESS; |
| } |
| |
| VkResult |
| panvk_QueueWaitIdle(VkQueue _queue) |
| { |
| VK_FROM_HANDLE(panvk_queue, queue, _queue); |
| |
| if (panvk_device_is_lost(queue->device)) |
| return VK_ERROR_DEVICE_LOST; |
| |
| const struct panfrost_device *pdev = &queue->device->physical_device->pdev; |
| struct drm_syncobj_wait wait = { |
| .handles = (uint64_t) (uintptr_t)(&queue->sync), |
| .count_handles = 1, |
| .timeout_nsec = INT64_MAX, |
| .flags = DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL, |
| }; |
| int ret; |
| |
| ret = drmIoctl(pdev->fd, DRM_IOCTL_SYNCOBJ_WAIT, &wait); |
| assert(!ret); |
| |
| return VK_SUCCESS; |
| } |
| |
| VkResult |
| panvk_EnumerateInstanceExtensionProperties(const char *pLayerName, |
| uint32_t *pPropertyCount, |
| VkExtensionProperties *pProperties) |
| { |
| if (pLayerName) |
| return vk_error(NULL, VK_ERROR_LAYER_NOT_PRESENT); |
| |
| return vk_enumerate_instance_extension_properties(&panvk_instance_extensions, |
| pPropertyCount, pProperties); |
| } |
| |
| PFN_vkVoidFunction |
| panvk_GetInstanceProcAddr(VkInstance _instance, const char *pName) |
| { |
| VK_FROM_HANDLE(panvk_instance, instance, _instance); |
| return vk_instance_get_proc_addr(&instance->vk, |
| &panvk_instance_entrypoints, |
| pName); |
| } |
| |
| /* The loader wants us to expose a second GetInstanceProcAddr function |
| * to work around certain LD_PRELOAD issues seen in apps. |
| */ |
| PUBLIC |
| VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL |
| vk_icdGetInstanceProcAddr(VkInstance instance, const char *pName); |
| |
| PUBLIC |
| VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL |
| vk_icdGetInstanceProcAddr(VkInstance instance, const char *pName) |
| { |
| return panvk_GetInstanceProcAddr(instance, pName); |
| } |
| |
| /* With version 4+ of the loader interface the ICD should expose |
| * vk_icdGetPhysicalDeviceProcAddr() |
| */ |
| PUBLIC |
| VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL |
| vk_icdGetPhysicalDeviceProcAddr(VkInstance _instance, |
| const char* pName); |
| |
| PFN_vkVoidFunction |
| vk_icdGetPhysicalDeviceProcAddr(VkInstance _instance, |
| const char* pName) |
| { |
| VK_FROM_HANDLE(panvk_instance, instance, _instance); |
| |
| return vk_instance_get_physical_device_proc_addr(&instance->vk, pName); |
| } |
| |
| VkResult |
| panvk_AllocateMemory(VkDevice _device, |
| const VkMemoryAllocateInfo *pAllocateInfo, |
| const VkAllocationCallbacks *pAllocator, |
| VkDeviceMemory *pMem) |
| { |
| VK_FROM_HANDLE(panvk_device, device, _device); |
| struct panvk_device_memory *mem; |
| |
| assert(pAllocateInfo->sType == VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO); |
| |
| if (pAllocateInfo->allocationSize == 0) { |
| /* Apparently, this is allowed */ |
| *pMem = VK_NULL_HANDLE; |
| return VK_SUCCESS; |
| } |
| |
| mem = vk_object_alloc(&device->vk, pAllocator, sizeof(*mem), |
| VK_OBJECT_TYPE_DEVICE_MEMORY); |
| if (mem == NULL) |
| return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); |
| |
| const VkImportMemoryFdInfoKHR *fd_info = |
| vk_find_struct_const(pAllocateInfo->pNext, |
| IMPORT_MEMORY_FD_INFO_KHR); |
| |
| if (fd_info && !fd_info->handleType) |
| fd_info = NULL; |
| |
| if (fd_info) { |
| assert(fd_info->handleType == |
| VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT || |
| fd_info->handleType == |
| VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT); |
| |
| /* |
| * TODO Importing the same fd twice gives us the same handle without |
| * reference counting. We need to maintain a per-instance handle-to-bo |
| * table and add reference count to panvk_bo. |
| */ |
| mem->bo = panfrost_bo_import(&device->physical_device->pdev, fd_info->fd); |
| /* take ownership and close the fd */ |
| close(fd_info->fd); |
| } else { |
| mem->bo = panfrost_bo_create(&device->physical_device->pdev, |
| pAllocateInfo->allocationSize, 0, |
| "User-requested memory"); |
| } |
| |
| assert(mem->bo); |
| |
| *pMem = panvk_device_memory_to_handle(mem); |
| |
| return VK_SUCCESS; |
| } |
| |
| void |
| panvk_FreeMemory(VkDevice _device, |
| VkDeviceMemory _mem, |
| const VkAllocationCallbacks *pAllocator) |
| { |
| VK_FROM_HANDLE(panvk_device, device, _device); |
| VK_FROM_HANDLE(panvk_device_memory, mem, _mem); |
| |
| if (mem == NULL) |
| return; |
| |
| panfrost_bo_unreference(mem->bo); |
| vk_object_free(&device->vk, pAllocator, mem); |
| } |
| |
| VkResult |
| panvk_MapMemory(VkDevice _device, |
| VkDeviceMemory _memory, |
| VkDeviceSize offset, |
| VkDeviceSize size, |
| VkMemoryMapFlags flags, |
| void **ppData) |
| { |
| VK_FROM_HANDLE(panvk_device, device, _device); |
| VK_FROM_HANDLE(panvk_device_memory, mem, _memory); |
| |
| if (mem == NULL) { |
| *ppData = NULL; |
| return VK_SUCCESS; |
| } |
| |
| if (!mem->bo->ptr.cpu) |
| panfrost_bo_mmap(mem->bo); |
| |
| *ppData = mem->bo->ptr.cpu; |
| |
| if (*ppData) { |
| *ppData += offset; |
| return VK_SUCCESS; |
| } |
| |
| return vk_error(device, VK_ERROR_MEMORY_MAP_FAILED); |
| } |
| |
| void |
| panvk_UnmapMemory(VkDevice _device, VkDeviceMemory _memory) |
| { |
| } |
| |
| VkResult |
| panvk_FlushMappedMemoryRanges(VkDevice _device, |
| uint32_t memoryRangeCount, |
| const VkMappedMemoryRange *pMemoryRanges) |
| { |
| return VK_SUCCESS; |
| } |
| |
| VkResult |
| panvk_InvalidateMappedMemoryRanges(VkDevice _device, |
| uint32_t memoryRangeCount, |
| const VkMappedMemoryRange *pMemoryRanges) |
| { |
| return VK_SUCCESS; |
| } |
| |
| void |
| panvk_GetBufferMemoryRequirements2(VkDevice device, |
| const VkBufferMemoryRequirementsInfo2 *pInfo, |
| VkMemoryRequirements2 *pMemoryRequirements) |
| { |
| VK_FROM_HANDLE(panvk_buffer, buffer, pInfo->buffer); |
| |
| const uint64_t align = 64; |
| const uint64_t size = align64(buffer->vk.size, align); |
| |
| pMemoryRequirements->memoryRequirements.memoryTypeBits = 1; |
| pMemoryRequirements->memoryRequirements.alignment = align; |
| pMemoryRequirements->memoryRequirements.size = size; |
| } |
| |
| void |
| panvk_GetImageMemoryRequirements2(VkDevice device, |
| const VkImageMemoryRequirementsInfo2 *pInfo, |
| VkMemoryRequirements2 *pMemoryRequirements) |
| { |
| VK_FROM_HANDLE(panvk_image, image, pInfo->image); |
| |
| const uint64_t align = 4096; |
| const uint64_t size = panvk_image_get_total_size(image); |
| |
| pMemoryRequirements->memoryRequirements.memoryTypeBits = 1; |
| pMemoryRequirements->memoryRequirements.alignment = align; |
| pMemoryRequirements->memoryRequirements.size = size; |
| } |
| |
| void |
| panvk_GetImageSparseMemoryRequirements2(VkDevice device, |
| const VkImageSparseMemoryRequirementsInfo2 *pInfo, |
| uint32_t *pSparseMemoryRequirementCount, |
| VkSparseImageMemoryRequirements2 *pSparseMemoryRequirements) |
| { |
| panvk_stub(); |
| } |
| |
| void |
| panvk_GetDeviceMemoryCommitment(VkDevice device, |
| VkDeviceMemory memory, |
| VkDeviceSize *pCommittedMemoryInBytes) |
| { |
| *pCommittedMemoryInBytes = 0; |
| } |
| |
| VkResult |
| panvk_BindBufferMemory2(VkDevice device, |
| uint32_t bindInfoCount, |
| const VkBindBufferMemoryInfo *pBindInfos) |
| { |
| for (uint32_t i = 0; i < bindInfoCount; ++i) { |
| VK_FROM_HANDLE(panvk_device_memory, mem, pBindInfos[i].memory); |
| VK_FROM_HANDLE(panvk_buffer, buffer, pBindInfos[i].buffer); |
| |
| if (mem) { |
| buffer->bo = mem->bo; |
| buffer->bo_offset = pBindInfos[i].memoryOffset; |
| } else { |
| buffer->bo = NULL; |
| } |
| } |
| return VK_SUCCESS; |
| } |
| |
| VkResult |
| panvk_BindImageMemory2(VkDevice device, |
| uint32_t bindInfoCount, |
| const VkBindImageMemoryInfo *pBindInfos) |
| { |
| for (uint32_t i = 0; i < bindInfoCount; ++i) { |
| VK_FROM_HANDLE(panvk_image, image, pBindInfos[i].image); |
| VK_FROM_HANDLE(panvk_device_memory, mem, pBindInfos[i].memory); |
| |
| if (mem) { |
| image->pimage.data.bo = mem->bo; |
| image->pimage.data.offset = pBindInfos[i].memoryOffset; |
| /* Reset the AFBC headers */ |
| if (drm_is_afbc(image->pimage.layout.modifier)) { |
| void *base = image->pimage.data.bo->ptr.cpu + image->pimage.data.offset; |
| |
| for (unsigned layer = 0; layer < image->pimage.layout.array_size; layer++) { |
| for (unsigned level = 0; level < image->pimage.layout.nr_slices; level++) { |
| void *header = base + |
| (layer * image->pimage.layout.array_stride) + |
| image->pimage.layout.slices[level].offset; |
| memset(header, 0, image->pimage.layout.slices[level].afbc.header_size); |
| } |
| } |
| } |
| } else { |
| image->pimage.data.bo = NULL; |
| image->pimage.data.offset = pBindInfos[i].memoryOffset; |
| } |
| } |
| |
| return VK_SUCCESS; |
| } |
| |
| VkResult |
| panvk_CreateEvent(VkDevice _device, |
| const VkEventCreateInfo *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, |
| VkEvent *pEvent) |
| { |
| VK_FROM_HANDLE(panvk_device, device, _device); |
| const struct panfrost_device *pdev = &device->physical_device->pdev; |
| struct panvk_event *event = |
| vk_object_zalloc(&device->vk, pAllocator, sizeof(*event), |
| VK_OBJECT_TYPE_EVENT); |
| if (!event) |
| return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); |
| |
| struct drm_syncobj_create create = { |
| .flags = 0, |
| }; |
| |
| int ret = drmIoctl(pdev->fd, DRM_IOCTL_SYNCOBJ_CREATE, &create); |
| if (ret) |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| |
| event->syncobj = create.handle; |
| *pEvent = panvk_event_to_handle(event); |
| |
| return VK_SUCCESS; |
| } |
| |
| void |
| panvk_DestroyEvent(VkDevice _device, |
| VkEvent _event, |
| const VkAllocationCallbacks *pAllocator) |
| { |
| VK_FROM_HANDLE(panvk_device, device, _device); |
| VK_FROM_HANDLE(panvk_event, event, _event); |
| const struct panfrost_device *pdev = &device->physical_device->pdev; |
| |
| if (!event) |
| return; |
| |
| struct drm_syncobj_destroy destroy = { .handle = event->syncobj }; |
| drmIoctl(pdev->fd, DRM_IOCTL_SYNCOBJ_DESTROY, &destroy); |
| |
| vk_object_free(&device->vk, pAllocator, event); |
| } |
| |
| VkResult |
| panvk_GetEventStatus(VkDevice _device, VkEvent _event) |
| { |
| VK_FROM_HANDLE(panvk_device, device, _device); |
| VK_FROM_HANDLE(panvk_event, event, _event); |
| const struct panfrost_device *pdev = &device->physical_device->pdev; |
| bool signaled; |
| |
| struct drm_syncobj_wait wait = { |
| .handles = (uintptr_t) &event->syncobj, |
| .count_handles = 1, |
| .timeout_nsec = 0, |
| .flags = DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT, |
| }; |
| |
| int ret = drmIoctl(pdev->fd, DRM_IOCTL_SYNCOBJ_WAIT, &wait); |
| if (ret) { |
| if (errno == ETIME) |
| signaled = false; |
| else { |
| assert(0); |
| return VK_ERROR_DEVICE_LOST; /* TODO */ |
| } |
| } else |
| signaled = true; |
| |
| return signaled ? VK_EVENT_SET : VK_EVENT_RESET; |
| } |
| |
| VkResult |
| panvk_SetEvent(VkDevice _device, VkEvent _event) |
| { |
| VK_FROM_HANDLE(panvk_device, device, _device); |
| VK_FROM_HANDLE(panvk_event, event, _event); |
| const struct panfrost_device *pdev = &device->physical_device->pdev; |
| |
| struct drm_syncobj_array objs = { |
| .handles = (uint64_t) (uintptr_t) &event->syncobj, |
| .count_handles = 1 |
| }; |
| |
| /* This is going to just replace the fence for this syncobj with one that |
| * is already in signaled state. This won't be a problem because the spec |
| * mandates that the event will have been set before the vkCmdWaitEvents |
| * command executes. |
| * https://www.khronos.org/registry/vulkan/specs/1.2/html/chap6.html#commandbuffers-submission-progress |
| */ |
| if (drmIoctl(pdev->fd, DRM_IOCTL_SYNCOBJ_SIGNAL, &objs)) |
| return VK_ERROR_DEVICE_LOST; |
| |
| return VK_SUCCESS; |
| } |
| |
| VkResult |
| panvk_ResetEvent(VkDevice _device, VkEvent _event) |
| { |
| VK_FROM_HANDLE(panvk_device, device, _device); |
| VK_FROM_HANDLE(panvk_event, event, _event); |
| const struct panfrost_device *pdev = &device->physical_device->pdev; |
| |
| struct drm_syncobj_array objs = { |
| .handles = (uint64_t) (uintptr_t) &event->syncobj, |
| .count_handles = 1 |
| }; |
| |
| if (drmIoctl(pdev->fd, DRM_IOCTL_SYNCOBJ_RESET, &objs)) |
| return VK_ERROR_DEVICE_LOST; |
| |
| return VK_SUCCESS; |
| } |
| |
| VkResult |
| panvk_CreateBuffer(VkDevice _device, |
| const VkBufferCreateInfo *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, |
| VkBuffer *pBuffer) |
| { |
| VK_FROM_HANDLE(panvk_device, device, _device); |
| struct panvk_buffer *buffer; |
| |
| assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO); |
| |
| buffer = vk_buffer_create(&device->vk, pCreateInfo, |
| pAllocator, sizeof(*buffer)); |
| if (buffer == NULL) |
| return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); |
| |
| *pBuffer = panvk_buffer_to_handle(buffer); |
| |
| return VK_SUCCESS; |
| } |
| |
| void |
| panvk_DestroyBuffer(VkDevice _device, |
| VkBuffer _buffer, |
| const VkAllocationCallbacks *pAllocator) |
| { |
| VK_FROM_HANDLE(panvk_device, device, _device); |
| VK_FROM_HANDLE(panvk_buffer, buffer, _buffer); |
| |
| if (!buffer) |
| return; |
| |
| vk_buffer_destroy(&device->vk, pAllocator, &buffer->vk); |
| } |
| |
| VkResult |
| panvk_CreateFramebuffer(VkDevice _device, |
| const VkFramebufferCreateInfo *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, |
| VkFramebuffer *pFramebuffer) |
| { |
| VK_FROM_HANDLE(panvk_device, device, _device); |
| struct panvk_framebuffer *framebuffer; |
| |
| assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO); |
| |
| size_t size = sizeof(*framebuffer) + sizeof(struct panvk_attachment_info) * |
| pCreateInfo->attachmentCount; |
| framebuffer = vk_object_alloc(&device->vk, pAllocator, size, |
| VK_OBJECT_TYPE_FRAMEBUFFER); |
| if (framebuffer == NULL) |
| return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); |
| |
| framebuffer->attachment_count = pCreateInfo->attachmentCount; |
| framebuffer->width = pCreateInfo->width; |
| framebuffer->height = pCreateInfo->height; |
| framebuffer->layers = pCreateInfo->layers; |
| for (uint32_t i = 0; i < pCreateInfo->attachmentCount; i++) { |
| VkImageView _iview = pCreateInfo->pAttachments[i]; |
| struct panvk_image_view *iview = panvk_image_view_from_handle(_iview); |
| framebuffer->attachments[i].iview = iview; |
| } |
| |
| *pFramebuffer = panvk_framebuffer_to_handle(framebuffer); |
| return VK_SUCCESS; |
| } |
| |
| void |
| panvk_DestroyFramebuffer(VkDevice _device, |
| VkFramebuffer _fb, |
| const VkAllocationCallbacks *pAllocator) |
| { |
| VK_FROM_HANDLE(panvk_device, device, _device); |
| VK_FROM_HANDLE(panvk_framebuffer, fb, _fb); |
| |
| if (fb) |
| vk_object_free(&device->vk, pAllocator, fb); |
| } |
| |
| void |
| panvk_DestroySampler(VkDevice _device, |
| VkSampler _sampler, |
| const VkAllocationCallbacks *pAllocator) |
| { |
| VK_FROM_HANDLE(panvk_device, device, _device); |
| VK_FROM_HANDLE(panvk_sampler, sampler, _sampler); |
| |
| if (!sampler) |
| return; |
| |
| vk_object_free(&device->vk, pAllocator, sampler); |
| } |
| |
| /* vk_icd.h does not declare this function, so we declare it here to |
| * suppress Wmissing-prototypes. |
| */ |
| PUBLIC VKAPI_ATTR VkResult VKAPI_CALL |
| vk_icdNegotiateLoaderICDInterfaceVersion(uint32_t *pSupportedVersion); |
| |
| PUBLIC VKAPI_ATTR VkResult VKAPI_CALL |
| vk_icdNegotiateLoaderICDInterfaceVersion(uint32_t *pSupportedVersion) |
| { |
| /* For the full details on loader interface versioning, see |
| * <https://github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers/blob/master/loader/LoaderAndLayerInterface.md>. |
| * What follows is a condensed summary, to help you navigate the large and |
| * confusing official doc. |
| * |
| * - Loader interface v0 is incompatible with later versions. We don't |
| * support it. |
| * |
| * - In loader interface v1: |
| * - The first ICD entrypoint called by the loader is |
| * vk_icdGetInstanceProcAddr(). The ICD must statically expose this |
| * entrypoint. |
| * - The ICD must statically expose no other Vulkan symbol unless it |
| * is linked with -Bsymbolic. |
| * - Each dispatchable Vulkan handle created by the ICD must be |
| * a pointer to a struct whose first member is VK_LOADER_DATA. The |
| * ICD must initialize VK_LOADER_DATA.loadMagic to |
| * ICD_LOADER_MAGIC. |
| * - The loader implements vkCreate{PLATFORM}SurfaceKHR() and |
| * vkDestroySurfaceKHR(). The ICD must be capable of working with |
| * such loader-managed surfaces. |
| * |
| * - Loader interface v2 differs from v1 in: |
| * - The first ICD entrypoint called by the loader is |
| * vk_icdNegotiateLoaderICDInterfaceVersion(). The ICD must |
| * statically expose this entrypoint. |
| * |
| * - Loader interface v3 differs from v2 in: |
| * - The ICD must implement vkCreate{PLATFORM}SurfaceKHR(), |
| * vkDestroySurfaceKHR(), and other API which uses VKSurfaceKHR, |
| * because the loader no longer does so. |
| * |
| * - Loader interface v4 differs from v3 in: |
| * - The ICD must implement vk_icdGetPhysicalDeviceProcAddr(). |
| * |
| * - Loader interface v5 differs from v4 in: |
| * - The ICD must support 1.1 and must not return |
| * VK_ERROR_INCOMPATIBLE_DRIVER from vkCreateInstance() unless a |
| * Vulkan Loader with interface v4 or smaller is being used and the |
| * application provides an API version that is greater than 1.0. |
| */ |
| *pSupportedVersion = MIN2(*pSupportedVersion, 5u); |
| return VK_SUCCESS; |
| } |
| |
| VkResult |
| panvk_GetMemoryFdKHR(VkDevice _device, |
| const VkMemoryGetFdInfoKHR *pGetFdInfo, |
| int *pFd) |
| { |
| VK_FROM_HANDLE(panvk_device, device, _device); |
| VK_FROM_HANDLE(panvk_device_memory, memory, pGetFdInfo->memory); |
| |
| assert(pGetFdInfo->sType == VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR); |
| |
| /* At the moment, we support only the below handle types. */ |
| assert(pGetFdInfo->handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT || |
| pGetFdInfo->handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT); |
| |
| int prime_fd = panfrost_bo_export(memory->bo); |
| if (prime_fd < 0) |
| return vk_error(device, VK_ERROR_OUT_OF_DEVICE_MEMORY); |
| |
| *pFd = prime_fd; |
| return VK_SUCCESS; |
| } |
| |
| VkResult |
| panvk_GetMemoryFdPropertiesKHR(VkDevice _device, |
| VkExternalMemoryHandleTypeFlagBits handleType, |
| int fd, |
| VkMemoryFdPropertiesKHR *pMemoryFdProperties) |
| { |
| assert(handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT); |
| pMemoryFdProperties->memoryTypeBits = 1; |
| return VK_SUCCESS; |
| } |
| |
| void |
| 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; |
| } |
| } |
| |
| void |
| panvk_GetPhysicalDeviceExternalFenceProperties(VkPhysicalDevice physicalDevice, |
| const VkPhysicalDeviceExternalFenceInfo *pExternalFenceInfo, |
| VkExternalFenceProperties *pExternalFenceProperties) |
| { |
| pExternalFenceProperties->exportFromImportedHandleTypes = 0; |
| pExternalFenceProperties->compatibleHandleTypes = 0; |
| pExternalFenceProperties->externalFenceFeatures = 0; |
| } |
| |
| void |
| panvk_GetDeviceGroupPeerMemoryFeatures(VkDevice device, |
| uint32_t heapIndex, |
| uint32_t localDeviceIndex, |
| uint32_t remoteDeviceIndex, |
| VkPeerMemoryFeatureFlags *pPeerMemoryFeatures) |
| { |
| assert(localDeviceIndex == remoteDeviceIndex); |
| |
| *pPeerMemoryFeatures = VK_PEER_MEMORY_FEATURE_COPY_SRC_BIT | |
| VK_PEER_MEMORY_FEATURE_COPY_DST_BIT | |
| VK_PEER_MEMORY_FEATURE_GENERIC_SRC_BIT | |
| VK_PEER_MEMORY_FEATURE_GENERIC_DST_BIT; |
| } |