feat(vulkan): run the compute/UI path without VK_EXT_descriptor_heap
Device::Initialize required twelve device extensions with no capability check. Mesa Turnip (Adreno) lacks six of them: VK_EXT_descriptor_heap, VK_KHR_shader_untyped_pointers, VK_EXT_device_fault and the three ray tracing extensions. vkCreateDevice failed there, so not even a UI-only program could start, although the UI path is compute-only and needs nothing else that Turnip is missing. The descriptor heap, ray tracing and device fault are now optional groups, detected at start-up from the extension list and the feature bits (Device::descriptorHeapSupported, rayTracingSupported, deviceFaultSupported). Ray tracing needs the heap; on a device without it the ray tracing entry points throw a message naming the cause. Without the heap the same bindless slots live in one update-after-bind descriptor set per frame, owned by DescriptorHeapVulkan: binding 0 storage images, 1 sampled images, 2 samplers, 3 storage buffers. Slot allocation, the slot handles, UIDispatchHeader and the UIRenderer API are unchanged; buffer slots simply stop being offset. Window::Render binds that frame's set where it binds the heap, every compute pipeline shares the heap object's pipeline layout, and push data travels as push constants. Each UI kernel is built twice from one source. ui-shared.glsl declares its resources through UI_HEAP_* macros that expand to descriptor_heap or, with CRAFTER_GRAPHICS_DESCRIPTOR_SETS defined, to set/binding. <name>.comp.spv and <name>.comp.sets.spv ship side by side and ComputeShader::Load picks one. The heap build of every shader is byte-identical to before. Breaking: - ComputeShader::Load takes the heap as a second argument. - DescriptorHeapVulkan is no longer copyable or movable. - Custom UI shaders declare heap resources with the UI_HEAP_* macros and are registered twice in project.cpp (see the README). - project.cpp uses Shader::defines and Shader::variant, so it needs a crafter-build that has them (Crafter.Build master, fe38cc1 or later). Also: - maintenance5 is enabled as a feature, not only as an extension: every VulkanBuffer already relied on VkBufferUsageFlags2CreateInfo. - Barrier stage masks drop RAY_TRACING_SHADER on devices without ray tracing, and ImageVulkan::Create resolves its default consumer stage to COMPUTE_SHADER there. - Window refuses a swapchain whose image count is not numFrames instead of writing past images[]. The limitation itself is #160. - CRAFTER_GRAPHICS_NO_DESCRIPTOR_HEAP forces the fallback on a driver that has the heap, so it can be exercised on a desktop GPU. Verified on an RTX 4090 in both modes under the validation layer, and on a Fairphone 6 (Turnip, Adreno 810, postmarketOS), where the fallback is the only path. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
6aa0338c0f
commit
48aa9be5ce
22 changed files with 912 additions and 110 deletions
|
|
@ -41,19 +41,31 @@ const char* const instanceExtensionNames[] = {
|
|||
"VK_KHR_wayland_surface"
|
||||
#endif
|
||||
};
|
||||
const char* const deviceExtensionNames[] = {
|
||||
// Device extensions are split by what breaks without them. Only the first
|
||||
// group is needed to put UI pixels on screen; Device::Initialize enables each
|
||||
// of the others only when the driver advertises all of its members, and
|
||||
// records the outcome in Device::descriptorHeapSupported / rayTracingSupported
|
||||
// / deviceFaultSupported. Mesa Turnip (Adreno), for one, has none of the heap,
|
||||
// ray tracing or fault extensions.
|
||||
const char* const requiredDeviceExtensionNames[] = {
|
||||
"VK_KHR_swapchain",
|
||||
"VK_KHR_spirv_1_4",
|
||||
"VK_KHR_shader_float_controls",
|
||||
"VK_KHR_maintenance5",
|
||||
};
|
||||
// Without these, descriptors come from a bindless descriptor set instead
|
||||
// (DescriptorHeapVulkan's sets mode) and shaders load their .sets.spv build.
|
||||
const char* const descriptorHeapExtensionNames[] = {
|
||||
"VK_EXT_descriptor_heap",
|
||||
"VK_KHR_shader_untyped_pointers",
|
||||
};
|
||||
// Ray tracing additionally needs the descriptor heap: every RT pipeline and
|
||||
// RT shader in this library is heap-only.
|
||||
const char* const rayTracingExtensionNames[] = {
|
||||
"VK_KHR_acceleration_structure",
|
||||
"VK_KHR_ray_tracing_pipeline",
|
||||
"VK_KHR_ray_query",
|
||||
"VK_EXT_shader_atomic_float",
|
||||
"VK_EXT_descriptor_heap",
|
||||
"VK_KHR_deferred_host_operations",
|
||||
"VK_KHR_maintenance5",
|
||||
"VK_KHR_shader_untyped_pointers",
|
||||
"VK_EXT_device_fault"
|
||||
};
|
||||
const char* const layerNames[] = {
|
||||
"VK_LAYER_KHRONOS_validation"
|
||||
|
|
@ -63,7 +75,9 @@ const char* const layerNames[] = {
|
|||
void Device::CheckVkResult(VkResult result) {
|
||||
if (result != VK_SUCCESS)
|
||||
{
|
||||
if(result == VK_ERROR_DEVICE_LOST) {
|
||||
// The fault report needs VK_EXT_device_fault; without it the entry
|
||||
// point below is not callable and the bare VkResult is all there is.
|
||||
if(result == VK_ERROR_DEVICE_LOST && deviceFaultSupported) {
|
||||
VkDeviceFaultCountsEXT faultCounts = {
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_FAULT_COUNTS_EXT,
|
||||
.pNext = NULL,
|
||||
|
|
@ -119,6 +133,26 @@ void Device::CheckVkResult(VkResult result) {
|
|||
}
|
||||
}
|
||||
|
||||
void Device::RequireRayTracing() {
|
||||
if (rayTracingSupported) {
|
||||
return;
|
||||
}
|
||||
// Thrown at the first ray tracing entry point (an RT-stage shader module,
|
||||
// a BLAS/TLAS build, the SBT, the pipeline) so the failure names the
|
||||
// cause. Letting the call through would hand heap-only SPIR-V or an
|
||||
// unloaded entry point to a driver that never enabled the feature.
|
||||
if (descriptorHeapSupported) {
|
||||
throw std::runtime_error(
|
||||
"Crafter.Graphics: ray tracing is unavailable: the Vulkan driver lacks one of "
|
||||
"VK_KHR_acceleration_structure, VK_KHR_ray_tracing_pipeline, VK_KHR_ray_query, "
|
||||
"VK_KHR_deferred_host_operations");
|
||||
}
|
||||
throw std::runtime_error(
|
||||
"Crafter.Graphics: ray tracing is unavailable: it needs VK_EXT_descriptor_heap and "
|
||||
"VK_KHR_shader_untyped_pointers, which this driver lacks (or "
|
||||
"CRAFTER_GRAPHICS_NO_DESCRIPTOR_HEAP is set). Only the compute/UI path runs here.");
|
||||
}
|
||||
|
||||
bool Device::PipelineCacheDataCompatible(std::span<const std::byte> data) {
|
||||
// VkPipelineCacheHeaderVersionOne is a fixed 32-byte little-endian header
|
||||
// (Vulkan spec 16.5.2): u32 headerSize, u32 headerVersion, u32 vendorID,
|
||||
|
|
@ -651,36 +685,130 @@ void Device::Initialize() {
|
|||
}
|
||||
}
|
||||
|
||||
// Enumerate available device extensions so we can opt into
|
||||
// VK_EXT_memory_decompression when the driver advertises it. Drivers
|
||||
// without it (AMD, Intel as of early 2026) get the CPU-decode fallback.
|
||||
// Enumerate the driver's device extensions once; every optional capability
|
||||
// below is decided from this set. Drivers without
|
||||
// VK_EXT_memory_decompression (AMD, Intel as of early 2026) get the
|
||||
// CPU-decode fallback; drivers without the descriptor heap (Mesa Turnip)
|
||||
// get the descriptor-set path and no ray tracing.
|
||||
std::unordered_set<std::string> availableExtensions;
|
||||
{
|
||||
std::uint32_t extCount = 0;
|
||||
vkEnumerateDeviceExtensionProperties(physDevice, nullptr, &extCount, nullptr);
|
||||
std::vector<VkExtensionProperties> exts(extCount);
|
||||
vkEnumerateDeviceExtensionProperties(physDevice, nullptr, &extCount, exts.data());
|
||||
for (const VkExtensionProperties& e : exts) {
|
||||
if (std::strcmp(e.extensionName, VK_EXT_MEMORY_DECOMPRESSION_EXTENSION_NAME) == 0) {
|
||||
memoryDecompressionSupported = true;
|
||||
break;
|
||||
availableExtensions.emplace(e.extensionName);
|
||||
}
|
||||
}
|
||||
const auto hasAll = [&availableExtensions](std::span<const char* const> names) {
|
||||
return std::ranges::all_of(names, [&](const char* name) { return availableExtensions.contains(name); });
|
||||
};
|
||||
|
||||
// Fail here, naming the gap, rather than in vkCreateDevice with a bare
|
||||
// VK_ERROR_EXTENSION_NOT_PRESENT.
|
||||
{
|
||||
std::string missing;
|
||||
for (const char* name : requiredDeviceExtensionNames) {
|
||||
if (!availableExtensions.contains(name)) {
|
||||
missing += ' ';
|
||||
missing += name;
|
||||
}
|
||||
}
|
||||
if (!missing.empty()) {
|
||||
throw std::runtime_error(std::format("Crafter.Graphics: the Vulkan driver lacks required device extension(s):{}", missing));
|
||||
}
|
||||
}
|
||||
|
||||
// Properties query: chain memory-decompression props only when supported,
|
||||
// otherwise sType validation flags it as an unrecognized struct on
|
||||
// drivers that don't expose the extension.
|
||||
if (memoryDecompressionSupported) {
|
||||
memoryDecompressionProperties.pNext = const_cast<void*>(rayTracingProperties.pNext);
|
||||
rayTracingProperties.pNext = &memoryDecompressionProperties;
|
||||
// CRAFTER_GRAPHICS_NO_DESCRIPTOR_HEAP forces the descriptor-set path on a
|
||||
// driver that does have the heap, so the fallback can be exercised on a
|
||||
// desktop GPU. It takes ray tracing down with it, exactly as on a driver
|
||||
// that really lacks the extension.
|
||||
//
|
||||
// A listed extension is not the whole answer: a driver may list one whose
|
||||
// feature bit is still off. Query the feature bits of the optional groups
|
||||
// whose extensions are present (only those structs are chained) and
|
||||
// require both, so such a driver takes the fallback instead of failing
|
||||
// in vkCreateDevice.
|
||||
const bool heapExtensions = hasAll(descriptorHeapExtensionNames);
|
||||
const bool rayTracingExtensions = hasAll(rayTracingExtensionNames);
|
||||
const bool faultExtension = availableExtensions.contains(VK_EXT_DEVICE_FAULT_EXTENSION_NAME);
|
||||
VkPhysicalDeviceDescriptorHeapFeaturesEXT heapQuery {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_HEAP_FEATURES_EXT,
|
||||
};
|
||||
VkPhysicalDeviceShaderUntypedPointersFeaturesKHR untypedPointersQuery {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_UNTYPED_POINTERS_FEATURES_KHR,
|
||||
};
|
||||
VkPhysicalDeviceAccelerationStructureFeaturesKHR accelerationStructureQuery {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR,
|
||||
};
|
||||
VkPhysicalDeviceRayTracingPipelineFeaturesKHR rayTracingPipelineQuery {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR,
|
||||
};
|
||||
VkPhysicalDeviceRayQueryFeaturesKHR rayQueryQuery {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_QUERY_FEATURES_KHR,
|
||||
};
|
||||
VkPhysicalDeviceFaultFeaturesEXT faultQuery {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FAULT_FEATURES_EXT,
|
||||
};
|
||||
VkPhysicalDeviceFeatures2 featureQuery {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
|
||||
};
|
||||
const auto ask = [&featureQuery](auto& features) {
|
||||
features.pNext = featureQuery.pNext;
|
||||
featureQuery.pNext = &features;
|
||||
};
|
||||
if (heapExtensions) {
|
||||
ask(heapQuery);
|
||||
ask(untypedPointersQuery);
|
||||
}
|
||||
if (rayTracingExtensions) {
|
||||
ask(accelerationStructureQuery);
|
||||
ask(rayTracingPipelineQuery);
|
||||
ask(rayQueryQuery);
|
||||
}
|
||||
if (faultExtension) {
|
||||
ask(faultQuery);
|
||||
}
|
||||
vkGetPhysicalDeviceFeatures2(physDevice, &featureQuery);
|
||||
|
||||
descriptorHeapSupported = heapExtensions
|
||||
&& heapQuery.descriptorHeap && untypedPointersQuery.shaderUntypedPointers
|
||||
&& std::getenv("CRAFTER_GRAPHICS_NO_DESCRIPTOR_HEAP") == nullptr;
|
||||
rayTracingSupported = descriptorHeapSupported && rayTracingExtensions
|
||||
&& accelerationStructureQuery.accelerationStructure
|
||||
&& rayTracingPipelineQuery.rayTracingPipeline
|
||||
&& rayQueryQuery.rayQuery;
|
||||
deviceFaultSupported = faultExtension && faultQuery.deviceFault;
|
||||
memoryDecompressionSupported = availableExtensions.contains(VK_EXT_MEMORY_DECOMPRESSION_EXTENSION_NAME);
|
||||
const bool atomicFloatSupported = availableExtensions.contains(VK_EXT_SHADER_ATOMIC_FLOAT_EXTENSION_NAME);
|
||||
// RAY_TRACING_SHADER is not a valid pipeline stage unless the ray tracing
|
||||
// pipeline feature is enabled; barrier code masks its stage flags with this.
|
||||
stageMask = rayTracingSupported
|
||||
? ~VkPipelineStageFlags{0}
|
||||
: ~VkPipelineStageFlags{VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR};
|
||||
|
||||
// Properties query, built back to front. A struct is chained only when its
|
||||
// extension is being used: an unrecognized sType is a validation error on
|
||||
// drivers that don't expose the extension, and the heap properties must
|
||||
// stay zero when the heap is forced off. Driver properties are core and
|
||||
// always queried; they identify the NVIDIA proprietary driver for the
|
||||
// descriptor-heap AS-read workaround (issue #15 / #7).
|
||||
void* propertiesChain = &driverProperties;
|
||||
if (descriptorHeapSupported) {
|
||||
descriptorHeapProperties.pNext = propertiesChain;
|
||||
propertiesChain = &descriptorHeapProperties;
|
||||
}
|
||||
if (rayTracingSupported) {
|
||||
rayTracingProperties.pNext = propertiesChain;
|
||||
propertiesChain = &rayTracingProperties;
|
||||
}
|
||||
if (memoryDecompressionSupported) {
|
||||
memoryDecompressionProperties.pNext = propertiesChain;
|
||||
propertiesChain = &memoryDecompressionProperties;
|
||||
}
|
||||
// Chain driver properties onto the tail of the query so we can detect
|
||||
// the NVIDIA proprietary driver for the descriptor-heap AS-read
|
||||
// workaround (issue #15 / #7).
|
||||
descriptorHeapProperties.pNext = &driverProperties;
|
||||
VkPhysicalDeviceProperties2 properties2 {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
|
||||
.pNext = &rayTracingProperties
|
||||
.pNext = propertiesChain
|
||||
};
|
||||
vkGetPhysicalDeviceProperties2(physDevice, &properties2);
|
||||
// Keep the core properties around for the pipeline-cache identity check
|
||||
|
|
@ -694,8 +822,13 @@ void Device::Initialize() {
|
|||
// NVIDIA's brand-new VK_EXT_descriptor_heap acceleration-structure read
|
||||
// path faults (see #7); enable the SPIR-V rewrite workaround there. Other
|
||||
// drivers (and any future fixed NVIDIA driver, once this gate is removed)
|
||||
// take the normal heap-bound AS path unchanged.
|
||||
workaroundDescriptorHeapAS = (driverProperties.driverID == VK_DRIVER_ID_NVIDIA_PROPRIETARY);
|
||||
// take the normal heap-bound AS path unchanged. The workaround is about
|
||||
// the heap, so it is off whenever the heap is: otherwise a forced-fallback
|
||||
// run on NVIDIA would still rewrite SPIR-V, enable shaderInt64 and push the
|
||||
// TLAS address through vkCmdPushDataEXT, none of which a real heap-less
|
||||
// driver does.
|
||||
workaroundDescriptorHeapAS = descriptorHeapSupported
|
||||
&& driverProperties.driverID == VK_DRIVER_ID_NVIDIA_PROPRIETARY;
|
||||
|
||||
// Sanity-gate: GDeflate 1.0 must actually be in the supported method set.
|
||||
if (memoryDecompressionSupported &&
|
||||
|
|
@ -727,24 +860,18 @@ void Device::Initialize() {
|
|||
queueCreateInfo.queueCount = 1;
|
||||
queueCreateInfo.pQueuePriorities = &priority;
|
||||
|
||||
VkPhysicalDeviceFaultFeaturesEXT faultFeatures2 = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FAULT_FEATURES_EXT,
|
||||
};
|
||||
VkPhysicalDeviceFeatures2 features22 = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
|
||||
.pNext = &faultFeatures2,
|
||||
};
|
||||
vkGetPhysicalDeviceFeatures2(physDevice, &features22);
|
||||
|
||||
// Feature structs below are declared without a pNext and linked into one
|
||||
// chain afterwards, each only when its extension is being enabled: a
|
||||
// feature struct for an extension that is not enabled is invalid, and
|
||||
// which ones are enabled now depends on the driver.
|
||||
VkPhysicalDeviceFaultFeaturesEXT faultFeatures = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FAULT_FEATURES_EXT,
|
||||
.deviceFault = VK_TRUE,
|
||||
.deviceFaultVendorBinary = faultFeatures2.deviceFaultVendorBinary,
|
||||
.deviceFaultVendorBinary = faultQuery.deviceFaultVendorBinary,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceShaderUntypedPointersFeaturesKHR untypedPointersFeatures {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_UNTYPED_POINTERS_FEATURES_KHR,
|
||||
.pNext = &faultFeatures,
|
||||
.shaderUntypedPointers = VK_TRUE,
|
||||
};
|
||||
|
||||
|
|
@ -752,37 +879,38 @@ void Device::Initialize() {
|
|||
// VK_ACCESS_2_*) — required for VK_EXT_memory_decompression's sync tokens.
|
||||
VkPhysicalDeviceVulkan13Features features13 {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES,
|
||||
.pNext = &untypedPointersFeatures,
|
||||
.synchronization2 = VK_TRUE,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceMemoryDecompressionFeaturesEXT memoryDecompressionFeatures {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_DECOMPRESSION_FEATURES_EXT,
|
||||
.pNext = &features13,
|
||||
.memoryDecompression = VK_TRUE,
|
||||
};
|
||||
void* postDecompressChain = memoryDecompressionSupported
|
||||
? static_cast<void*>(&memoryDecompressionFeatures)
|
||||
: static_cast<void*>(&features13);
|
||||
|
||||
VkPhysicalDeviceDescriptorHeapFeaturesEXT desciptorHeapFeatures {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_HEAP_FEATURES_EXT,
|
||||
.pNext = postDecompressChain,
|
||||
.descriptorHeap = VK_TRUE,
|
||||
};
|
||||
|
||||
// Every VulkanBuffer is created with usage = 0 plus a
|
||||
// VkBufferUsageFlags2CreateInfo, and heap pipelines carry a
|
||||
// VkPipelineCreateFlags2CreateInfo. Both are maintenance5 functionality,
|
||||
// which needs the feature bit as well as the extension name.
|
||||
VkPhysicalDeviceMaintenance5Features maintenance5Features {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_FEATURES,
|
||||
.maintenance5 = VK_TRUE,
|
||||
};
|
||||
|
||||
VkPhysicalDevice16BitStorageFeatures bit16 {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES,
|
||||
.pNext = &desciptorHeapFeatures,
|
||||
.storageBuffer16BitAccess = VK_TRUE,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceVulkan12Features features12 {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
|
||||
.pNext = &bit16,
|
||||
.shaderFloat16 = VK_TRUE,
|
||||
// Bindless / runtime descriptor array indexing — needed for the
|
||||
// descriptor_heap shader path.
|
||||
// Bindless / runtime descriptor array indexing — needed by both
|
||||
// binding models (heap and descriptor sets).
|
||||
.shaderUniformBufferArrayNonUniformIndexing = VK_TRUE,
|
||||
.shaderSampledImageArrayNonUniformIndexing = VK_TRUE,
|
||||
.shaderStorageBufferArrayNonUniformIndexing = VK_TRUE,
|
||||
|
|
@ -791,28 +919,64 @@ void Device::Initialize() {
|
|||
.scalarBlockLayout = VK_TRUE,
|
||||
.bufferDeviceAddress = VK_TRUE
|
||||
};
|
||||
if (!descriptorHeapSupported) {
|
||||
// Descriptor-set path (DescriptorHeapVulkan sets mode): one bindless
|
||||
// set whose slots are written while earlier frames may still be
|
||||
// executing with it bound, and where most slots are never written.
|
||||
// The sampled-image bit also covers SAMPLER descriptors.
|
||||
features12.descriptorIndexing = VK_TRUE;
|
||||
features12.descriptorBindingSampledImageUpdateAfterBind = VK_TRUE;
|
||||
features12.descriptorBindingStorageImageUpdateAfterBind = VK_TRUE;
|
||||
features12.descriptorBindingStorageBufferUpdateAfterBind = VK_TRUE;
|
||||
features12.descriptorBindingUpdateUnusedWhilePending = VK_TRUE;
|
||||
features12.descriptorBindingPartiallyBound = VK_TRUE;
|
||||
}
|
||||
|
||||
VkPhysicalDeviceRayQueryFeaturesKHR physicalDeviceRayQueryFeatures{
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_QUERY_FEATURES_KHR,
|
||||
.pNext = &features12,
|
||||
.rayQuery = VK_TRUE
|
||||
};
|
||||
|
||||
VkPhysicalDeviceRayTracingPipelineFeaturesKHR physicalDeviceRayTracingPipelineFeatures{
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR,
|
||||
.pNext = &physicalDeviceRayQueryFeatures,
|
||||
.rayTracingPipeline = VK_TRUE
|
||||
};
|
||||
|
||||
VkPhysicalDeviceAccelerationStructureFeaturesKHR deviceAccelerationStructureFeature = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR,
|
||||
.pNext = &physicalDeviceRayTracingPipelineFeatures,
|
||||
.accelerationStructure = VK_TRUE
|
||||
};
|
||||
|
||||
void* featureChain = nullptr;
|
||||
const auto link = [&featureChain](auto& features) {
|
||||
features.pNext = featureChain;
|
||||
featureChain = &features;
|
||||
};
|
||||
if (deviceFaultSupported) {
|
||||
link(faultFeatures);
|
||||
}
|
||||
if (descriptorHeapSupported) {
|
||||
link(untypedPointersFeatures);
|
||||
}
|
||||
link(features13);
|
||||
if (memoryDecompressionSupported) {
|
||||
link(memoryDecompressionFeatures);
|
||||
}
|
||||
if (descriptorHeapSupported) {
|
||||
link(desciptorHeapFeatures);
|
||||
}
|
||||
link(maintenance5Features);
|
||||
link(bit16);
|
||||
link(features12);
|
||||
if (rayTracingSupported) {
|
||||
link(physicalDeviceRayQueryFeatures);
|
||||
link(physicalDeviceRayTracingPipelineFeatures);
|
||||
link(deviceAccelerationStructureFeature);
|
||||
}
|
||||
|
||||
VkPhysicalDeviceFeatures2 physical_features2 = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
|
||||
.pNext = &deviceAccelerationStructureFeature,
|
||||
.pNext = featureChain,
|
||||
.features = {
|
||||
// Order matches VkPhysicalDeviceFeatures declaration so the
|
||||
// designated-initializer-order warning stays quiet.
|
||||
|
|
@ -833,12 +997,23 @@ void Device::Initialize() {
|
|||
}
|
||||
};
|
||||
|
||||
// Build the enabled-extension list dynamically so we can append the
|
||||
// optional VK_EXT_memory_decompression entry only when the driver
|
||||
// advertises it.
|
||||
// The enabled-extension list is the required set plus each optional group
|
||||
// the driver advertises in full (decided above from the enumeration).
|
||||
std::vector<const char*> enabledDeviceExtensions(
|
||||
std::begin(deviceExtensionNames),
|
||||
std::end(deviceExtensionNames));
|
||||
std::begin(requiredDeviceExtensionNames),
|
||||
std::end(requiredDeviceExtensionNames));
|
||||
if (descriptorHeapSupported) {
|
||||
enabledDeviceExtensions.append_range(descriptorHeapExtensionNames);
|
||||
}
|
||||
if (rayTracingSupported) {
|
||||
enabledDeviceExtensions.append_range(rayTracingExtensionNames);
|
||||
}
|
||||
if (deviceFaultSupported) {
|
||||
enabledDeviceExtensions.push_back(VK_EXT_DEVICE_FAULT_EXTENSION_NAME);
|
||||
}
|
||||
if (atomicFloatSupported) {
|
||||
enabledDeviceExtensions.push_back(VK_EXT_SHADER_ATOMIC_FLOAT_EXTENSION_NAME);
|
||||
}
|
||||
if (memoryDecompressionSupported) {
|
||||
enabledDeviceExtensions.push_back(VK_EXT_MEMORY_DECOMPRESSION_EXTENSION_NAME);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue