device/vulkan/adapters.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // SPDX-License-Identifier: BSD-3-Clause | ||
| 2 | #include "chrono_batch_render/device/vulkan/device.h" | ||
| 3 | |||
| 4 | #include <algorithm> | ||
| 5 | #include <string> | ||
| 6 | |||
| 7 | #include "chrono_batch_render/util/library.h" | ||
| 8 | #include "chrono_batch_render/util/text.h" | ||
| 9 | #include "chrono_batch_render/util/version.h" | ||
| 10 | |||
| 11 | namespace chrono::batch_render::device::vulkan { | ||
| 12 | namespace { | ||
| 13 | |||
| 14 | constexpr uint32_t kVendorNvidia = 0x10de; | ||
| 15 | constexpr uint32_t kVendorIntel = 0x8086; | ||
| 16 | |||
| 17 | 42 | Version DecodeDriverVersion(uint32_t vendor, uint32_t packed) { | |
| 18 | 42 | if (vendor == kVendorNvidia) { | |
| 19 | ✗ | return {int(packed >> 22), int(packed >> 14 & 0xff), int(packed >> 6 & 0xff)}; | |
| 20 | } | ||
| 21 | #if defined(_WIN32) | ||
| 22 | if (vendor == kVendorIntel) { | ||
| 23 | return {int(packed >> 14), int(packed & 0x3fff)}; | ||
| 24 | } | ||
| 25 | #else | ||
| 26 | 42 | static_cast<void>(kVendorIntel); | |
| 27 | #endif | ||
| 28 | 42 | return DecodeVersion(packed); | |
| 29 | } | ||
| 30 | |||
| 31 | 42 | std::string DriverName(VkDriverId id, const char* name) { | |
| 32 | 42 | switch (id) { | |
| 33 | ✗ | case VK_DRIVER_ID_NVIDIA_PROPRIETARY: | |
| 34 | ✗ | return "nvidia"; | |
| 35 | ✗ | case VK_DRIVER_ID_AMD_PROPRIETARY: | |
| 36 | ✗ | return "amd"; | |
| 37 | ✗ | case VK_DRIVER_ID_AMD_OPEN_SOURCE: | |
| 38 | ✗ | return "amdvlk"; | |
| 39 | ✗ | case VK_DRIVER_ID_MESA_RADV: | |
| 40 | ✗ | return "radv"; | |
| 41 | ✗ | case VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS: | |
| 42 | ✗ | return "intel"; | |
| 43 | ✗ | case VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA: | |
| 44 | ✗ | return "anv"; | |
| 45 | ✗ | case VK_DRIVER_ID_MESA_NVK: | |
| 46 | ✗ | return "nvk"; | |
| 47 | 42 | case VK_DRIVER_ID_MESA_LLVMPIPE: | |
| 48 | 42 | return "llvmpipe"; | |
| 49 | ✗ | case VK_DRIVER_ID_MOLTENVK: | |
| 50 | ✗ | return "moltenvk"; | |
| 51 | ✗ | case VK_DRIVER_ID_MESA_HONEYKRISP: | |
| 52 | ✗ | return "honeykrisp"; | |
| 53 | ✗ | case VK_DRIVER_ID_MESA_DOZEN: | |
| 54 | ✗ | return "dozen"; | |
| 55 | ✗ | case VK_DRIVER_ID_MESA_VENUS: | |
| 56 | ✗ | return "venus"; | |
| 57 | ✗ | case VK_DRIVER_ID_GOOGLE_SWIFTSHADER: | |
| 58 | ✗ | return "swiftshader"; | |
| 59 | ✗ | default: | |
| 60 | ✗ | return Lowercase(name); | |
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | 42 | AdapterType ToAdapterType(VkPhysicalDeviceType type) { | |
| 65 | 42 | switch (type) { | |
| 66 | case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: | ||
| 67 | return AdapterType::Discrete; | ||
| 68 | ✗ | case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: | |
| 69 | ✗ | return AdapterType::Integrated; | |
| 70 | ✗ | case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: | |
| 71 | ✗ | return AdapterType::Virtual; | |
| 72 | 42 | case VK_PHYSICAL_DEVICE_TYPE_CPU: | |
| 73 | 42 | return AdapterType::Cpu; | |
| 74 | ✗ | default: | |
| 75 | ✗ | return AdapterType::Other; | |
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | 42 | void DescribeMemory(VkPhysicalDevice device, AdapterInfo& adapter) { | |
| 80 | 42 | VkPhysicalDeviceMemoryProperties memory; | |
| 81 | 42 | vkGetPhysicalDeviceMemoryProperties(device, &memory); | |
| 82 | 42 | uint64_t local = 0; | |
| 83 | 84 | for (uint32_t heap = 0; heap < memory.memoryHeapCount; ++heap) { | |
| 84 | 42 | if ((memory.memoryHeaps[heap].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0) { | |
| 85 | 84 | local = std::max(local, memory.memoryHeaps[heap].size); | |
| 86 | } | ||
| 87 | } | ||
| 88 | // NOLINTNEXTLINE(bugprone-signed-bitwise) | ||
| 89 | 42 | constexpr VkMemoryPropertyFlags kMapped = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | | |
| 90 | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | | ||
| 91 | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; | ||
| 92 | 42 | uint64_t mapped = 0; | |
| 93 | 84 | for (uint32_t type = 0; type < memory.memoryTypeCount; ++type) { | |
| 94 | 42 | if ((memory.memoryTypes[type].propertyFlags & kMapped) == kMapped) { | |
| 95 | 84 | mapped = std::max(mapped, memory.memoryHeaps[memory.memoryTypes[type].heapIndex].size); | |
| 96 | } | ||
| 97 | } | ||
| 98 | 42 | adapter.memoryBytes = local; | |
| 99 | 42 | adapter.features.unifiedMemory = | |
| 100 | 42 | adapter.type == AdapterType::Integrated || adapter.type == AdapterType::Cpu; | |
| 101 | 84 | adapter.features.hostMappedMemory = | |
| 102 | 42 | adapter.features.unifiedMemory || (local > 0 && mapped >= local / 10 * 9); | |
| 103 | 42 | } | |
| 104 | |||
| 105 | 42 | void DescribeFeatures(AdapterInfo& adapter, const Extensions& extensions, | |
| 106 | const FeatureChain& supported, | ||
| 107 | const VkPhysicalDeviceVulkan12Properties& vulkan12, | ||
| 108 | const VkPhysicalDeviceVulkan13Properties& vulkan13) { | ||
| 109 | 42 | Features& features = adapter.features; | |
| 110 | 42 | const bool rayQueries = std::ranges::all_of( | |
| 111 | 126 | kRayQueryExtensions, [&](const char* name) { return extensions.Has(name); }); | |
| 112 | 42 | features.hardwareTraversal = rayQueries && supported.rayQuery.rayQuery && | |
| 113 | 42 | supported.accelerationStructure.accelerationStructure; | |
| 114 | #if defined(_WIN32) | ||
| 115 | features.externalMemory = extensions.Has("VK_KHR_external_memory_win32"); | ||
| 116 | #else | ||
| 117 | 42 | features.externalMemory = extensions.Has(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME); | |
| 118 | #endif | ||
| 119 | |||
| 120 | 42 | const VkPhysicalDeviceVulkan12Features& indexing = supported.vulkan12; | |
| 121 | 126 | features.hardwareTextures = indexing.runtimeDescriptorArray && | |
| 122 | 42 | indexing.descriptorBindingPartiallyBound && | |
| 123 | 84 | indexing.descriptorBindingSampledImageUpdateAfterBind && | |
| 124 | 42 | indexing.shaderSampledImageArrayNonUniformIndexing; | |
| 125 | 84 | adapter.limits.maxTextures = | |
| 126 | features.hardwareTextures | ||
| 127 | 42 | ? std::min({kMaxTextures, vulkan12.maxPerStageDescriptorUpdateAfterBindSampledImages, | |
| 128 | 42 | vulkan12.maxDescriptorSetUpdateAfterBindSampledImages}) | |
| 129 | : 0; | ||
| 130 | |||
| 131 | 42 | features.blockCompression = supported.base.features.textureCompressionBC; | |
| 132 | 42 | features.anisotropicFiltering = supported.base.features.samplerAnisotropy; | |
| 133 | 42 | features.float64 = supported.base.features.shaderFloat64; | |
| 134 | 42 | features.atomicFloat32Add = supported.atomicFloat.shaderBufferFloat32AtomicAdd; | |
| 135 | 42 | features.atomicInt64 = supported.vulkan12.shaderBufferInt64Atomics; | |
| 136 | 42 | features.shaderClock = supported.clock.shaderDeviceClock; | |
| 137 | 42 | features.kernelStatistics = supported.executables.pipelineExecutableInfo; | |
| 138 | 42 | features.waveSizeMin = vulkan13.minSubgroupSize; | |
| 139 | 42 | features.waveSizeMax = vulkan13.maxSubgroupSize; | |
| 140 | 42 | } | |
| 141 | |||
| 142 | 42 | std::vector<std::string> MissingFeatures(VkPhysicalDevice device, const FeatureChain& supported) { | |
| 143 | 42 | std::vector<std::string> missing; | |
| 144 | 42 | if (!supported.vulkan12.bufferDeviceAddress) { | |
| 145 | ✗ | missing.emplace_back("buffer device addresses"); | |
| 146 | } | ||
| 147 | 42 | if (!supported.vulkan12.timelineSemaphore) { | |
| 148 | ✗ | missing.emplace_back("timeline semaphores"); | |
| 149 | } | ||
| 150 | 42 | if (!supported.vulkan13.synchronization2) { | |
| 151 | ✗ | missing.emplace_back("synchronization2"); | |
| 152 | } | ||
| 153 | 42 | if (!supported.vulkan13.subgroupSizeControl || !supported.vulkan13.computeFullSubgroups) { | |
| 154 | ✗ | missing.emplace_back("full subgroups"); | |
| 155 | } | ||
| 156 | 42 | if (!supported.base.features.shaderInt64) { | |
| 157 | ✗ | missing.emplace_back("64-bit integers in shaders"); | |
| 158 | } | ||
| 159 | 42 | if (!ComputeQueueFamily(device)) { | |
| 160 | ✗ | missing.emplace_back("a compute queue"); | |
| 161 | } | ||
| 162 | 42 | return missing; | |
| 163 | ✗ | } | |
| 164 | |||
| 165 | 42 | AdapterInfo Describe(VkPhysicalDevice device, uint32_t ordinal) { | |
| 166 | 42 | VkPhysicalDeviceProperties properties; | |
| 167 | 42 | vkGetPhysicalDeviceProperties(device, &properties); | |
| 168 | |||
| 169 | 42 | AdapterInfo adapter; | |
| 170 | 42 | adapter.backend = Backend::Vulkan; | |
| 171 | 42 | adapter.ordinal = ordinal; | |
| 172 | 42 | adapter.name = properties.deviceName; | |
| 173 | 42 | adapter.type = ToAdapterType(properties.deviceType); | |
| 174 | 42 | adapter.apiVersion = DecodeVersion(properties.apiVersion); | |
| 175 | 42 | adapter.driverVersion = DecodeDriverVersion(properties.vendorID, properties.driverVersion); | |
| 176 | 42 | adapter.limits.maxGroupSize = properties.limits.maxComputeWorkGroupInvocations; | |
| 177 | 42 | adapter.limits.maxParamBytes = properties.limits.maxPushConstantsSize; | |
| 178 | 42 | adapter.limits.maxAnisotropy = properties.limits.maxSamplerAnisotropy; | |
| 179 | 42 | DescribeMemory(device, adapter); | |
| 180 | 42 | if (adapter.apiVersion < kMinimumInstanceVersion) { | |
| 181 | ✗ | adapter.unsupported = TooOld("its Vulkan", adapter.apiVersion, kMinimumInstanceVersion); | |
| 182 | ✗ | return adapter; | |
| 183 | } | ||
| 184 | |||
| 185 | 42 | VkPhysicalDeviceVulkan13Properties vulkan13{ | |
| 186 | VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_PROPERTIES}; | ||
| 187 | 42 | VkPhysicalDeviceVulkan12Properties vulkan12{ | |
| 188 | 42 | VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES, &vulkan13}; | |
| 189 | 42 | VkPhysicalDeviceVulkan11Properties vulkan11{ | |
| 190 | 42 | VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES, &vulkan12}; | |
| 191 | 42 | VkPhysicalDeviceProperties2 properties2{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, | |
| 192 | 42 | &vulkan11}; | |
| 193 | 42 | vkGetPhysicalDeviceProperties2(device, &properties2); | |
| 194 | 42 | std::copy(std::begin(vulkan11.deviceUUID), std::end(vulkan11.deviceUUID), adapter.uuid.begin()); | |
| 195 | 42 | adapter.architecture = DriverName(vulkan12.driverID, vulkan12.driverName); | |
| 196 | 42 | adapter.limits.maxBufferBytes = vulkan13.maxBufferSize; | |
| 197 | 42 | adapter.limits.maxReservedBytes = adapter.limits.maxBufferBytes; | |
| 198 | |||
| 199 | 42 | const Extensions extensions(device); | |
| 200 | 42 | FeatureChain supported; | |
| 201 | 294 | supported.Link([&](const char* name) { return extensions.Has(name); }); | |
| 202 | 42 | vkGetPhysicalDeviceFeatures2(device, &supported.base); | |
| 203 | 42 | DescribeFeatures(adapter, extensions, supported, vulkan12, vulkan13); | |
| 204 | 84 | adapter.features.presentation = | |
| 205 | 84 | Extensions().Has(VK_KHR_SURFACE_EXTENSION_NAME) && | |
| 206 | 84 | extensions.Has(VK_KHR_SWAPCHAIN_EXTENSION_NAME) && supported.vulkan13.dynamicRendering && | |
| 207 | 42 | ComputeQueueFamily(device, VK_QUEUE_COMPUTE_BIT | VK_QUEUE_GRAPHICS_BIT).has_value(); | |
| 208 | |||
| 209 | 42 | const std::vector<std::string> missing = MissingFeatures(device, supported); | |
| 210 | 42 | if (!missing.empty()) { | |
| 211 | ✗ | adapter.unsupported = "it lacks " + Join(missing); | |
| 212 | } | ||
| 213 | 42 | return adapter; | |
| 214 | 42 | } | |
| 215 | |||
| 216 | } | ||
| 217 | |||
| 218 | 42 | std::vector<AdapterInfo> ListAdapters() { | |
| 219 | 42 | GetApi(); | |
| 220 | 42 | const std::vector<VkPhysicalDevice> devices = PhysicalDevices(Instance(false)); | |
| 221 | 42 | std::vector<AdapterInfo> adapters; | |
| 222 | 42 | adapters.reserve(devices.size()); | |
| 223 | 84 | for (uint32_t ordinal = 0; ordinal < devices.size(); ++ordinal) { | |
| 224 | 84 | adapters.push_back(Describe(devices[ordinal], ordinal)); | |
| 225 | } | ||
| 226 | 42 | return adapters; | |
| 227 | 42 | } | |
| 228 | |||
| 229 | } | ||
| 230 |