Skip to content

Device.kt

src/jvmMain/kotlin/afield/graphics/vulkan/device/Device.kt · 987 lines

package afield.graphics.vulkan.device

The logical Vulkan device: Device wraps a PhysicalDevice and enables the extensions and features the renderer uses.

It also reports optional capabilities such as ray tracing limits and Metal object export, and offers Device.calibratedHostGpuTimeNS for lining GPU timestamps up with host time.

import afield.graphics.vulkan.util.OS
import afield.graphics.vulkan.util.OSType
import afield.graphics.vulkan.util.Options
import afield.graphics.vulkan.util.vkCheck
import afield.app.diagnostics.logging.lprint
import afield.app.diagnostics.logging.lprintln
import org.lwjgl.system.MemoryStack
import org.lwjgl.system.MemoryUtil.memPutAddress
import org.lwjgl.vulkan.*
import org.lwjgl.vulkan.EXTCalibratedTimestamps.VK_TIME_DOMAIN_CLOCK_MONOTONIC_EXT
import org.lwjgl.vulkan.EXTCalibratedTimestamps.VK_TIME_DOMAIN_DEVICE_EXT
import org.lwjgl.vulkan.VK10.*
import org.lwjgl.vulkan.VK11
import org.lwjgl.vulkan.VK11.vkGetPhysicalDeviceProperties2

class Device(val physicalDevice: PhysicalDevice, private val instance: Instance)

class Device(val physicalDevice: PhysicalDevice, private val instance: Instance) {
    private val ngxDelegate = lazy { afield.media.reconstruction.NgxRuntime.open(this, instance) }

internal val ngxRuntime by ngxDelegate

data class RayTracingLimits

The device's ray-tracing and acceleration-structure limits, read off it once when the acceleration path is usable. Shader-binding-table layout needs the three handle sizes and alignments; geometry building needs the scratch alignment and the geometry, instance and primitive ceilings.

    data class RayTracingLimits(
        val shaderGroupHandleSize: Int,
        val shaderGroupHandleAlignment: Int,
        val shaderGroupBaseAlignment: Int,
        val maxRayRecursionDepth: Int,
        val maxRayDispatchInvocationCount: Int,
        val maxRayHitAttributeSize: Int,
        val minAccelerationStructureScratchOffsetAlignment: Int,
        val maxGeometryCount: Long,
        val maxInstanceCount: Long,
        val maxPrimitiveCount: Long,
        val maxPerStageDescriptorAccelerationStructures: Int,
        val maxDescriptorSetAccelerationStructures: Int
    )

val openDlssNrFeatureFailure: String?

Why DLSS ray reconstruction could not be prepared on this device, or null when it was. The extensions it asked for are already in enabledExtensions by then, so this is the message to show rather than a reason to re-probe.

val vkDevice: VkDevice

The raw VkDevice handle, which every LWJGL call in the engine takes as its first argument.

val supportsPushDescriptors: Boolean

VK_KHR_push_descriptor is available. A descriptor set layout built for push descriptors requires this, and refuses by name without it rather than failing inside Vulkan.

val pipelineStatisticsQueryEnabled: Boolean

The query features GPU profiling can use, enabled whenever the device has them so that Options.profiling can be switched on a running renderer. Performance counters cost something to enable, so they are only there when profiling was on at startup.

val performanceQueryEnabled: Boolean

VK_KHR_performance_query is available and profiling was on at startup. Unlike pipelineStatisticsQueryEnabled this cannot be switched later, because the counters cost something to enable. The renderer reads it together with a Windows-only test.

val pushDescriptorMax: Int

The device's maxPushDescriptors, or 0 when push descriptors are unavailable. A layout asking for more than this is refused with both numbers named.

val supportsExtendedDynamicState3: Boolean

VK_EXT_extended_dynamic_state3 is available and offers dynamic depth-clamp. Field sets only that one EDS3 state, so that one feature decides support; polygon mode is asked for separately where offered, since KosmicKrisp has EDS3 without it and requesting it anyway fails device creation.

val supportsExtendedDynamicState: Boolean

VK_EXT_extended_dynamic_state is available. Unlike EDS3 this needs no feature beyond the extension.

val supportsMaintenance7: Boolean

VK_KHR_maintenance7 is available and its feature enabled. Secondary graphics command buffers are used only where this and the option asking for them both hold.

val supportsMaintenance11: Boolean

VK_KHR_maintenance11 is available and its feature enabled. It is what lets a single array layer of an image be aliased as its own descriptor.

val supportsDynamicRenderingUnusedAttachments: Boolean

VK_EXT_dynamic_rendering_unused_attachments is available and its feature enabled, which is also the condition under which Field asks for it. Nothing in the engine branches on it: it relaxes which attachment formats a pipeline may declare rather than adding a call to make.

val supportsUnifiedImageLayouts: Boolean

The device offers unifiedImageLayouts; usesUnifiedImageLayouts says whether Field turned it on.

val usesUnifiedImageLayouts: Boolean

VK_KHR_unified_image_layouts is enabled and ImageLayouts sends GENERAL.

val supportsMultiview: Boolean

VK_KHR_multiview is available, which is how one draw reaches both eyes. A render-to-texture given a multiview view mask requires it, as does the multiview side-by-side stereo target; both refuse by name without it.

val raySupport: RayDeviceSupport

What the hardware offers for ray tracing, before any of Field's own opt-in. The supports… flags beside this are this probe and the ray-tracing option together.

val enabledExtensions: Set<String>

Exactly the extensions vkCreateDevice was given, deduplicated. This is what Field asked for; PhysicalDevice.hasDeviceExtension is what the device offers.

val supportsAccelerationStructures: Boolean

Acceleration structures can be built: ray tracing is opted into and the hardware path is complete.

val supportsRayTracing: Boolean

Ray-tracing pipelines — raygen, hit and miss stages — are available and opted into.

val supportsRayQuery: Boolean

Ray queries from an ordinary compute or fragment shader are available and opted into.

val rayTracingLimits: RayTracingLimits?

The device's ray-tracing limits, or null when the acceleration path is unusable.

val supportsMultiviewMeshShader: Boolean

Mesh-stage multiview is a separate feature from ordinary multiview.

data class MeshShaderLimits

    data class MeshShaderLimits(
        val outputVertices: Int,
        val outputPrimitives: Int,
        val taskPayloadBytes: Int,
        val multiviewViews: Int,
        val meshInvocations: Int,
        val taskInvocations: Int,
    )

val meshShaderLimits: MeshShaderLimits?

val supportsOpticalFlow: Boolean

Optional shader/vision features. SDKs are not required to enable these Vulkan features.

val supportsCooperativeVector: Boolean

In-shader cooperative vectors, which learned shaders infer with. It takes three features together — cooperative vectors, replicated composites and the Vulkan memory model — and can be turned off with the system property afield.cooperativeVector.disabled.

val supportsCooperativeVectorTraining: Boolean

Cooperative-vector training as well as inference. Offline shader training stays available without it.

val supportsClusterAccelerationStructure: Boolean

VK_NV_cluster_acceleration_structure, which needs acceleration structures underneath it. Switched off by the system property afield.clusterAccelerationStructure.disabled.

var usePresentIndexing = !Options.isMac

Presents are tagged with an id and waited on, which is Windows-only and needs both VK_KHR_present_id and VK_KHR_present_wait. Construction answers this for the real device, so the value here is only what holds until then.

var enableMeshShading = !Options.isMac

Mesh and task shader stages are enabled, which is Windows-only and needs VK_EXT_mesh_shader. Construction overwrites the value here the same way usePresentIndexing does.

var enableRayTracing = !Options.isMac

Whether ray-tracing pipelines were enabled at device creation. supportsRayTracing is the same answer as a read-only property and is what the engine reads; construction overwrites the value here as it does for usePresentIndexing.

var usesPresentTiming = false

True when VK_EXT_present_timing was successfully enabled on the device. Never true on macOS/MoltenVK.

Cache the device's supported extensions once.

    private val supportedExtensions: Set<String> by lazy { enumerateDeviceExtensions() }

val supportsMetalObjects: Boolean

VK_EXT_metal_objects (MoltenVK) — required to export an RTT color image's backing IOSurface.

    val supportsMetalObjects: Boolean
        get() = supportedExtensions.contains(org.lwjgl.vulkan.EXTMetalObjects.VK_EXT_METAL_OBJECTS_EXTENSION_NAME)

val supportsExternalMemoryHost: Boolean

VK_EXT_external_memory_host, enabled on macOS: an IOSurface's own pages imported as a VkBuffer (afield.graphics.texture.IOSurfaceHostBuffer). That is how IOSurfaces reach Vulkan on KosmicKrisp, which imports image memory only as an MTLHeap — never an IOSurface-backed MTLTexture — and has no VK_EXT_metal_objects.

    val supportsExternalMemoryHost: Boolean
        get() = OS == OSType.MACOS &&
            supportedExtensions.contains(EXTExternalMemoryHost.VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME)

val minImportedHostPointerAlignment: Long by lazy

The alignment an imported host pointer and its size must have (16 KiB on Apple silicon).

    val minImportedHostPointerAlignment: Long by lazy {
        if (!supportsExternalMemoryHost) 0L else MemoryStack.stackPush().use { stack ->
            val host = VkPhysicalDeviceExternalMemoryHostPropertiesEXT.calloc(stack).`sType$Default`()
            VK11.vkGetPhysicalDeviceProperties2(
                physicalDevice.vkPhysicalDevice,
                VkPhysicalDeviceProperties2.calloc(stack).`sType$Default`().pNext(host.address())
            )
            host.minImportedHostPointerAlignment()
        }
    }

fun requireMetalObjects(what: String)

Refuse, naming the way out, when supportsMetalObjects is false — which is the case on KosmicKrisp, the driver Field picks on Apple silicon with macOS 26 and a recent SDK. Field's two live IOSurface paths go through supportsExternalMemoryHost and work there; image-level IOSurface import and export is what needs this extension.

    fun requireMetalObjects(what: String) = check(supportsMetalObjects) {
        "$what needs VK_EXT_metal_objects, which the ${physicalDevice.driverName.ifEmpty { "current" }} Vulkan driver " +
            "does not have. Run Field on MoltenVK for it: AFIELD_VULKAN_DRIVER=moltenvk ./field.sh"
    }

val supportsExternalMemoryFd: Boolean

VK_KHR_external_memory_fd (Linux) — required to export an RTT color image's backing memory as an opaque fd for CUDA/NVENC import (see VulkanExternalMemory.kt / field-encode-linux).

    val supportsExternalMemoryFd: Boolean
        get() = supportedExtensions.contains(org.lwjgl.vulkan.KHRExternalMemoryFd.VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME)

val supportsExternalSemaphoreFd: Boolean

Linux CUDA interop synchronization, negotiated independently of external memory.

    val supportsExternalSemaphoreFd: Boolean
        get() = supportedExtensions.contains(org.lwjgl.vulkan.KHRExternalSemaphoreFd.VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME)

val supportsDmaBufImport: Boolean

Extensions needed to import Electron nativePixmap dma-bufs with explicit DRM modifiers.

    val supportsDmaBufImport: Boolean
        get() = supportsExternalMemoryFd &&
            supportedExtensions.contains(EXTExternalMemoryDmaBuf.VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME) &&
            supportedExtensions.contains(EXTImageDrmFormatModifier.VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME) &&
            supportedExtensions.contains(EXTQueueFamilyForeign.VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME)

val supportsExternalMemoryWin32: Boolean

VK_KHR_external_memory_win32, used for duplicated Electron D3D11 NT handles.

    val supportsExternalMemoryWin32: Boolean
        get() = supportedExtensions.contains(KHRExternalMemoryWin32.VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME)

init

body — 586 lines, from line 257
    init {
        MemoryStack.stackPush().use { stack ->
            val hasExt = { name: String -> supportedExtensions.contains(name) }
            val ascii = { name: String -> stack.ASCII(name) }

            val usePortability =
                hasExt(KHRPortabilitySubset.VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME) && OS == OSType.MACOS
            val useExternalMemory = OS == OSType.WINDOWS
            usePresentIndexing = OS == OSType.WINDOWS &&
                hasExt(KHRPresentId.VK_KHR_PRESENT_ID_EXTENSION_NAME) &&
                hasExt(KHRPresentWait.VK_KHR_PRESENT_WAIT_EXTENSION_NAME)
            enableMeshShading =
                OS != OSType.MACOS && hasExt(EXTMeshShader.VK_EXT_MESH_SHADER_EXTENSION_NAME)

            // VK_EXT_present_timing has a strict dependency bundle. Enabling the extension
            // without every member is invalid even if presentTiming itself is supported.
            val hasPresentTimingBundle = OS != OSType.MACOS &&
                hasExt(EXTPresentTiming.VK_EXT_PRESENT_TIMING_EXTENSION_NAME) &&
                hasExt(KHRPresentId2.VK_KHR_PRESENT_ID_2_EXTENSION_NAME) &&
                hasExt(KHRCalibratedTimestamps.VK_KHR_CALIBRATED_TIMESTAMPS_EXTENSION_NAME) &&
                KHRGetSurfaceCapabilities2.VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME in instance.enabledExtensions
            usesPresentTiming = if (hasPresentTimingBundle) {
                val ptFeatures = VkPhysicalDevicePresentTimingFeaturesEXT.calloc(stack)
                    .`sType$Default`()
                val presentId2Features = VkPhysicalDevicePresentId2FeaturesKHR.calloc(stack)
                    .`sType$Default`()
                ptFeatures.pNext(presentId2Features.address())
                val probe = VkPhysicalDeviceFeatures2.calloc(stack).apply {
                    sType(VK12.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2)
                    pNext(ptFeatures.address())
                }
                VK11.vkGetPhysicalDeviceFeatures2(physicalDevice.vkPhysicalDevice, probe)
                ptFeatures.presentTiming() && presentId2Features.presentId2()
            } else false

            val profilingEnabled = Options.profiling
            val hasPerfQuery = hasExt(KHRPerformanceQuery.VK_KHR_PERFORMANCE_QUERY_EXTENSION_NAME)
            val hasHostQueryReset = hasExt(EXTHostQueryReset.VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME)
            val hasCalibrated = hasExt(EXTCalibratedTimestamps.VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME)
            val hasPushDescriptor = hasExt(KHRPushDescriptor.VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME)
            supportsPushDescriptors = hasPushDescriptor
            val supportedCoreFeatures = VkPhysicalDeviceFeatures2.calloc(stack).apply {
                sType(VK12.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2)
            }
            VK11.vkGetPhysicalDeviceFeatures2(physicalDevice.vkPhysicalDevice, supportedCoreFeatures)
            val enablePipelineStatisticsQuery = supportedCoreFeatures.features().pipelineStatisticsQuery()
            pipelineStatisticsQueryEnabled = enablePipelineStatisticsQuery
            performanceQueryEnabled = profilingEnabled && hasPerfQuery
            if (profilingEnabled && !enablePipelineStatisticsQuery) {
                lprint(
                    "info,graphics",
                    "profiling enabled without pipelineStatisticsQuery; using timestamp timing only"
                )
            }
            val hasExtDynamicState =
                hasExt(org.lwjgl.vulkan.EXTExtendedDynamicState.VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME)
            val hasExtDynamicState3 =
                hasExt(org.lwjgl.vulkan.EXTExtendedDynamicState3.VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME)
            val hasMaintenance7 = hasExt(KHRMaintenance7.VK_KHR_MAINTENANCE_7_EXTENSION_NAME)
            supportsMaintenance7 = if (hasMaintenance7) {
                val m7Features = VkPhysicalDeviceMaintenance7FeaturesKHR.calloc(stack)
                    .sType(KHRMaintenance7.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_7_FEATURES_KHR)
                val m7FeatureProbe = VkPhysicalDeviceFeatures2.calloc(stack).apply {
                    sType(VK12.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2)
                    pNext(m7Features.address())
                }
                VK11.vkGetPhysicalDeviceFeatures2(physicalDevice.vkPhysicalDevice, m7FeatureProbe)
                m7Features.maintenance7()
            } else false
            val hasMaintenance11 = hasExt(KHRMaintenance11.VK_KHR_MAINTENANCE_11_EXTENSION_NAME)
            supportsMaintenance11 = if (hasMaintenance11) {
                val m11Features = VkPhysicalDeviceMaintenance11FeaturesKHR.calloc(stack)
                    .sType(KHRMaintenance11.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_11_FEATURES_KHR)
                val m11FeatureProbe = VkPhysicalDeviceFeatures2.calloc(stack).apply {
                    sType(VK12.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2)
                    pNext(m11Features.address())
                }
                VK11.vkGetPhysicalDeviceFeatures2(physicalDevice.vkPhysicalDevice, m11FeatureProbe)
                m11Features.maintenance11()
            } else false
            val hasDynamicRenderingUnusedAttachments =
                hasExt(EXTDynamicRenderingUnusedAttachments.VK_EXT_DYNAMIC_RENDERING_UNUSED_ATTACHMENTS_EXTENSION_NAME)
            supportsDynamicRenderingUnusedAttachments = if (hasDynamicRenderingUnusedAttachments) {
                val unusedAttachmentFeatures = VkPhysicalDeviceDynamicRenderingUnusedAttachmentsFeaturesEXT.calloc(stack)
                    .sType(EXTDynamicRenderingUnusedAttachments.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_UNUSED_ATTACHMENTS_FEATURES_EXT)
                val unusedAttachmentFeatureProbe = VkPhysicalDeviceFeatures2.calloc(stack).apply {
                    sType(VK12.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2)
                    pNext(unusedAttachmentFeatures.address())
                }
                VK11.vkGetPhysicalDeviceFeatures2(physicalDevice.vkPhysicalDevice, unusedAttachmentFeatureProbe)
                unusedAttachmentFeatures.dynamicRenderingUnusedAttachments()
            } else false
            val unifiedLayoutFeatures = VkPhysicalDeviceUnifiedImageLayoutsFeaturesKHR.calloc(stack).`sType$Default`()
            if (hasExt(KHRUnifiedImageLayouts.VK_KHR_UNIFIED_IMAGE_LAYOUTS_EXTENSION_NAME)) probeFeatures(stack, unifiedLayoutFeatures.address())
            supportsUnifiedImageLayouts = unifiedLayoutFeatures.unifiedImageLayouts()
            usesUnifiedImageLayouts = supportsUnifiedImageLayouts && Options.unifiedImageLayouts && !Options.enableVR
            // Field sets only the depth-clamp dynamic state, so that is the EDS3 feature that decides
            // support. polygonMode is requested only where offered: KosmicKrisp has EDS3 without it
            // (and without fillModeNonSolid), and asking for it anyway fails vkCreateDevice.
            val eds3Supported = VkPhysicalDeviceExtendedDynamicState3FeaturesEXT.calloc(stack).`sType$Default`()
            if (hasExtDynamicState3) probeFeatures(stack, eds3Supported.address())
            supportsExtendedDynamicState = hasExtDynamicState
            supportsExtendedDynamicState3 = hasExtDynamicState3 && eds3Supported.extendedDynamicState3DepthClampEnable()
            val eds3PolygonMode = eds3Supported.extendedDynamicState3PolygonMode()
            val multiviewSupported = VkPhysicalDeviceMultiviewFeatures.calloc(stack).`sType$Default`()
            probeFeatures(stack, multiviewSupported.address())
            supportsMultiview = multiviewSupported.multiview()
            val meshSupported = VkPhysicalDeviceMeshShaderFeaturesEXT.calloc(stack).`sType$Default`()
            if (enableMeshShading) {
                VK11.vkGetPhysicalDeviceFeatures2(physicalDevice.vkPhysicalDevice,
                    VkPhysicalDeviceFeatures2.calloc(stack).`sType$Default`().pNext(meshSupported.address()))
                enableMeshShading = meshSupported.meshShader() && meshSupported.taskShader()
            }
            supportsMultiviewMeshShader = enableMeshShading && supportsMultiview && meshSupported.multiviewMeshShader()
            meshShaderLimits = if (enableMeshShading) {
                val limits = VkPhysicalDeviceMeshShaderPropertiesEXT.calloc(stack).`sType$Default`()
                vkGetPhysicalDeviceProperties2(physicalDevice.vkPhysicalDevice,
                    VkPhysicalDeviceProperties2.calloc(stack).`sType$Default`().pNext(limits.address()))
                MeshShaderLimits(limits.maxMeshOutputVertices(), limits.maxMeshOutputPrimitives(),
                    limits.maxTaskPayloadSize(), limits.maxMeshMultiviewViewCount(),
                    limits.maxMeshWorkGroupInvocations(), limits.maxTaskWorkGroupInvocations())
            } else null
            raySupport = RayDeviceSupport.probe(physicalDevice.vkPhysicalDevice, supportedExtensions)
            supportsAccelerationStructures = Options.enableRayTracing && raySupport.accelerationUsable
            enableRayTracing = Options.enableRayTracing && raySupport.pipelineUsable
            supportsRayTracing = enableRayTracing
            supportsRayQuery = Options.enableRayTracing && raySupport.queryUsable
            rayTracingLimits = if (raySupport.accelerationUsable) queryRayTracingLimits(stack) else null
            val clusterFeatures=VkPhysicalDeviceClusterAccelerationStructureFeaturesNV.calloc(stack).`sType$Default`()
            val opticalFeatures=VkPhysicalDeviceOpticalFlowFeaturesNV.calloc(stack).`sType$Default`()
            val vectorFeatures=VkPhysicalDeviceCooperativeVectorFeaturesNV.calloc(stack).`sType$Default`()
            val replicatedFeatures=VkPhysicalDeviceShaderReplicatedCompositesFeaturesEXT.calloc(stack).`sType$Default`()
            val memoryModelFeatures=VkPhysicalDeviceVulkanMemoryModelFeatures.calloc(stack).`sType$Default`()
            fun probeExtra(address: Long) = VK11.vkGetPhysicalDeviceFeatures2(physicalDevice.vkPhysicalDevice,
                VkPhysicalDeviceFeatures2.calloc(stack).`sType$Default`().pNext(address))
            if(hasExt(NVOpticalFlow.VK_NV_OPTICAL_FLOW_EXTENSION_NAME))probeExtra(opticalFeatures.address())
            if(hasExt(NVCooperativeVector.VK_NV_COOPERATIVE_VECTOR_EXTENSION_NAME))probeExtra(vectorFeatures.address())
            if(hasExt(EXTShaderReplicatedComposites.VK_EXT_SHADER_REPLICATED_COMPOSITES_EXTENSION_NAME))probeExtra(replicatedFeatures.address())
            probeExtra(memoryModelFeatures.address())
            val supportedMemoryModelDeviceScope = memoryModelFeatures.vulkanMemoryModelDeviceScope()
            if(hasExt(NVClusterAccelerationStructure.VK_NV_CLUSTER_ACCELERATION_STRUCTURE_EXTENSION_NAME))probeExtra(clusterFeatures.address())
            supportsClusterAccelerationStructure=supportsAccelerationStructures && clusterFeatures.clusterAccelerationStructure() &&
                !java.lang.Boolean.getBoolean("afield.clusterAccelerationStructure.disabled")
            supportsOpticalFlow=opticalFeatures.opticalFlow() && !java.lang.Boolean.getBoolean("afield.opticalFlow.disabled")
            supportsCooperativeVector=vectorFeatures.cooperativeVector() && replicatedFeatures.shaderReplicatedComposites() &&
                memoryModelFeatures.vulkanMemoryModel() && !java.lang.Boolean.getBoolean("afield.cooperativeVector.disabled")
            supportsCooperativeVectorTraining=supportsCooperativeVector && vectorFeatures.cooperativeVectorTraining() &&
                !java.lang.Boolean.getBoolean("afield.cooperativeVectorTraining.disabled")

            // SteamVR's Linux XR_KHR_vulkan_enable2 implementation appends a
            // VkPhysicalDeviceTimelineSemaphoreFeatures while creating the device. Vulkan forbids
            // that promoted struct alongside VkPhysicalDeviceVulkan12Features, so the Linux XR
            // path enables the other promoted features individually below.
            val useSplitVulkan12Features =
                OS == OSType.LINUX && Options.enableVR && Instance.hasOpenXRBootstrap()

            // Build extension list defensively.
            val extNames = ArrayList<String>(12).apply {
                add(KHRSwapchain.VK_KHR_SWAPCHAIN_EXTENSION_NAME) // not optional
                if(supportsOpticalFlow)add(NVOpticalFlow.VK_NV_OPTICAL_FLOW_EXTENSION_NAME)
                if(supportsCooperativeVector){add(NVCooperativeVector.VK_NV_COOPERATIVE_VECTOR_EXTENSION_NAME);add(EXTShaderReplicatedComposites.VK_EXT_SHADER_REPLICATED_COMPOSITES_EXTENSION_NAME)}
                addIfSupported(KHRDynamicRendering.VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME)
                addIfSupported(KHRDepthStencilResolve.VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME)
                addIfSupported(KHRCreateRenderpass2.VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME)
                addIfSupported(KHRSeparateDepthStencilLayouts.VK_KHR_SEPARATE_DEPTH_STENCIL_LAYOUTS_EXTENSION_NAME)
                addIfSupported(KHRPushDescriptor.VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME)
                if (supportsMaintenance7) add(KHRMaintenance7.VK_KHR_MAINTENANCE_7_EXTENSION_NAME)
                if (supportsMaintenance11) add(KHRMaintenance11.VK_KHR_MAINTENANCE_11_EXTENSION_NAME)
                if (supportsDynamicRenderingUnusedAttachments) {
                    add(EXTDynamicRenderingUnusedAttachments.VK_EXT_DYNAMIC_RENDERING_UNUSED_ATTACHMENTS_EXTENSION_NAME)
                }
                if (usesUnifiedImageLayouts) add(KHRUnifiedImageLayouts.VK_KHR_UNIFIED_IMAGE_LAYOUTS_EXTENSION_NAME)

                addIfSupported(KHR16bitStorage.VK_KHR_16BIT_STORAGE_EXTENSION_NAME)

                // shaderOutputLayer/shaderOutputViewportIndex normally come from
                // VkPhysicalDeviceVulkan12Features. The split Linux XR feature chain cannot carry
                // that aggregate struct, so expose the equivalent pre-promotion capability.
                if (useSplitVulkan12Features) {
                    check(hasExt(EXTShaderViewportIndexLayer.VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME)) {
                        "Linux OpenXR requires ${EXTShaderViewportIndexLayer.VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME}"
                    }
                    add(EXTShaderViewportIndexLayer.VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME)
                }

                if (enableMeshShading) {
                    add(EXTMeshShader.VK_EXT_MESH_SHADER_EXTENSION_NAME)
                }

                if (supportsAccelerationStructures) {
                    add(KHRDeferredHostOperations.VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME)
                    add(KHRAccelerationStructure.VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME)
                    if(supportsClusterAccelerationStructure)add(NVClusterAccelerationStructure.VK_NV_CLUSTER_ACCELERATION_STRUCTURE_EXTENSION_NAME)
                    if (supportsRayTracing) add(KHRRayTracingPipeline.VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME)
                    addIfSupported(KHRSpirv14.VK_KHR_SPIRV_1_4_EXTENSION_NAME)
                    addIfSupported(KHRShaderFloatControls.VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME)
                    if (supportsRayQuery) {
                        add(KHRRayQuery.VK_KHR_RAY_QUERY_EXTENSION_NAME)
                    }
                }

                if (OS == OSType.WINDOWS) {
                    addIfSupported(KHRExternalMemory.VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME)
                    addIfSupported(KHRExternalMemoryWin32.VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME)
                    addIfSupported(KHRPresentId.VK_KHR_PRESENT_ID_EXTENSION_NAME)
                    addIfSupported(KHRPresentWait.VK_KHR_PRESENT_WAIT_EXTENSION_NAME)
                }

                if (OS == OSType.MACOS) {
                    addIfSupported(KHRPortabilitySubset.VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME)
                    // Lets an RTT color image export its backing IOSurface for GPU video streaming.
                    addIfSupported(org.lwjgl.vulkan.EXTMetalObjects.VK_EXT_METAL_OBJECTS_EXTENSION_NAME)
                    // IOSurface pages as a VkBuffer: how streaming and browser surfaces reach
                    // KosmicKrisp, which has no metal_objects (see supportsExternalMemoryHost).
                    addIfSupported(EXTExternalMemoryHost.VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME)
                }

                if (OS == OSType.LINUX) {
                    // Lets an RTT color image export its backing memory as an opaque fd, for
                    // zero-copy CUDA/NVENC video streaming (field-encode-linux). VK_KHR_external_memory
                    // is the base capability VK_KHR_external_memory_fd builds on.
                    addIfSupported(KHRExternalMemory.VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME)
                    addIfSupported(org.lwjgl.vulkan.KHRExternalMemoryFd.VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME)
                    addIfSupported(org.lwjgl.vulkan.KHRExternalSemaphoreFd.VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME)
                    addIfSupported(EXTExternalMemoryDmaBuf.VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME)
                    addIfSupported(EXTImageDrmFormatModifier.VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME)
                    addIfSupported(EXTQueueFamilyForeign.VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME)
                }

                // VK_EXT_present_timing: scanout-jitter logging via actual display timestamps.
                // Explicitly excluded from macOS/MoltenVK where the extension is absent/broken.
                if (usesPresentTiming) {
                    add(EXTPresentTiming.VK_EXT_PRESENT_TIMING_EXTENSION_NAME)
                    add(KHRPresentId2.VK_KHR_PRESENT_ID_2_EXTENSION_NAME)
                    add(KHRCalibratedTimestamps.VK_KHR_CALIBRATED_TIMESTAMPS_EXTENSION_NAME)
                }

                // Host query reset whatever Options.profiling says now: profiling can be switched on later.
                addIfSupported(EXTHostQueryReset.VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME)
                if (Options.profiling) {
                    addIfSupported(KHRPerformanceQuery.VK_KHR_PERFORMANCE_QUERY_EXTENSION_NAME)
                    addIfSupported(EXTCalibratedTimestamps.VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME)
                }

                addIfSupported(KHRShaderNonSemanticInfo.VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME)
                addIfSupported(org.lwjgl.vulkan.EXTExtendedDynamicState.VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME)
                if (supportsExtendedDynamicState3) add(org.lwjgl.vulkan.EXTExtendedDynamicState3.VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME)

                if (Options.enableVR) {
                    val d = Instance.getOpenXRRequiredDeviceExts(physicalDevice.vkPhysicalDevice.address())
                    lprint("info,openxr", "required device extensions are $d")
                    addAll(d)
                }
                // this is useful outside of the context of VR as well
                addIfSupported(KHRMultiview.VK_KHR_MULTIVIEW_EXTENSION_NAME)

                // VK_EXT_hdr_metadata lets us advertise mastering-display primaries to the OS/compositor
                // for HDR10 swap chains.  Opt-in only; safe to skip when unsupported.
                if (Options.enableHDR) {
                    addIfSupported(EXTHdrMetadata.VK_EXT_HDR_METADATA_EXTENSION_NAME)
                }

            }
            val nrPlanResult=afield.graphics.render.neural.OpenDlssNrSupport.prepare(instance.vkInstance.address(),physicalDevice.vkPhysicalDevice.address())
            val nrPlan=nrPlanResult.getOrNull()
            openDlssNrFeatureFailure=nrPlanResult.exceptionOrNull()?.message
            nrPlan?.let { extNames.addAll(it.native.required()) }
            extNames.addAll(afield.media.reconstruction.NgxSupport.deviceExtensions(instance, physicalDevice.vkPhysicalDevice.address(), supportedExtensions))
            val dedupedExtNames = extNames.distinct()
            enabledExtensions = dedupedExtNames.toSet()

            val requiredExtensions = stack.mallocPointer(dedupedExtNames.size).apply {
                dedupedExtNames.forEach { put(ascii(it)) }
                flip()
            }

            // --- Core features via VkPhysicalDeviceFeatures2 + Vulkan12/13
            val features = VkPhysicalDeviceFeatures2.calloc(stack).apply {
                sType(VK12.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2)
                // Requested where offered, and named when not, so a driver missing one reports it here
                // rather than failing vkCreateDevice with VK_ERROR_FEATURE_NOT_PRESENT. KosmicKrisp
                // before SDK 1.4.357 lacked vertexPipelineStoresAndAtomics.
                val core = supportedCoreFeatures.features()
                val missing = listOfNotNull(
                    "fragmentStoresAndAtomics".takeUnless { core.fragmentStoresAndAtomics() },
                    "vertexPipelineStoresAndAtomics".takeUnless { core.vertexPipelineStoresAndAtomics() },
                    "shaderInt64".takeUnless { core.shaderInt64() },
                )
                if (missing.isNotEmpty()) {
                    lprintln("warning,vulkan", "${physicalDevice.driverName} lacks $missing; shaders that need them will fail to build")
                }
                features().fragmentStoresAndAtomics(core.fragmentStoresAndAtomics())
                features().vertexPipelineStoresAndAtomics(core.vertexPipelineStoresAndAtomics())
                features().shaderInt64(core.shaderInt64())
                features().shaderInt16(supportedCoreFeatures.features().shaderInt16())
                // Guard: geometryShader is unsupported on Apple Silicon / MoltenVK.
                features().geometryShader(physicalDevice.vkPhysicalDeviceFeatures.geometryShader())
                features().robustBufferAccess(Options.robustness)
                features().pipelineStatisticsQuery(enablePipelineStatisticsQuery)
            }

            val features11 = VkPhysicalDeviceVulkan11Features.calloc(stack).apply {
                `sType$Default`()
                multiview(supportsMultiview)
                shaderDrawParameters(true)
                storagePushConstant16(true)
            }

            val features12 = VkPhysicalDeviceVulkan12Features.calloc(stack).apply {
                sType(VK12.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES)

                shaderOutputLayer(true)
                shaderOutputViewportIndex(true)

                shaderFloat16(true)

                bufferDeviceAddress(true)
                timelineSemaphore(true)
                // Core in 1.2; GPU profiling resets its query pools from the host (see timing.kt).
                hostQueryReset(true)
                vulkanMemoryModel(supportsCooperativeVector)
                // Ordinary atomic shaders also use Device scope when this memory model is active.
                vulkanMemoryModelDeviceScope(supportsCooperativeVector && supportedMemoryModelDeviceScope)

                // Descriptor indexing (promoted to 1.2) — bindless-friendly bits
                descriptorIndexing(true)
                runtimeDescriptorArray(true)
                descriptorBindingPartiallyBound(true)
                descriptorBindingVariableDescriptorCount(true)

                // Non-uniform indexing (use as needed)
                shaderSampledImageArrayNonUniformIndexing(true)
                shaderStorageImageArrayNonUniformIndexing(true)
                shaderUniformBufferArrayNonUniformIndexing(true)
                shaderStorageBufferArrayNonUniformIndexing(true)
                // If you use texel buffers with non-uniform indexing, enable these too:
                // shaderUniformTexelBufferArrayNonUniformIndexing(true)
                // shaderStorageTexelBufferArrayNonUniformIndexing(true)

                // Leave all *UpdateAfterBind* = false unless your pool/layouts use the corresponding flags.
                // descriptorBindingSampledImageUpdateAfterBind(true)
                // descriptorBindingStorageImageUpdateAfterBind(true)
                // descriptorBindingUniformBufferUpdateAfterBind(true)
                // descriptorBindingStorageBufferUpdateAfterBind(true)
                // descriptorBindingUniformTexelBufferUpdateAfterBind(true)
                // descriptorBindingStorageTexelBufferUpdateAfterBind(true)
                // descriptorBindingUpdateUnusedWhilePending(true)
            }

            val features13 = VkPhysicalDeviceVulkan13Features.calloc(stack).apply {
                sType(VK13.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES)
                dynamicRendering(true)
                synchronization2(true)
            }

            val eds3 = VkPhysicalDeviceExtendedDynamicState3FeaturesEXT.calloc(stack)
                .sType(org.lwjgl.vulkan.EXTExtendedDynamicState3.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_3_FEATURES_EXT)
                .extendedDynamicState3PolygonMode(eds3PolygonMode)
                .extendedDynamicState3DepthClampEnable(true)

            val eds = VkPhysicalDeviceExtendedDynamicStateFeaturesEXT.calloc(stack)
                .sType(org.lwjgl.vulkan.EXTExtendedDynamicState.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT)
                .extendedDynamicState(true)

            features.pNext(features11.address())
            var featureTail = features11.address()
            if (useSplitVulkan12Features) {
                val shaderFloat16 = VkPhysicalDeviceShaderFloat16Int8Features.calloc(stack)
                    .`sType$Default`()
                    .shaderFloat16(true)
                memPutAddress(featureTail + VkBaseOutStructure.PNEXT, shaderFloat16.address())
                featureTail = shaderFloat16.address()

                val bufferDeviceAddress = VkPhysicalDeviceBufferDeviceAddressFeatures.calloc(stack)
                    .`sType$Default`()
                    .bufferDeviceAddress(true)
                memPutAddress(featureTail + VkBaseOutStructure.PNEXT, bufferDeviceAddress.address())
                featureTail = bufferDeviceAddress.address()

                val descriptorIndexing = VkPhysicalDeviceDescriptorIndexingFeatures.calloc(stack)
                    .`sType$Default`()
                    .runtimeDescriptorArray(true)
                    .descriptorBindingPartiallyBound(true)
                    .descriptorBindingVariableDescriptorCount(true)
                    .shaderSampledImageArrayNonUniformIndexing(true)
                    .shaderStorageImageArrayNonUniformIndexing(true)
                    .shaderUniformBufferArrayNonUniformIndexing(true)
                    .shaderStorageBufferArrayNonUniformIndexing(true)
                memPutAddress(featureTail + VkBaseOutStructure.PNEXT, descriptorIndexing.address())
                featureTail = descriptorIndexing.address()
                if(supportsCooperativeVector) {
                    memoryModelFeatures.vulkanMemoryModelDeviceScope(supportedMemoryModelDeviceScope).vulkanMemoryModelAvailabilityVisibilityChains(false).pNext(0L)
                    memPutAddress(featureTail+VkBaseOutStructure.PNEXT,memoryModelFeatures.address());featureTail=memoryModelFeatures.address()
                }
            } else {
                memPutAddress(featureTail + VkBaseOutStructure.PNEXT, features12.address())
                featureTail = features12.address()
            }
            memPutAddress(featureTail + VkBaseOutStructure.PNEXT, features13.address())
            var tail = features13.address()
            if(supportsOpticalFlow) {
                opticalFeatures.pNext(0L);memPutAddress(tail+VkBaseOutStructure.PNEXT,opticalFeatures.address());tail=opticalFeatures.address()
            }
            if(supportsCooperativeVector) {
                vectorFeatures.cooperativeVectorTraining(supportsCooperativeVectorTraining).pNext(0L)
                memPutAddress(tail+VkBaseOutStructure.PNEXT,vectorFeatures.address());tail=vectorFeatures.address()
                replicatedFeatures.pNext(0L);memPutAddress(tail+VkBaseOutStructure.PNEXT,replicatedFeatures.address());tail=replicatedFeatures.address()
            }

            if(supportsClusterAccelerationStructure) {
                clusterFeatures.pNext(0L);memPutAddress(tail+VkBaseOutStructure.PNEXT,clusterFeatures.address());tail=clusterFeatures.address()
            }
            if (supportsExtendedDynamicState) {
                memPutAddress(tail + VkBaseOutStructure.PNEXT, eds.address()); tail = eds.address()
            }

            if (supportsExtendedDynamicState3) {
                memPutAddress(tail + VkBaseOutStructure.PNEXT, eds3.address()); tail = eds3.address()
            }

            if (supportsMaintenance7) {
                val m7 = VkPhysicalDeviceMaintenance7FeaturesKHR.calloc(stack)
                    .sType(KHRMaintenance7.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_7_FEATURES_KHR)
                    .maintenance7(true)
                memPutAddress(tail + VkBaseOutStructure.PNEXT, m7.address()); tail = m7.address()
            }

            if (supportsMaintenance11) {
                val m11 = VkPhysicalDeviceMaintenance11FeaturesKHR.calloc(stack)
                    .sType(KHRMaintenance11.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_11_FEATURES_KHR)
                    .maintenance11(true)
                memPutAddress(tail + VkBaseOutStructure.PNEXT, m11.address()); tail = m11.address()
            }

            if (supportsDynamicRenderingUnusedAttachments) {
                val unusedAttachments = VkPhysicalDeviceDynamicRenderingUnusedAttachmentsFeaturesEXT.calloc(stack)
                    .sType(EXTDynamicRenderingUnusedAttachments.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_UNUSED_ATTACHMENTS_FEATURES_EXT)
                    .dynamicRenderingUnusedAttachments(true)
                memPutAddress(tail + VkBaseOutStructure.PNEXT, unusedAttachments.address()); tail = unusedAttachments.address()
            }

            if (usesUnifiedImageLayouts) {
                val unified = VkPhysicalDeviceUnifiedImageLayoutsFeaturesKHR.calloc(stack).`sType$Default`()
                    .unifiedImageLayouts(true)
                memPutAddress(tail + VkBaseOutStructure.PNEXT, unified.address()); tail = unified.address()
            }

            // Mesh/task features are queried independently; Linux is a first-class mesh path.
            if (enableMeshShading) {
                val mesh = VkPhysicalDeviceMeshShaderFeaturesEXT.calloc(stack)
                    .sType(EXTMeshShader.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_EXT)
                    .meshShader(true).taskShader(true).multiviewMeshShader(supportsMultiviewMeshShader)
                memPutAddress(tail + VkBaseOutStructure.PNEXT, mesh.address()); tail = mesh.address()
            }

            if (usePresentIndexing) {
                val presentId = VkPhysicalDevicePresentIdFeaturesKHR.calloc(stack)
                    .sType(KHRPresentId.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR)
                    .presentId(true)

                val presentWait = VkPhysicalDevicePresentWaitFeaturesKHR.calloc(stack)
                    .sType(KHRPresentWait.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_WAIT_FEATURES_KHR)
                    .presentWait(true)

                memPutAddress(tail + VkBaseOutStructure.PNEXT, presentId.address()); tail = presentId.address()
                memPutAddress(tail + VkBaseOutStructure.PNEXT, presentWait.address()); tail = presentWait.address()
            }

            // VK_EXT_present_timing feature enable — never on macOS/MoltenVK.
            if (usesPresentTiming) {
                val presentTiming = VkPhysicalDevicePresentTimingFeaturesEXT.calloc(stack)
                    .`sType$Default`()
                    .presentTiming(true)
                memPutAddress(tail + VkBaseOutStructure.PNEXT, presentTiming.address()); tail = presentTiming.address()

                val presentId2 = VkPhysicalDevicePresentId2FeaturesKHR.calloc(stack)
                    .`sType$Default`()
                    .presentId2(true)
                memPutAddress(tail + VkBaseOutStructure.PNEXT, presentId2.address()); tail = presentId2.address()
            }

            if (supportsAccelerationStructures) {
                val accelerationStructure = VkPhysicalDeviceAccelerationStructureFeaturesKHR.calloc(stack)
                    .sType(KHRAccelerationStructure.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR)
                    .accelerationStructure(true)

                val rayTracingPipeline = VkPhysicalDeviceRayTracingPipelineFeaturesKHR.calloc(stack)
                    .sType(KHRRayTracingPipeline.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR)
                    .rayTracingPipeline(true)

                memPutAddress(tail + VkBaseOutStructure.PNEXT, accelerationStructure.address()); tail =
                    accelerationStructure.address()
                if (supportsRayTracing) {
                    memPutAddress(tail + VkBaseOutStructure.PNEXT, rayTracingPipeline.address())
                    tail = rayTracingPipeline.address()
                }

                if (supportsRayQuery) {
                    val rayQuery = VkPhysicalDeviceRayQueryFeaturesKHR.calloc(stack)
                        .sType(KHRRayQuery.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_QUERY_FEATURES_KHR)
                        .rayQuery(true)
                    memPutAddress(tail + VkBaseOutStructure.PNEXT, rayQuery.address()); tail = rayQuery.address()
                }
            }

            // Profiling features (conditionally).
            if (profilingEnabled && hasPerfQuery) {
                val perf = VkPhysicalDevicePerformanceQueryFeaturesKHR.calloc(stack)
                    .sType(KHRPerformanceQuery.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PERFORMANCE_QUERY_FEATURES_KHR)
                    .performanceCounterQueryPools(true)
                    .performanceCounterMultipleQueryPools(true)
                memPutAddress(tail + VkBaseOutStructure.PNEXT, perf.address()); tail = perf.address()
            }

            // Only the split chain needs the extension struct: alongside VkPhysicalDeviceVulkan12Features
            // it is invalid (VUID-VkDeviceCreateInfo-pNext-02830), and that struct carries the bit itself.
            if (hasHostQueryReset && useSplitVulkan12Features) {
                val hostReset = VkPhysicalDeviceHostQueryResetFeatures.calloc(stack)
                    .sType(EXTHostQueryReset.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT)
                    .hostQueryReset(true)
                memPutAddress(tail + VkBaseOutStructure.PNEXT, hostReset.address()); tail = hostReset.address()
            }

            nrPlan?.apply(features.address())

            // Enable all queue families.
            val qProps = physicalDevice.vkQueueFamilyProps
            val qInfos = VkDeviceQueueCreateInfo.calloc(qProps.capacity(), stack)
            for (i in 0 until qProps.capacity()) {
                val priorities = stack.callocFloat(qProps[i].queueCount())
                qInfos[i]
                    .sType(VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO)
                    .queueFamilyIndex(i)
                    .pQueuePriorities(priorities)
            }

            val ci = VkDeviceCreateInfo.calloc(stack)
                .sType(VK12.VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO)
                .ppEnabledExtensionNames(requiredExtensions)
                .pQueueCreateInfos(qInfos)
                .pNext(features.address())

            try {
                if (Options.enableVR && Instance.hasOpenXRBootstrap()) {
                    vkDevice = Instance.createVkDeviceViaXR(ci, instance.vkInstance, physicalDevice.vkPhysicalDevice)
                } else {
                    val pDev = stack.mallocPointer(1)
                    vkCheck(vkCreateDevice(physicalDevice.vkPhysicalDevice, ci, null, pDev), "Failed to create device")
                    vkDevice = VkDevice(pDev[0], physicalDevice.vkPhysicalDevice, ci)
                }
            } finally { nrPlan?.close() }

            // Query push-descriptor limits (0 if unsupported)
            pushDescriptorMax = if (hasPushDescriptor) {
                val props2 = VkPhysicalDeviceProperties2.calloc(stack)
                    .sType(VK11.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2)
                val pushProps = VkPhysicalDevicePushDescriptorPropertiesKHR.calloc(stack)
                    .sType(KHRPushDescriptor.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR)
                props2.pNext(pushProps.address())
                vkGetPhysicalDeviceProperties2(physicalDevice.vkPhysicalDevice, props2)
                pushProps.maxPushDescriptors()
            } else 0
            lprintln(
                "info,device",
                "Device push descriptors: supported=$supportsPushDescriptors, maxPushDescriptors=$pushDescriptorMax"
            )
            lprintln(
                "info,device",
                "Device ray tracing: pipeline=$supportsRayTracing, rayQuery=$supportsRayQuery, limits=$rayTracingLimits"
            )
            lprintln(
                "info,device",
                "Device maintenance: m7=$supportsMaintenance7, m11=$supportsMaintenance11, dynamicRenderingUnusedAttachments=$supportsDynamicRenderingUnusedAttachments"
            )
            lprintln(
                "info,device",
                "Device present timing: usesPresentTiming=$usesPresentTiming"
            )
            // Only now: an image created before the device existed could not have used it anyway.
            ImageLayouts.unified = usesUnifiedImageLayouts
            lprintln(
                "info,device",
                "Device unified image layouts: supported=$supportsUnifiedImageLayouts, used=$usesUnifiedImageLayouts"
            )
        }
    }

fun waitIdle()

Block until every queue on this device is idle. The blunt instrument teardown and swapchain rebuilds use; a per-queue or per-fence wait is what a frame should use.

    fun waitIdle() {
        vkDeviceWaitIdle(vkDevice)
    }

var acceptingSubmissions

False once the owning Renderer's teardown has begun (closeSubmissions); Queue.submit then refuses by name instead of queuing work against buffers that are about to be freed.

    @Volatile
    var acceptingSubmissions = true
        private set

fun closeSubmissions()

Refuse every later Queue.submit; taken under Queue.globalQueueLock so none is mid-flight.

    fun closeSubmissions() {
        synchronized(Queue.globalQueueLock) { acceptingSubmissions = false }
    }

fun cleanup()

Close the reconstruction runtime if one was ever opened, then destroy the device. Everything allocated from the device has to be gone first; closeSubmissions and waitIdle are how a renderer gets there.

    fun cleanup() {
        if (ngxDelegate.isInitialized()) ngxRuntime.getOrNull()?.close()
        vkDestroyDevice(vkDevice, null)
    }

Fill the feature struct at address (its sType set, pNext 0) with what the device supports.

source — 50 lines, from line 876
    private fun probeFeatures(stack: MemoryStack, address: Long) {
        VK11.vkGetPhysicalDeviceFeatures2(
            physicalDevice.vkPhysicalDevice,
            VkPhysicalDeviceFeatures2.calloc(stack).`sType$Default`().pNext(address)
        )
    }

    private fun MutableList<String>.addIfSupported(name: String) {
        if (name in supportedExtensions) add(name)
    }

    private fun enumerateDeviceExtensions(): Set<String> {
        val out = HashSet<String>()
        MemoryStack.stackPush().use { stack ->
            val countBuf = stack.callocInt(1)
            vkEnumerateDeviceExtensionProperties(physicalDevice.vkPhysicalDevice, null as String?, countBuf, null)
            val props = VkExtensionProperties.calloc(countBuf[0], stack)
            vkEnumerateDeviceExtensionProperties(physicalDevice.vkPhysicalDevice, null as String?, countBuf, props)
            repeat(props.capacity()) { i -> out += props[i].extensionNameString() }
        }
        return out
    }

    private fun queryRayTracingLimits(stack: MemoryStack): RayTracingLimits {
        val props2 = VkPhysicalDeviceProperties2.calloc(stack)
            .sType(VK11.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2)
        val rtProps = VkPhysicalDeviceRayTracingPipelinePropertiesKHR.calloc(stack)
            .sType(KHRRayTracingPipeline.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR)
        val asProps = VkPhysicalDeviceAccelerationStructurePropertiesKHR.calloc(stack)
            .sType(KHRAccelerationStructure.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_PROPERTIES_KHR)

        props2.pNext(asProps.address())
        if (raySupport.pipelineExtension) asProps.pNext(rtProps.address())
        VK11.vkGetPhysicalDeviceProperties2(physicalDevice.vkPhysicalDevice, props2)

        return RayTracingLimits(
            shaderGroupHandleSize = rtProps.shaderGroupHandleSize(),
            shaderGroupHandleAlignment = rtProps.shaderGroupHandleAlignment(),
            shaderGroupBaseAlignment = rtProps.shaderGroupBaseAlignment(),
            maxRayRecursionDepth = rtProps.maxRayRecursionDepth(),
            maxRayDispatchInvocationCount = rtProps.maxRayDispatchInvocationCount(),
            maxRayHitAttributeSize = rtProps.maxRayHitAttributeSize(),
            minAccelerationStructureScratchOffsetAlignment = asProps.minAccelerationStructureScratchOffsetAlignment(),
            maxGeometryCount = asProps.maxGeometryCount(),
            maxInstanceCount = asProps.maxInstanceCount(),
            maxPrimitiveCount = asProps.maxPrimitiveCount(),
            maxPerStageDescriptorAccelerationStructures = asProps.maxPerStageDescriptorAccelerationStructures(),
            maxDescriptorSetAccelerationStructures = asProps.maxDescriptorSetAccelerationStructures()
        )
    }

fun hasCalibrateableDomains(): Boolean

True if DEVICE and CLOCK_MONOTONIC time domains are calibrateable (guarded by extension presence).

    fun hasCalibrateableDomains(): Boolean {
        if (!supportedExtensions.contains(EXTCalibratedTimestamps.VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME)) {
            return false
        }
        MemoryStack.stackPush().use { stack ->
            val countBuf = stack.mallocInt(1)
            EXTCalibratedTimestamps.vkGetPhysicalDeviceCalibrateableTimeDomainsEXT(
                physicalDevice.vkPhysicalDevice, countBuf, null as java.nio.IntBuffer?
            )
            val count = countBuf[0]
            if (count == 0) return false

            val domains = stack.mallocInt(count)
            EXTCalibratedTimestamps.vkGetPhysicalDeviceCalibrateableTimeDomainsEXT(
                physicalDevice.vkPhysicalDevice, countBuf, domains
            )
            var hasDevice = false
            var hasMono = false
            for (i in 0 until count) {
                when (domains[i]) {
                    VK_TIME_DOMAIN_DEVICE_EXT -> hasDevice = true
                    VK_TIME_DOMAIN_CLOCK_MONOTONIC_EXT -> hasMono = true
                }
            }
            return hasDevice && hasMono
        }
    }

fun calibratedHostGpuTimeNS(): Pair<Long, Long>

Returns (hostMonotonicNS, gpuNS). Requires VK_EXT_calibrated_timestamps. Callers should gate on hasCalibrateableDomains.

    fun calibratedHostGpuTimeNS(): Pair<Long, Long> {
        require(supportedExtensions.contains(EXTCalibratedTimestamps.VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME)) {
            "VK_EXT_calibrated_timestamps not enabled"
        }
        MemoryStack.stackPush().use { stack ->
            val infos = VkCalibratedTimestampInfoEXT.calloc(2, stack)
            infos[0]
                .sType(EXTCalibratedTimestamps.VK_STRUCTURE_TYPE_CALIBRATED_TIMESTAMP_INFO_EXT)
                .timeDomain(VK_TIME_DOMAIN_DEVICE_EXT)
            infos[1]
                .sType(EXTCalibratedTimestamps.VK_STRUCTURE_TYPE_CALIBRATED_TIMESTAMP_INFO_EXT)
                .timeDomain(VK_TIME_DOMAIN_CLOCK_MONOTONIC_EXT)

            val timestamps = stack.mallocLong(2)
            val maxDeviation = stack.mallocLong(1)

            val res = EXTCalibratedTimestamps.vkGetCalibratedTimestampsEXT(
                vkDevice, infos, timestamps, maxDeviation
            )
            vkCheck(res, "vkGetCalibratedTimestampsEXT failed")

            val gpuNS = timestamps[0]
            val hostNS = timestamps[1]
            return hostNS to gpuNS
        }
    }
}