RayDeviceSupport.kt
src/jvmMain/kotlin/afield/graphics/vulkan/device/RayDeviceSupport.kt · 94 lines
package afield.graphics.vulkan.device
What the GPU supports for hardware ray tracing: RayDeviceSupport lists the acceleration-structure, ray-query and ray-tracing-pipeline extensions and features, and derives whether each path is usable.
lightingCapabilityReport turns that into a JSON report for a Device.
import org.json.JSONObject
import org.lwjgl.system.MemoryStack
import org.lwjgl.vulkan.*
data class RayDeviceSupport
Physical support, separate from Device's enabled features. Field already requires Vulkan 1.3, which supplies buffer-device-address, descriptor-indexing, SPIR-V 1.4 and float-control dependencies.
data class RayDeviceSupport(
/** `VK_KHR_acceleration_structure` is in the device's extension list. */
val accelerationExtension: Boolean,
/** `VK_KHR_deferred_host_operations` is present, which acceleration-structure builds depend on. */
val deferredHostOperations: Boolean,
/** `VK_KHR_ray_query` is in the device's extension list. */
val queryExtension: Boolean,
/** `VK_KHR_ray_tracing_pipeline` is in the device's extension list. */
val pipelineExtension: Boolean,
/** The `bufferDeviceAddress` feature, which shader-side acceleration-structure access needs. */
val bufferDeviceAddress: Boolean,
/**
* The `accelerationStructure` feature. Only queried when [accelerationExtension] holds, so a
* device missing the extension reports this false as well.
*/
val accelerationStructure: Boolean,
/**
* The `rayQuery` feature — tracing from an ordinary compute or fragment shader. Only queried
* when [queryExtension] holds.
*/
val rayQuery: Boolean,
/**
* The `rayTracingPipeline` feature — raygen, hit and miss shader stages. Only queried when
* [pipelineExtension] holds.
*/
val rayTracingPipeline: Boolean
) {
val accelerationUsable get()
val accelerationUsable get() = accelerationExtension && deferredHostOperations && bufferDeviceAddress && accelerationStructure
val queryUsable get() = accelerationUsable && queryExtension && rayQuery
val pipelineUsable get() = accelerationUsable && pipelineExtension && rayTracingPipeline
companion object
fun probe(device: VkPhysicalDevice, extensions: Set<String>): RayDeviceSupport
fun probe(device: VkPhysicalDevice, extensions: Set<String>): RayDeviceSupport = MemoryStack.stackPush().use { stack ->
val asExt = KHRAccelerationStructure.VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME in extensions
val queryExt = KHRRayQuery.VK_KHR_RAY_QUERY_EXTENSION_NAME in extensions
val pipelineExt = KHRRayTracingPipeline.VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME in extensions
val address = VkPhysicalDeviceBufferDeviceAddressFeatures.calloc(stack).`sType$Default`()
val accel = VkPhysicalDeviceAccelerationStructureFeaturesKHR.calloc(stack).`sType$Default`()
val query = VkPhysicalDeviceRayQueryFeaturesKHR.calloc(stack).`sType$Default`()
val pipeline = VkPhysicalDeviceRayTracingPipelineFeaturesKHR.calloc(stack).`sType$Default`()
val features = VkPhysicalDeviceFeatures2.calloc(stack).`sType$Default`().pNext(address.address())
var tail = address.address()
for (next in listOfNotNull(accel.takeIf { asExt }?.address(), query.takeIf { queryExt }?.address(), pipeline.takeIf { pipelineExt }?.address())) {
org.lwjgl.system.MemoryUtil.memPutAddress(tail + VkBaseOutStructure.PNEXT, next)
tail = next
}
VK11.vkGetPhysicalDeviceFeatures2(device, features)
RayDeviceSupport(asExt, KHRDeferredHostOperations.VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME in extensions,
queryExt, pipelineExt, address.bufferDeviceAddress(), accel.accelerationStructure(), query.rayQuery(), pipeline.rayTracingPipeline())
}
}
}
fun Device.lightingCapabilityReport(): JSONObject
JSON snapshot from the actual selected physical/logical device; safe on non-ray-query devices.
fun Device.lightingCapabilityReport(): JSONObject = MemoryStack.stackPush().use { stack ->
val driver = VkPhysicalDeviceDriverProperties.calloc(stack).`sType$Default`()
val props = VkPhysicalDeviceProperties2.calloc(stack).`sType$Default`().pNext(driver.address())
VK11.vkGetPhysicalDeviceProperties2(physicalDevice.vkPhysicalDevice, props)
val p = props.properties()
val format = VkFormatProperties.calloc(stack)
VK10.vkGetPhysicalDeviceFormatProperties(physicalDevice.vkPhysicalDevice, VK10.VK_FORMAT_R32G32B32_SFLOAT, format)
JSONObject().put("device", p.deviceNameString()).put("driver", driver.driverNameString())
.put("driverInfo", driver.driverInfoString()).put("apiVersion", "${VK10.VK_VERSION_MAJOR(p.apiVersion())}.${VK10.VK_VERSION_MINOR(p.apiVersion())}.${VK10.VK_VERSION_PATCH(p.apiVersion())}")
.put("java", System.getProperty("java.runtime.version"))
.put("supported", JSONObject().put("accelerationStructure", raySupport.accelerationStructure)
.put("rayQuery", raySupport.rayQuery).put("rayTracingPipeline", raySupport.rayTracingPipeline)
.put("bufferDeviceAddress", raySupport.bufferDeviceAddress).put("deferredHostOperations", raySupport.deferredHostOperations))
.put("enabled", JSONObject().put("accelerationStructure", supportsAccelerationStructures)
.put("rayQuery", supportsRayQuery).put("rayTracingPipeline", supportsRayTracing)
.put("bufferDeviceAddress", true).put("extensions", enabledExtensions.sorted()))
.put("limits", rayTracingLimits?.let { JSONObject().put("scratchAlignment", it.minAccelerationStructureScratchOffsetAlignment)
.put("maxGeometryCount", it.maxGeometryCount).put("maxInstanceCount", it.maxInstanceCount)
.put("maxPrimitiveCount", it.maxPrimitiveCount).put("maxPerStageAccelerationStructures", it.maxPerStageDescriptorAccelerationStructures)
.put("maxDescriptorSetAccelerationStructures", it.maxDescriptorSetAccelerationStructures) } ?: JSONObject.NULL)
.put("rgb32fAccelerationVertices", format.bufferFeatures() and KHRAccelerationStructure.VK_FORMAT_FEATURE_ACCELERATION_STRUCTURE_VERTEX_BUFFER_BIT_KHR != 0)
.put("maxStorageBufferRange", p.limits().maxStorageBufferRange().toUInt().toLong()).put("maxPushConstantsSize", p.limits().maxPushConstantsSize())
.put("timestampPeriodNs", p.limits().timestampPeriod())
.put("queueTimestampValidBits", (0 until physicalDevice.vkQueueFamilyProps.capacity()).map { physicalDevice.vkQueueFamilyProps[it].timestampValidBits() })
}