/* Copyright (C) 2023 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #include "precompiled.h" #include "DeviceSelection.h" #include "lib/code_annotation.h" #include "lib/config2.h" #include "renderer/backend/vulkan/Device.h" #include "renderer/backend/vulkan/Utilities.h" #include "scriptinterface/JSON.h" #include "scriptinterface/Object.h" #include "scriptinterface/ScriptInterface.h" #include "scriptinterface/ScriptRequest.h" #include #include #include #include #include namespace Renderer { namespace Backend { namespace Vulkan { namespace { std::vector GetPhysicalDeviceExtensions(VkPhysicalDevice device) { uint32_t extensionCount = 0; ENSURE_VK_SUCCESS(vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr)); std::vector extensions(extensionCount); ENSURE_VK_SUCCESS(vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, extensions.data())); std::vector availableExtensions; availableExtensions.reserve(extensions.size()); for (const VkExtensionProperties& extension : extensions) availableExtensions.emplace_back(extension.extensionName); std::sort(availableExtensions.begin(), availableExtensions.end()); return availableExtensions; } uint32_t GetDeviceTypeScore(const VkPhysicalDeviceType deviceType) { uint32_t score = 0; // We prefer discrete GPU over integrated, and integrated over others. switch (deviceType) { case VK_PHYSICAL_DEVICE_TYPE_OTHER: score = 1; break; case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: score = 4; break; case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: score = 5; break; case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: score = 3; break; case VK_PHYSICAL_DEVICE_TYPE_CPU: score = 2; break; default: break; } return score; } VkDeviceSize GetDeviceTotalMemory( const VkPhysicalDeviceMemoryProperties& memoryProperties) { VkDeviceSize totalMemory = 0; for (uint32_t heapIndex = 0; heapIndex < memoryProperties.memoryHeapCount; ++heapIndex) if (memoryProperties.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) totalMemory += memoryProperties.memoryHeaps[heapIndex].size; return totalMemory; } VkDeviceSize GetHostTotalMemory( const VkPhysicalDeviceMemoryProperties& memoryProperties) { VkDeviceSize totalMemory = 0; for (uint32_t heapIndex = 0; heapIndex < memoryProperties.memoryHeapCount; ++heapIndex) if ((memoryProperties.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) == 0) totalMemory += memoryProperties.memoryHeaps[heapIndex].size; return totalMemory; } // We don't support some types in JS, so wrap them to have in the report. template struct ReportFormatHelper { std::string operator()(const T&) const { return "unknown"; } }; template struct ReportFormatHelper>> { float operator()(const T& value) const { return static_cast(value); } }; template struct ReportFormatHelper>> { static constexpr bool IsSigned = std::is_signed_v; using ResultType = std::conditional_t; uint32_t operator()(const T& value) const { if (value > std::numeric_limits::max()) return std::numeric_limits::max(); if constexpr (IsSigned) { if (value < std::numeric_limits::min()) return std::numeric_limits::min(); } return static_cast(value); } }; template struct ReportFormatHelper>> { using HelperType = ReportFormatHelper>; using ResultType = std::invoke_result_t>; ResultType operator()(const T& value) const { HelperType helper{}; return helper(value); } }; template struct ReportFormatHelper>> { using HelperType = ReportFormatHelper>; using ElementType = std::invoke_result_t>; std::vector operator()(const T& value) const { std::vector arr; arr.reserve(std::size(value)); HelperType helper{}; for (const auto& element : value) arr.emplace_back(helper(element)); return arr; } }; SAvailablePhysicalDevice MakeAvailablePhysicalDevice( const uint32_t physicalDeviceIndex, VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, const std::vector& requiredDeviceExtensions) { SAvailablePhysicalDevice availablePhysicalDevice{}; availablePhysicalDevice.index = physicalDeviceIndex; availablePhysicalDevice.device = physicalDevice; availablePhysicalDevice.hasOutputToSurfaceSupport = false; availablePhysicalDevice.extensions = GetPhysicalDeviceExtensions(availablePhysicalDevice.device); auto hasExtension = [&extensions = availablePhysicalDevice.extensions](const char* name) -> bool { return std::find(extensions.begin(), extensions.end(), name) != extensions.end(); }; availablePhysicalDevice.hasRequiredExtensions = std::all_of(requiredDeviceExtensions.begin(), requiredDeviceExtensions.end(), hasExtension); vkGetPhysicalDeviceMemoryProperties( availablePhysicalDevice.device, &availablePhysicalDevice.memoryProperties); availablePhysicalDevice.deviceTotalMemory = GetDeviceTotalMemory(availablePhysicalDevice.memoryProperties); availablePhysicalDevice.hostTotalMemory = GetHostTotalMemory(availablePhysicalDevice.memoryProperties); if (hasExtension(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME)) { VkPhysicalDeviceDescriptorIndexingPropertiesEXT descriptorIndexingProperties{}; descriptorIndexingProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES_EXT; VkPhysicalDeviceProperties2 devicesProperties2{}; devicesProperties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; devicesProperties2.pNext = &descriptorIndexingProperties; vkGetPhysicalDeviceProperties2(availablePhysicalDevice.device, &devicesProperties2); availablePhysicalDevice.properties = devicesProperties2.properties; availablePhysicalDevice.descriptorIndexingProperties = descriptorIndexingProperties; VkPhysicalDeviceDescriptorIndexingFeaturesEXT descriptorIndexingFeatures{}; descriptorIndexingFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT; VkPhysicalDeviceFeatures2 deviceFeatures2{}; deviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; deviceFeatures2.pNext = &descriptorIndexingFeatures; vkGetPhysicalDeviceFeatures2(availablePhysicalDevice.device, &deviceFeatures2); availablePhysicalDevice.features = deviceFeatures2.features; availablePhysicalDevice.descriptorIndexingFeatures = descriptorIndexingFeatures; } else { vkGetPhysicalDeviceProperties(availablePhysicalDevice.device, &availablePhysicalDevice.properties); vkGetPhysicalDeviceFeatures(availablePhysicalDevice.device, &availablePhysicalDevice.features); } uint32_t queueFamilyCount = 0; vkGetPhysicalDeviceQueueFamilyProperties(availablePhysicalDevice.device, &queueFamilyCount, nullptr); availablePhysicalDevice.queueFamilies.resize(queueFamilyCount); vkGetPhysicalDeviceQueueFamilyProperties( availablePhysicalDevice.device, &queueFamilyCount, availablePhysicalDevice.queueFamilies.data()); availablePhysicalDevice.graphicsQueueFamilyIndex = availablePhysicalDevice.queueFamilies.size(); availablePhysicalDevice.presentQueueFamilyIndex = availablePhysicalDevice.queueFamilies.size(); if (surface != VK_NULL_HANDLE) { for (size_t familyIdx = 0; familyIdx < availablePhysicalDevice.queueFamilies.size(); ++familyIdx) { const VkQueueFamilyProperties& queueFamily = availablePhysicalDevice.queueFamilies[familyIdx]; VkBool32 hasOutputToSurfaceSupport = false; ENSURE_VK_SUCCESS(vkGetPhysicalDeviceSurfaceSupportKHR( availablePhysicalDevice.device, familyIdx, surface, &hasOutputToSurfaceSupport)); if ((queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) && hasOutputToSurfaceSupport) { availablePhysicalDevice.hasOutputToSurfaceSupport = hasOutputToSurfaceSupport; availablePhysicalDevice.graphicsQueueFamilyIndex = familyIdx; availablePhysicalDevice.presentQueueFamilyIndex = familyIdx; break; } } } ENSURE_VK_SUCCESS(vkGetPhysicalDeviceSurfaceCapabilitiesKHR( availablePhysicalDevice.device, surface, &availablePhysicalDevice.surfaceCapabilities)); uint32_t surfaceFormatCount = 0; ENSURE_VK_SUCCESS(vkGetPhysicalDeviceSurfaceFormatsKHR( availablePhysicalDevice.device, surface, &surfaceFormatCount, nullptr)); if (surfaceFormatCount > 0) { availablePhysicalDevice.surfaceFormats.resize(surfaceFormatCount); ENSURE_VK_SUCCESS(vkGetPhysicalDeviceSurfaceFormatsKHR( availablePhysicalDevice.device, surface, &surfaceFormatCount, availablePhysicalDevice.surfaceFormats.data())); } uint32_t presentModeCount = 0; ENSURE_VK_SUCCESS(vkGetPhysicalDeviceSurfacePresentModesKHR( availablePhysicalDevice.device, surface, &presentModeCount, nullptr)); if (presentModeCount > 0) { availablePhysicalDevice.presentModes.resize(presentModeCount); ENSURE_VK_SUCCESS(vkGetPhysicalDeviceSurfacePresentModesKHR( availablePhysicalDevice.device, surface, &presentModeCount, availablePhysicalDevice.presentModes.data())); } return availablePhysicalDevice; } } // anonymous namespace std::vector GetAvailablePhysicalDevices( VkInstance instance, VkSurfaceKHR surface, const std::vector& requiredDeviceExtensions) { uint32_t physicalDeviceCount = 0; ENSURE_VK_SUCCESS(vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, nullptr)); if (physicalDeviceCount == 0) return {}; std::vector availablePhysicalDevices; availablePhysicalDevices.reserve(physicalDeviceCount); std::vector physicalDevices(physicalDeviceCount); ENSURE_VK_SUCCESS(vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, physicalDevices.data())); for (uint32_t physicalDeviceIndex = 0; physicalDeviceIndex < physicalDeviceCount; ++physicalDeviceIndex) { availablePhysicalDevices.emplace_back(MakeAvailablePhysicalDevice( physicalDeviceIndex, physicalDevices[physicalDeviceIndex], surface, requiredDeviceExtensions)); } return availablePhysicalDevices; } bool IsPhysicalDeviceUnsupported(const SAvailablePhysicalDevice& device) { if (!device.hasRequiredExtensions) return true; // We can't draw something without graphics queue. And currently we don't // support separate queues for graphics and present. if (device.graphicsQueueFamilyIndex != device.presentQueueFamilyIndex) return true; if (device.graphicsQueueFamilyIndex == device.queueFamilies.size()) return true; if (!device.hasOutputToSurfaceSupport) return true; if (device.properties.limits.maxBoundDescriptorSets < 4) return true; // It's guaranteed to have sRGB but we don't support it yet. return std::none_of(device.surfaceFormats.begin(), device.surfaceFormats.end(), IsSurfaceFormatSupported); } bool ComparePhysicalDevices( const SAvailablePhysicalDevice& device1, const SAvailablePhysicalDevice& device2) { const uint32_t deviceTypeScore1 = GetDeviceTypeScore(device1.properties.deviceType); const uint32_t deviceTypeScore2 = GetDeviceTypeScore(device2.properties.deviceType); if (deviceTypeScore1 != deviceTypeScore2) return deviceTypeScore1 > deviceTypeScore2; // We use a total device memory amount to compare. We assume that more memory // means better performance as previous metrics are equal. if (device1.deviceTotalMemory != device2.deviceTotalMemory) return device1.deviceTotalMemory > device2.deviceTotalMemory; return device1.index < device2.index; } bool IsSurfaceFormatSupported( const VkSurfaceFormatKHR& surfaceFormat) { return surfaceFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR && (surfaceFormat.format == VK_FORMAT_R8G8B8A8_UNORM || surfaceFormat.format == VK_FORMAT_B8G8R8A8_UNORM); } void ReportAvailablePhysicalDevice(const SAvailablePhysicalDevice& device, const ScriptRequest& rq, JS::HandleValue settings) { Script::SetProperty(rq, settings, "name", device.properties.deviceName); Script::SetProperty(rq, settings, "version", std::to_string(VK_API_VERSION_VARIANT(device.properties.apiVersion)) + "." + std::to_string(VK_API_VERSION_MAJOR(device.properties.apiVersion)) + "." + std::to_string(VK_API_VERSION_MINOR(device.properties.apiVersion)) + "." + std::to_string(VK_API_VERSION_PATCH(device.properties.apiVersion))); Script::SetProperty(rq, settings, "apiVersion", device.properties.apiVersion); Script::SetProperty(rq, settings, "driverVersion", device.properties.driverVersion); Script::SetProperty(rq, settings, "vendorID", device.properties.vendorID); Script::SetProperty(rq, settings, "deviceID", device.properties.deviceID); Script::SetProperty(rq, settings, "deviceType", static_cast(device.properties.deviceType)); Script::SetProperty(rq, settings, "index", device.index); JS::RootedValue memory(rq.cx); Script::CreateObject(rq, &memory); JS::RootedValue memoryTypes(rq.cx); Script::CreateArray(rq, &memoryTypes, device.memoryProperties.memoryTypeCount); for (uint32_t memoryTypeIndex = 0; memoryTypeIndex < device.memoryProperties.memoryTypeCount; ++memoryTypeIndex) { const VkMemoryType& type = device.memoryProperties.memoryTypes[memoryTypeIndex]; JS::RootedValue memoryType(rq.cx); Script::CreateObject(rq, &memoryType); Script::SetProperty(rq, memoryType, "propertyFlags", static_cast(type.propertyFlags)); Script::SetProperty(rq, memoryType, "heapIndex", type.heapIndex); Script::SetPropertyInt(rq, memoryTypes, memoryTypeIndex, memoryType); } JS::RootedValue memoryHeaps(rq.cx); Script::CreateArray(rq, &memoryHeaps, device.memoryProperties.memoryHeapCount); for (uint32_t memoryHeapIndex = 0; memoryHeapIndex < device.memoryProperties.memoryHeapCount; ++memoryHeapIndex) { const VkMemoryHeap& heap = device.memoryProperties.memoryHeaps[memoryHeapIndex]; JS::RootedValue memoryHeap(rq.cx); Script::CreateObject(rq, &memoryHeap); // We can't serialize uint64_t in JS, so put data in KiB. Script::SetProperty(rq, memoryHeap, "size", static_cast(heap.size / 1024)); Script::SetProperty(rq, memoryHeap, "flags", static_cast(heap.flags)); Script::SetPropertyInt(rq, memoryHeaps, memoryHeapIndex, memoryHeap); } Script::SetProperty(rq, memory, "types", memoryTypes); Script::SetProperty(rq, memory, "heaps", memoryHeaps); Script::SetProperty(rq, settings, "memory", memory); JS::RootedValue constants(rq.cx); Script::CreateObject(rq, &constants); JS::RootedValue limitsConstants(rq.cx); Script::CreateObject(rq, &limitsConstants); #define REPORT_LIMITS_CONSTANT(NAME) \ do \ { \ const ReportFormatHelper helper{}; \ Script::SetProperty(rq, limitsConstants, #NAME, helper(device.properties.limits.NAME)); \ } while (0) REPORT_LIMITS_CONSTANT(maxImageDimension1D); REPORT_LIMITS_CONSTANT(maxImageDimension2D); REPORT_LIMITS_CONSTANT(maxImageDimension3D); REPORT_LIMITS_CONSTANT(maxImageDimensionCube); REPORT_LIMITS_CONSTANT(maxImageArrayLayers); REPORT_LIMITS_CONSTANT(maxUniformBufferRange); REPORT_LIMITS_CONSTANT(maxStorageBufferRange); REPORT_LIMITS_CONSTANT(maxPushConstantsSize); REPORT_LIMITS_CONSTANT(maxMemoryAllocationCount); REPORT_LIMITS_CONSTANT(maxSamplerAllocationCount); REPORT_LIMITS_CONSTANT(bufferImageGranularity); REPORT_LIMITS_CONSTANT(maxBoundDescriptorSets); REPORT_LIMITS_CONSTANT(maxPerStageDescriptorSamplers); REPORT_LIMITS_CONSTANT(maxPerStageDescriptorUniformBuffers); REPORT_LIMITS_CONSTANT(maxPerStageDescriptorStorageBuffers); REPORT_LIMITS_CONSTANT(maxPerStageDescriptorSampledImages); REPORT_LIMITS_CONSTANT(maxPerStageDescriptorStorageImages); REPORT_LIMITS_CONSTANT(maxPerStageDescriptorInputAttachments); REPORT_LIMITS_CONSTANT(maxPerStageResources); REPORT_LIMITS_CONSTANT(maxDescriptorSetSamplers); REPORT_LIMITS_CONSTANT(maxDescriptorSetUniformBuffers); REPORT_LIMITS_CONSTANT(maxDescriptorSetUniformBuffersDynamic); REPORT_LIMITS_CONSTANT(maxDescriptorSetStorageBuffers); REPORT_LIMITS_CONSTANT(maxDescriptorSetStorageBuffersDynamic); REPORT_LIMITS_CONSTANT(maxDescriptorSetSampledImages); REPORT_LIMITS_CONSTANT(maxDescriptorSetStorageImages); REPORT_LIMITS_CONSTANT(maxDescriptorSetInputAttachments); REPORT_LIMITS_CONSTANT(maxVertexInputAttributes); REPORT_LIMITS_CONSTANT(maxVertexInputBindings); REPORT_LIMITS_CONSTANT(maxVertexInputAttributeOffset); REPORT_LIMITS_CONSTANT(maxVertexInputBindingStride); REPORT_LIMITS_CONSTANT(maxComputeSharedMemorySize); REPORT_LIMITS_CONSTANT(maxComputeWorkGroupCount); REPORT_LIMITS_CONSTANT(maxComputeWorkGroupInvocations); REPORT_LIMITS_CONSTANT(maxComputeWorkGroupSize); REPORT_LIMITS_CONSTANT(maxDrawIndexedIndexValue); REPORT_LIMITS_CONSTANT(maxSamplerLodBias); REPORT_LIMITS_CONSTANT(maxSamplerAnisotropy); REPORT_LIMITS_CONSTANT(minMemoryMapAlignment); REPORT_LIMITS_CONSTANT(minTexelBufferOffsetAlignment); REPORT_LIMITS_CONSTANT(minUniformBufferOffsetAlignment); REPORT_LIMITS_CONSTANT(minStorageBufferOffsetAlignment); REPORT_LIMITS_CONSTANT(maxFramebufferWidth); REPORT_LIMITS_CONSTANT(maxFramebufferHeight); REPORT_LIMITS_CONSTANT(maxFramebufferLayers); REPORT_LIMITS_CONSTANT(framebufferColorSampleCounts); REPORT_LIMITS_CONSTANT(framebufferDepthSampleCounts); REPORT_LIMITS_CONSTANT(framebufferStencilSampleCounts); REPORT_LIMITS_CONSTANT(framebufferNoAttachmentsSampleCounts); REPORT_LIMITS_CONSTANT(maxColorAttachments); REPORT_LIMITS_CONSTANT(sampledImageColorSampleCounts); REPORT_LIMITS_CONSTANT(sampledImageDepthSampleCounts); REPORT_LIMITS_CONSTANT(sampledImageStencilSampleCounts); REPORT_LIMITS_CONSTANT(storageImageSampleCounts); REPORT_LIMITS_CONSTANT(optimalBufferCopyOffsetAlignment); REPORT_LIMITS_CONSTANT(optimalBufferCopyRowPitchAlignment); #undef REPORT_LIMITS_CONSTANT Script::SetProperty(rq, constants, "limits", limitsConstants); JS::RootedValue descriptorIndexingConstants(rq.cx); Script::CreateObject(rq, &descriptorIndexingConstants); #define REPORT_DESCRIPTOR_INDEXING_CONSTANT(NAME) \ do \ { \ const ReportFormatHelper helper{}; \ Script::SetProperty(rq, descriptorIndexingConstants, #NAME, helper(device.descriptorIndexingProperties.NAME)); \ } while (0) REPORT_DESCRIPTOR_INDEXING_CONSTANT(maxUpdateAfterBindDescriptorsInAllPools); REPORT_DESCRIPTOR_INDEXING_CONSTANT(shaderSampledImageArrayNonUniformIndexingNative); REPORT_DESCRIPTOR_INDEXING_CONSTANT(maxPerStageDescriptorUpdateAfterBindSamplers); REPORT_DESCRIPTOR_INDEXING_CONSTANT(maxPerStageDescriptorUpdateAfterBindSampledImages); REPORT_DESCRIPTOR_INDEXING_CONSTANT(maxPerStageDescriptorUpdateAfterBindUniformBuffers); REPORT_DESCRIPTOR_INDEXING_CONSTANT(maxPerStageUpdateAfterBindResources); REPORT_DESCRIPTOR_INDEXING_CONSTANT(maxDescriptorSetUpdateAfterBindSamplers); REPORT_DESCRIPTOR_INDEXING_CONSTANT(maxDescriptorSetUpdateAfterBindSampledImages); REPORT_DESCRIPTOR_INDEXING_CONSTANT(maxDescriptorSetUpdateAfterBindUniformBuffers); REPORT_DESCRIPTOR_INDEXING_CONSTANT(maxDescriptorSetUpdateAfterBindUniformBuffersDynamic); #undef REPORT_DESCRIPTOR_INDEXING_CONSTANT Script::SetProperty(rq, constants, "descriptor_indexing", descriptorIndexingConstants); Script::SetProperty(rq, settings, "constants", constants); JS::RootedValue features(rq.cx); Script::CreateObject(rq, &features); #define REPORT_FEATURE(NAME) \ Script::SetProperty(rq, features, #NAME, static_cast(device.features.NAME)); REPORT_FEATURE(robustBufferAccess); REPORT_FEATURE(fullDrawIndexUint32); REPORT_FEATURE(imageCubeArray); REPORT_FEATURE(geometryShader); REPORT_FEATURE(tessellationShader); REPORT_FEATURE(logicOp); REPORT_FEATURE(multiDrawIndirect); REPORT_FEATURE(depthClamp); REPORT_FEATURE(depthBiasClamp); REPORT_FEATURE(fillModeNonSolid); REPORT_FEATURE(samplerAnisotropy); REPORT_FEATURE(textureCompressionETC2); REPORT_FEATURE(textureCompressionASTC_LDR); REPORT_FEATURE(textureCompressionBC); REPORT_FEATURE(pipelineStatisticsQuery); REPORT_FEATURE(shaderUniformBufferArrayDynamicIndexing); REPORT_FEATURE(shaderSampledImageArrayDynamicIndexing); #undef REPORT_FEATURE #define REPORT_DESCRIPTOR_INDEXING_FEATURE(NAME) \ Script::SetProperty(rq, features, #NAME, static_cast(device.descriptorIndexingFeatures.NAME)); REPORT_DESCRIPTOR_INDEXING_FEATURE(shaderSampledImageArrayNonUniformIndexing); REPORT_DESCRIPTOR_INDEXING_FEATURE(descriptorBindingUniformBufferUpdateAfterBind); REPORT_DESCRIPTOR_INDEXING_FEATURE(descriptorBindingSampledImageUpdateAfterBind); REPORT_DESCRIPTOR_INDEXING_FEATURE(descriptorBindingPartiallyBound); REPORT_DESCRIPTOR_INDEXING_FEATURE(descriptorBindingUpdateUnusedWhilePending); REPORT_DESCRIPTOR_INDEXING_FEATURE(descriptorBindingPartiallyBound); REPORT_DESCRIPTOR_INDEXING_FEATURE(descriptorBindingVariableDescriptorCount); REPORT_DESCRIPTOR_INDEXING_FEATURE(runtimeDescriptorArray); #undef REPORT_DESCRIPTOR_INDEXING_FEATURE Script::SetProperty(rq, settings, "features", features); JS::RootedValue presentModes(rq.cx); Script::CreateArray(rq, &presentModes, device.presentModes.size()); for (size_t index = 0; index < device.presentModes.size(); ++index) { Script::SetPropertyInt( rq, presentModes, index, static_cast(device.presentModes[index])); } Script::SetProperty(rq, settings, "present_modes", presentModes); JS::RootedValue surfaceFormats(rq.cx); Script::CreateArray(rq, &surfaceFormats, device.surfaceFormats.size()); for (size_t index = 0; index < device.surfaceFormats.size(); ++index) { JS::RootedValue surfaceFormat(rq.cx); Script::CreateObject(rq, &surfaceFormat); Script::SetProperty( rq, surfaceFormat, "format", static_cast(device.surfaceFormats[index].format)); Script::SetProperty( rq, surfaceFormat, "color_space", static_cast(device.surfaceFormats[index].colorSpace)); Script::SetPropertyInt(rq, surfaceFormats, index, surfaceFormat); } Script::SetProperty(rq, settings, "surface_formats", surfaceFormats); JS::RootedValue surfaceCapabilities(rq.cx); Script::CreateObject(rq, &surfaceCapabilities); #define REPORT_SURFACE_CAPABILITIES_CONSTANT(NAME) \ do \ { \ const ReportFormatHelper helper{}; \ Script::SetProperty(rq, surfaceCapabilities, #NAME, helper(device.surfaceCapabilities.NAME)); \ } while (0) REPORT_SURFACE_CAPABILITIES_CONSTANT(minImageCount); REPORT_SURFACE_CAPABILITIES_CONSTANT(maxImageCount); REPORT_SURFACE_CAPABILITIES_CONSTANT(maxImageArrayLayers); REPORT_SURFACE_CAPABILITIES_CONSTANT(supportedTransforms); REPORT_SURFACE_CAPABILITIES_CONSTANT(supportedCompositeAlpha); REPORT_SURFACE_CAPABILITIES_CONSTANT(supportedUsageFlags); #undef REPORT_SURFACE_CAPABILITIES_CONSTANT Script::SetProperty(rq, settings, "surface_capabilities", surfaceCapabilities); } } // namespace Vulkan } // namespace Backend } // namespace Renderer