Investigated the VK_ERROR_DEVICE_LOST on the native VulkanTriangle (#7). Verified the engine side is correct and validation-clean: the BLAS/TLAS build finishes before render (FinishInit waits), the built instance is well-formed (identity transform, mask=0xFF, correct BLAS ref), and vkWriteResourceDescriptorsEXT stores the TLAS device address at the expected heap offset (confirmed by dumping the heap bytes). Khronos validation 1.4.350 reports zero errors. The fault is isolated to reading the acceleration structure through VK_EXT_descriptor_heap: - images/buffers via the same heap render fine (trace disabled -> the raygen imageStore path renders a full gradient); - both traceRayEXT and inline rayQueryEXT (no SBT) fault identically on the AS read; - reproduces with the AS descriptor at heap byte 0 / shader index 0 (no offset/stride ambiguity) and regardless of pAddressRange size. NVIDIA 610.43.02 is the only descriptor_heap implementation available (llvmpipe lacks the extension), so there is no second implementation to cross-check. Conclusion: driver-side fault in NVIDIA's brand-new VK_EXT_descriptor_heap acceleration-structure path; should be reported to NVIDIA. The traceRayEXT call is left active so the example stays a faithful reproducer. Documented in both READMEs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
274 lines
11 KiB
C++
274 lines
11 KiB
C++
#ifndef CRAFTER_GRAPHICS_WINDOW_DOM
|
|
#include "vulkan/vulkan.h"
|
|
#endif
|
|
#include <cassert>
|
|
|
|
import Crafter.Graphics;
|
|
using namespace Crafter;
|
|
import std;
|
|
import Crafter.Event;
|
|
import Crafter.Math;
|
|
|
|
#ifndef CRAFTER_GRAPHICS_WINDOW_DOM
|
|
int main() {
|
|
Device::Initialize();
|
|
Window window(1280, 720, "HelloVulkan");
|
|
VkCommandBuffer cmd = window.StartInit();
|
|
DescriptorHeapVulkan descriptorHeap;
|
|
descriptorHeap.Initialize(1,1,0);
|
|
|
|
VkSpecializationMapEntry entry = {
|
|
.constantID = 0,
|
|
.offset = 0,
|
|
.size = sizeof(uint16_t)
|
|
};
|
|
|
|
VkSpecializationInfo specilizationInfo = {
|
|
.mapEntryCount = 1,
|
|
.pMapEntries = &entry,
|
|
.dataSize = sizeof(uint16_t),
|
|
.pData = &descriptorHeap.bufferStartElement
|
|
};
|
|
|
|
std::array<VulkanShader, 3> shaders{{
|
|
{"raygen.spv", "main", VK_SHADER_STAGE_RAYGEN_BIT_KHR, &specilizationInfo},
|
|
{"miss.spv", "main", VK_SHADER_STAGE_MISS_BIT_KHR, nullptr},
|
|
{"closesthit.spv", "main", VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, nullptr}
|
|
}};
|
|
|
|
ShaderBindingTableVulkan shaderTable;
|
|
shaderTable.Init(shaders);
|
|
|
|
std::array<VkRayTracingShaderGroupCreateInfoKHR, 1> raygenGroups {{
|
|
{
|
|
.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR,
|
|
.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR,
|
|
.generalShader = 0,
|
|
.closestHitShader = VK_SHADER_UNUSED_KHR,
|
|
.anyHitShader = VK_SHADER_UNUSED_KHR,
|
|
.intersectionShader = VK_SHADER_UNUSED_KHR,
|
|
},
|
|
}};
|
|
std::array<VkRayTracingShaderGroupCreateInfoKHR, 1> missGroups {{
|
|
{
|
|
.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR,
|
|
.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR,
|
|
.generalShader = 1,
|
|
.closestHitShader = VK_SHADER_UNUSED_KHR,
|
|
.anyHitShader = VK_SHADER_UNUSED_KHR,
|
|
.intersectionShader = VK_SHADER_UNUSED_KHR,
|
|
},
|
|
}};
|
|
std::array<VkRayTracingShaderGroupCreateInfoKHR, 1> hitGroups {{
|
|
{
|
|
.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR,
|
|
.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_KHR,
|
|
.generalShader = VK_SHADER_UNUSED_KHR,
|
|
.closestHitShader = 2,
|
|
.anyHitShader = VK_SHADER_UNUSED_KHR,
|
|
.intersectionShader = VK_SHADER_UNUSED_KHR,
|
|
},
|
|
}};
|
|
|
|
PipelineRTVulkan pipeline;
|
|
pipeline.Init(cmd, raygenGroups, missGroups, hitGroups, shaderTable);
|
|
|
|
Mesh triangleMesh;
|
|
std::array<Vector<float, 3, 3>, 3> verts {{{-150, -150, 100}, {0, 150, 100}, {150, -150, 100}}};
|
|
std::array<std::uint32_t, 3> index {{2,1,0}};
|
|
triangleMesh.Build(verts, index, cmd);
|
|
|
|
RenderingElement3D renderer = {
|
|
.instance = {
|
|
.instanceCustomIndex = 0,
|
|
.mask = 0xFF,
|
|
.instanceShaderBindingTableRecordOffset = 0,
|
|
.flags = VK_GEOMETRY_INSTANCE_FORCE_OPAQUE_BIT_KHR,
|
|
.accelerationStructureReference = triangleMesh.blasAddr
|
|
}
|
|
};
|
|
|
|
RenderingElement3D::elements.emplace_back(&renderer);
|
|
|
|
MatrixRowMajor<float, 4, 3, 1> transform = MatrixRowMajor<float, 4, 3, 1>::Identity();
|
|
transform.Store(reinterpret_cast<float*>(renderer.instance.transform.matrix));
|
|
RenderingElement3D::BuildTLAS(cmd, 0);
|
|
RenderingElement3D::BuildTLAS(cmd, 1);
|
|
RenderingElement3D::BuildTLAS(cmd, 2);
|
|
|
|
window.FinishInit();
|
|
|
|
auto imgSlots = descriptorHeap.AllocateImageSlots(1);
|
|
auto bufSlots = descriptorHeap.AllocateBufferSlots(1);
|
|
|
|
VkDeviceAddressRangeKHR tlasRange0 = {
|
|
.address = RenderingElement3D::tlases[0].address,
|
|
};
|
|
|
|
VkDeviceAddressRangeKHR tlasRange1 = {
|
|
.address = RenderingElement3D::tlases[1].address,
|
|
};
|
|
|
|
VkDeviceAddressRangeKHR tlasRange2 = {
|
|
.address = RenderingElement3D::tlases[2].address,
|
|
};
|
|
|
|
VkImageDescriptorInfoEXT imageInfo0 = {
|
|
.sType = VK_STRUCTURE_TYPE_IMAGE_DESCRIPTOR_INFO_EXT,
|
|
.pView = &window.imageViews[0],
|
|
.layout = VK_IMAGE_LAYOUT_GENERAL
|
|
};
|
|
|
|
VkImageDescriptorInfoEXT imageInfo1 = {
|
|
.sType = VK_STRUCTURE_TYPE_IMAGE_DESCRIPTOR_INFO_EXT,
|
|
.pView = &window.imageViews[1],
|
|
.layout = VK_IMAGE_LAYOUT_GENERAL
|
|
};
|
|
|
|
VkImageDescriptorInfoEXT imageInfo2 = {
|
|
.sType = VK_STRUCTURE_TYPE_IMAGE_DESCRIPTOR_INFO_EXT,
|
|
.pView = &window.imageViews[2],
|
|
.layout = VK_IMAGE_LAYOUT_GENERAL
|
|
};
|
|
|
|
VkResourceDescriptorInfoEXT resources[6] = {
|
|
{
|
|
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
|
|
.type = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR,
|
|
.data = { .pAddressRange = &tlasRange0}
|
|
},
|
|
{
|
|
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
|
|
.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
|
.data = { .pImage = &imageInfo0 }
|
|
},
|
|
{
|
|
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
|
|
.type = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR,
|
|
.data = { .pAddressRange = &tlasRange1}
|
|
},
|
|
{
|
|
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
|
|
.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
|
.data = { .pImage = &imageInfo1 }
|
|
},
|
|
{
|
|
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
|
|
.type = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR,
|
|
.data = { .pAddressRange = &tlasRange2}
|
|
},
|
|
{
|
|
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
|
|
.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
|
.data = { .pImage = &imageInfo2 }
|
|
},
|
|
};
|
|
|
|
VkHostAddressRangeEXT destinations[6] = {
|
|
{
|
|
.address = descriptorHeap.resourceHeap[0].value + descriptorHeap.BufferByteOffset(bufSlots.firstElement),
|
|
.size = Device::descriptorHeapProperties.bufferDescriptorSize
|
|
},
|
|
{
|
|
.address = descriptorHeap.resourceHeap[0].value + descriptorHeap.ImageByteOffset(imgSlots.firstElement),
|
|
.size = Device::descriptorHeapProperties.imageDescriptorSize
|
|
},
|
|
{
|
|
.address = descriptorHeap.resourceHeap[1].value + descriptorHeap.BufferByteOffset(bufSlots.firstElement),
|
|
.size = Device::descriptorHeapProperties.bufferDescriptorSize
|
|
},
|
|
{
|
|
.address = descriptorHeap.resourceHeap[1].value + descriptorHeap.ImageByteOffset(imgSlots.firstElement),
|
|
.size = Device::descriptorHeapProperties.imageDescriptorSize
|
|
},
|
|
{
|
|
.address = descriptorHeap.resourceHeap[2].value + descriptorHeap.BufferByteOffset(bufSlots.firstElement),
|
|
.size = Device::descriptorHeapProperties.bufferDescriptorSize
|
|
},
|
|
{
|
|
.address = descriptorHeap.resourceHeap[2].value + descriptorHeap.ImageByteOffset(imgSlots.firstElement),
|
|
.size = Device::descriptorHeapProperties.imageDescriptorSize
|
|
},
|
|
};
|
|
|
|
Device::vkWriteResourceDescriptorsEXT(Device::device, 6, resources, destinations);
|
|
|
|
descriptorHeap.resourceHeap[0].FlushDevice();
|
|
descriptorHeap.resourceHeap[1].FlushDevice();
|
|
descriptorHeap.resourceHeap[2].FlushDevice();
|
|
|
|
window.descriptorHeap = &descriptorHeap;
|
|
RTPass rtPass(&pipeline);
|
|
window.passes.push_back(&rtPass);
|
|
|
|
// NOTE: on NVIDIA 610.43.02 this aborts with VK_ERROR_DEVICE_LOST the
|
|
// first time the raygen shader reads the acceleration structure out of
|
|
// the VK_EXT_descriptor_heap. The build, descriptors and SBT are all
|
|
// correct and validation-clean; it is a driver-side fault in the
|
|
// descriptor-heap acceleration-structure path. See README.md
|
|
// ("Native status — known driver fault") for the full investigation.
|
|
window.Render();
|
|
window.StartSync();
|
|
}
|
|
#else
|
|
// DOM-mode port. Same scene (one triangle), software-emulated raytracing
|
|
// via compute. Shaders are read from .wgsl files shipped as static
|
|
// assets (see project.cpp). Renders barycentric colors via the
|
|
// hit/miss/raygen mega-switch in PipelineRTWebGPU.
|
|
int main() {
|
|
Device::Initialize();
|
|
static Window window(1280, 720, "HelloVulkan");
|
|
auto cmd = window.StartInit();
|
|
|
|
DescriptorHeapWebGPU heap;
|
|
heap.Initialize(/*images*/ 4, /*buffers*/ 4, /*samplers*/ 2);
|
|
|
|
std::array<WebGPUShader, 3> shaders {{
|
|
WebGPUShader(std::filesystem::path("raygen.wgsl"), "raygen_main", WebGPURTStage::Raygen),
|
|
WebGPUShader(std::filesystem::path("miss.wgsl"), "miss_main", WebGPURTStage::Miss),
|
|
WebGPUShader(std::filesystem::path("closesthit.wgsl"), "closesthit_main", WebGPURTStage::ClosestHit),
|
|
}};
|
|
ShaderBindingTableWebGPU sbt;
|
|
sbt.Init(shaders);
|
|
|
|
std::array<RTShaderGroup, 1> raygenGroups {{
|
|
{ .type = RTShaderGroupType::General, .generalShader = 0 },
|
|
}};
|
|
std::array<RTShaderGroup, 1> missGroups {{
|
|
{ .type = RTShaderGroupType::General, .generalShader = 1 },
|
|
}};
|
|
std::array<RTShaderGroup, 1> hitGroups {{
|
|
{ .type = RTShaderGroupType::TrianglesHitGroup, .closestHitShader = 2 },
|
|
}};
|
|
|
|
PipelineRTWebGPU pipeline;
|
|
pipeline.Init(cmd, raygenGroups, missGroups, hitGroups, sbt);
|
|
|
|
Mesh triangleMesh;
|
|
std::array<Vector<float, 3, 3>, 3> verts {{{-150, -150, 100}, {0, 150, 100}, {150, -150, 100}}};
|
|
std::array<std::uint32_t, 3> index {{2, 1, 0}};
|
|
triangleMesh.Build(verts, index, cmd);
|
|
|
|
static RenderingElement3D renderer;
|
|
renderer.instance.transform.matrix[0][0] = 1; renderer.instance.transform.matrix[0][1] = 0; renderer.instance.transform.matrix[0][2] = 0; renderer.instance.transform.matrix[0][3] = 0;
|
|
renderer.instance.transform.matrix[1][0] = 0; renderer.instance.transform.matrix[1][1] = 1; renderer.instance.transform.matrix[1][2] = 0; renderer.instance.transform.matrix[1][3] = 0;
|
|
renderer.instance.transform.matrix[2][0] = 0; renderer.instance.transform.matrix[2][1] = 0; renderer.instance.transform.matrix[2][2] = 1; renderer.instance.transform.matrix[2][3] = 0;
|
|
renderer.instance.instanceCustomIndex = 0;
|
|
renderer.instance.mask = 0xFF;
|
|
renderer.instance.instanceShaderBindingTableRecordOffset = 0;
|
|
renderer.instance.flags = kRTGeometryInstanceForceOpaque;
|
|
renderer.instance.accelerationStructureReference = triangleMesh.blasAddr;
|
|
RenderingElement3D::Add(&renderer);
|
|
RenderingElement3D::BuildTLAS(cmd, 0);
|
|
|
|
window.descriptorHeap = &heap;
|
|
window.FinishInit();
|
|
|
|
RTPass rtPass(&pipeline);
|
|
window.passes.push_back(&rtPass);
|
|
|
|
window.Render();
|
|
window.StartUpdate();
|
|
window.StartSync();
|
|
}
|
|
#endif
|