载入中...
搜索中...
未找到
VulkanRayTracing.cpp
浏览该文件的文档.
2
3#include "common/Capability.h"
4#include "common/Diagnostic.h"
5#include "common/Exception.h"
6#include "common/Module.h"
7#include "common/Status.h"
8#include "graphics/Canvas.h"
9#include "graphics/Graphics.h"
10#include "graphics/Mesh.h"
11#include "graphics/Texture.h"
12#include "graphics/shaders/rt_reflection_rchit_spv.inc"
13#include "graphics/shaders/rt_reflection_rgen_spv.inc"
14#include "graphics/shaders/rt_reflection_rmiss_spv.inc"
18
19#include <algorithm>
20#include <cstring>
21#include <string>
22#include <vector>
23
25namespace {
26
27uint32_t alignedSize(uint32_t value, uint32_t alignment) {
28 if (alignment == 0) return value;
29 return (value + alignment - 1u) & ~(alignment - 1u);
30}
31
32vk::TransformMatrixKHR toTransformMatrix(const glm::mat4& m) {
33 // Vulkan expects a 3x4 row-major affine matrix.
34 vk::TransformMatrixKHR out{};
35 const float* src = &m[0][0];
36 out.matrix[0][0] = src[0];
37 out.matrix[0][1] = src[4];
38 out.matrix[0][2] = src[8];
39 out.matrix[0][3] = src[12];
40 out.matrix[1][0] = src[1];
41 out.matrix[1][1] = src[5];
42 out.matrix[1][2] = src[9];
43 out.matrix[1][3] = src[13];
44 out.matrix[2][0] = src[2];
45 out.matrix[2][1] = src[6];
46 out.matrix[2][2] = src[10];
47 out.matrix[2][3] = src[14];
48 return out;
49}
50
51} // namespace
52
54 if (!device) return;
55 if (buffer) (*device)->destroyBuffer(buffer, device->allocation_callbacks);
56 if (memory) (*device)->freeMemory(memory, device->allocation_callbacks);
57 buffer = vk::Buffer{};
58 memory = vk::DeviceMemory{};
59 size = 0;
60 address = 0;
61 device = nullptr;
62}
63
65 buffer = o.buffer;
66 memory = o.memory;
67 size = o.size;
68 address = o.address;
69 device = o.device;
70 o.buffer = vk::Buffer{};
71 o.memory = vk::DeviceMemory{};
72 o.size = 0;
73 o.address = 0;
74 o.device = nullptr;
75}
76
77Result<void> DeviceAddressBuffer::allocate(vkb::Device& dev, vk::DeviceSize bytes, vk::BufferUsageFlags usage) {
78 if (bytes == 0) return Result<void>::failure(
79 Diagnostic::error(DiagnosticCode::InvalidArgument, std::string("DeviceAddressBuffer.allocate") + ": " + "size is zero", "graphics.raytracing"));
80 release();
81 device = &dev;
82
83 vk::BufferCreateInfo ci{};
84 ci.size = bytes;
85 ci.usage = usage | vk::BufferUsageFlagBits::eShaderDeviceAddress;
86 ci.sharingMode = vk::SharingMode::eExclusive;
87 buffer = (*device)->createBuffer(ci, device->allocation_callbacks);
88 size = bytes;
89
90 const auto memreq = (*device)->getBufferMemoryRequirements(buffer);
91
92 vk::MemoryAllocateFlagsInfo flagsInfo{};
93 flagsInfo.flags = vk::MemoryAllocateFlagBits::eDeviceAddress;
94
95 vk::MemoryAllocateInfo allocInfo{};
96 allocInfo.pNext = &flagsInfo;
97 allocInfo.allocationSize = memreq.size;
98 allocInfo.memoryTypeIndex =
99 device->physical_device.findMemoryTypeIndex(memreq.memoryTypeBits, vk::MemoryPropertyFlagBits::eDeviceLocal);
100 memory = (*device)->allocateMemory(allocInfo, device->allocation_callbacks);
101 (*device)->bindBufferMemory(buffer, memory, 0);
102
103 vk::BufferDeviceAddressInfo addrInfo{};
104 addrInfo.buffer = buffer;
105 address = (*device)->getBufferAddress(addrInfo);
106 if (address == 0) {
107 release();
109 Diagnostic::error(DiagnosticCode::Failed, std::string("DeviceAddressBuffer.allocate") + ": " + "getBufferAddress returned 0", "graphics.raytracing"));
110 }
111 return Result<void>::success();
112}
113
114Result<void> DeviceAddressBuffer::upload(vk::CommandPool pool, vk::Queue queue, const void* data,
115 vk::DeviceSize bytes) {
116 if (!device || !buffer) return Result<void>::failure(
117 Diagnostic::error(DiagnosticCode::Failed, std::string("DeviceAddressBuffer.upload") + ": " + "buffer not allocated", "graphics.raytracing"));
118 if (!data || bytes == 0 || bytes > size) return Result<void>::failure(
119 Diagnostic::error(DiagnosticCode::InvalidArgument, std::string("DeviceAddressBuffer.upload") + ": " + "invalid host data", "graphics.raytracing"));
120
121 using pfb = vk::MemoryPropertyFlagBits;
122 vkb::GenericBuffer staging(*device, vk::BufferUsageFlagBits::eTransferSrc, bytes,
123 pfb::eHostVisible | pfb::eHostCoherent);
124 staging.updateLocal(vkb::FrameSlot::gpuIdle(), data, bytes);
125
126 auto cmd = (*device)->allocateCommandBuffers({pool, vk::CommandBufferLevel::ePrimary, 1}).front();
127 cmd.begin({vk::CommandBufferUsageFlagBits::eOneTimeSubmit});
128 vk::BufferCopy copy{};
129 copy.size = bytes;
130 cmd.copyBuffer(staging.buffer, buffer, copy);
131 cmd.end();
132 vk::SubmitInfo submit{};
133 submit.commandBufferCount = 1;
134 submit.pCommandBuffers = &cmd;
135 queue.submit(submit, vk::Fence{});
136 queue.waitIdle();
137 (*device)->freeCommandBuffers(pool, cmd);
138 return Result<void>::success();
139}
140
142 if (device && handle) {
143 (*device)->destroyAccelerationStructureKHR(handle, device->allocation_callbacks);
144 handle = vk::AccelerationStructureKHR{};
145 }
147 deviceAddress = 0;
148 device = nullptr;
149}
150
152 handle = o.handle;
153 storage = std::move(o.storage);
154 deviceAddress = o.deviceAddress;
155 device = o.device;
156 o.handle = vk::AccelerationStructureKHR{};
157 o.deviceAddress = 0;
158 o.device = nullptr;
159}
160
162 static VulkanRayTracing instance;
163 return instance;
164}
165
167
169 auto* base = eve::ModuleManager::getInstance<eve::graphics::Graphics>("Graphics");
170 if (!base) base = eve::graphics::Graphics::create();
171 auto* vg = dynamic_cast<eve::graphics::vulkan::Graphics*>(base);
172 if (!vg || !vg->supportsRayTracing()) {
173 if (vg) caps_ = vg->rayTracingCaps();
175 DiagnosticCode::Unsupported, std::string("ensureAttached") + ": hardware ray tracing unavailable", "graphics.raytracing"));
176 }
177 vkb::Device* current = &vg->getDevice();
178 // Rebind whenever Graphics rebuilt the logical device (pointer identity).
179 if (device_ == current && caps_.rayTracingAvailable()) return Result<void>::success();
180 attachDevice(current, vg->rayTracingCaps(), vg->getUploadPool(),
181 vg->getDevice().getQueue(vkb::QueueType::graphics));
183 DiagnosticCode::Unsupported, std::string("ensureAttached") + ": hardware ray tracing unavailable", "graphics.raytracing"));
184 return Result<void>::success();
185}
186
188 if (device_ && caps_.rayTracingAvailable()) return true;
189 // Const path: probe Graphics without mutating. Non-const callers use ensureAttached.
190 auto* base = eve::ModuleManager::getInstance<eve::graphics::Graphics>("Graphics");
191 auto* vg = dynamic_cast<eve::graphics::vulkan::Graphics*>(base);
192 return vg && vg->supportsRayTracing();
193}
194
196 if (caps_.rayTracingAvailable()) return caps_;
197 auto* base = eve::ModuleManager::getInstance<eve::graphics::Graphics>("Graphics");
198 auto* vg = dynamic_cast<eve::graphics::vulkan::Graphics*>(base);
199 return vg ? vg->rayTracingCaps() : RayTracingCaps{};
200}
201
202void VulkanRayTracing::attachDevice(vkb::Device* device, const RayTracingCaps& caps, vk::CommandPool uploadPool,
203 vk::Queue graphicsQueue) {
204 detachDevice();
205 device_ = device;
206 caps_ = caps;
207 uploadPool_ = uploadPool;
208 graphicsQueue_ = graphicsQueue;
209}
210
212 clearScene();
213 if (device_) {
214 if (pipeline_) (*device_)->destroyPipeline(pipeline_, device_->allocation_callbacks);
215 if (pipelineLayout_) (*device_)->destroyPipelineLayout(pipelineLayout_, device_->allocation_callbacks);
216 if (setLayout_) (*device_)->destroyDescriptorSetLayout(setLayout_, device_->allocation_callbacks);
217 if (descriptorPool_) (*device_)->destroyDescriptorPool(descriptorPool_, device_->allocation_callbacks);
218 if (sampler_) (*device_)->destroySampler(sampler_, device_->allocation_callbacks);
219 if (raygenModule_) (*device_)->destroyShaderModule(raygenModule_, device_->allocation_callbacks);
220 if (missModule_) (*device_)->destroyShaderModule(missModule_, device_->allocation_callbacks);
221 if (closestHitModule_) (*device_)->destroyShaderModule(closestHitModule_, device_->allocation_callbacks);
222 }
223 pipeline_ = vk::Pipeline{};
224 pipelineLayout_ = vk::PipelineLayout{};
225 setLayout_ = vk::DescriptorSetLayout{};
226 descriptorPool_ = vk::DescriptorPool{};
227 descriptorSet_ = vk::DescriptorSet{};
228 sampler_ = vk::Sampler{};
229 raygenModule_ = vk::ShaderModule{};
230 missModule_ = vk::ShaderModule{};
231 closestHitModule_ = vk::ShaderModule{};
232 outputImage_ = StorageColorImage{};
233 outputWidth_ = 0;
234 outputHeight_ = 0;
235 sbtBuffer_.release();
236 raygenRegion_ = vk::StridedDeviceAddressRegionKHR{};
237 missRegion_ = vk::StridedDeviceAddressRegionKHR{};
238 hitRegion_ = vk::StridedDeviceAddressRegionKHR{};
239 callableRegion_ = vk::StridedDeviceAddressRegionKHR{};
240 device_ = nullptr;
241 uploadPool_ = vk::CommandPool{};
242 graphicsQueue_ = vk::Queue{};
243 caps_ = {};
244}
245
247 meshes_.clear();
248 tlas_.release();
249}
250
251Result<uint32_t> VulkanRayTracing::addTriangleMesh(const float* positionsXYZ, int vertexCount, const uint32_t* indices,
252 int indexCount, const glm::mat4& transform) {
253 if (auto attached = ensureAttached(); !attached.ok()) return Result<uint32_t>::failure(attached.status());
254 if (!positionsXYZ || vertexCount < 3 || !indices || indexCount < 3 || (indexCount % 3) != 0)
256 "addTriangleMesh: invalid geometry", "graphics.raytracing"));
257
258 auto mesh = std::make_unique<TriangleMeshRecord>();
259 mesh->vertexCount = uint32_t(vertexCount);
260 mesh->indexCount = uint32_t(indexCount);
261 mesh->transform = transform;
262
263 const vk::DeviceSize vBytes = vk::DeviceSize(vertexCount) * 3u * sizeof(float);
264 const vk::DeviceSize iBytes = vk::DeviceSize(indexCount) * sizeof(uint32_t);
265 const auto usage = vk::BufferUsageFlagBits::eAccelerationStructureBuildInputReadOnlyKHR |
266 vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eTransferDst;
267
268 if (auto r = mesh->vertexBuffer.allocate(*device_, vBytes, usage); !r.ok())
269 return Result<uint32_t>::failure(r.status());
270 if (auto r = mesh->indexBuffer.allocate(*device_, iBytes, usage); !r.ok())
271 return Result<uint32_t>::failure(r.status());
272 if (auto r = mesh->vertexBuffer.upload(uploadPool_, graphicsQueue_, positionsXYZ, vBytes); !r.ok())
273 return Result<uint32_t>::failure(r.status());
274 if (auto r = mesh->indexBuffer.upload(uploadPool_, graphicsQueue_, indices, iBytes); !r.ok())
275 return Result<uint32_t>::failure(r.status());
276 if (auto r = buildBlas(*mesh); !r.ok()) return Result<uint32_t>::failure(r.status());
277
278 const uint32_t id = uint32_t(meshes_.size());
279 meshes_.push_back(std::move(mesh));
280 return Result<uint32_t>::success(id);
281}
282
284 if (auto attached = ensureAttached(); !attached.ok()) return Result<uint32_t>::failure(attached.status());
285 if (!mesh || !mesh->gpuHandle)
287 DiagnosticCode::InvalidArgument, "addMesh: null mesh or missing GPU handle", "graphics.raytracing"));
288 auto* gpu = static_cast<eve::graphics::vulkan::GpuMesh*>(mesh->gpuHandle);
289 if (gpu->vertexCount < 3 || gpu->indexCount < 3 || (gpu->indexCount % 3u) != 0)
291 Diagnostic::error(DiagnosticCode::InvalidArgument, "addMesh: invalid geometry", "graphics.raytracing"));
292
293 auto& vb = eve::graphics::vulkan::meshDrawVertices(*gpu);
294 if (!vb.buffer)
296 Diagnostic::error(DiagnosticCode::Failed, "addMesh: empty vertex buffer", "graphics.raytracing"));
297
298 std::vector<float> positions(size_t(gpu->vertexCount) * 3u);
299 {
300 void* mapped = vb.map();
301 const auto* verts = static_cast<const eve::graphics::vulkan::MeshVertex*>(mapped);
302 for (uint32_t i = 0; i < gpu->vertexCount; ++i) {
303 positions[size_t(i) * 3u + 0u] = verts[i].pos.x;
304 positions[size_t(i) * 3u + 1u] = verts[i].pos.y;
305 positions[size_t(i) * 3u + 2u] = verts[i].pos.z;
306 }
307 vb.unmap();
308 }
309
310 std::vector<uint32_t> indices(gpu->indexCount);
311 if (!gpu->cpuIndices.empty() && gpu->cpuIndices.size() >= gpu->indexCount) {
312 std::copy_n(gpu->cpuIndices.begin(), gpu->indexCount, indices.begin());
313 } else {
314 auto& ib = eve::graphics::vulkan::meshDrawIndices(*gpu);
315 if (!ib.buffer)
317 Diagnostic::error(DiagnosticCode::Failed, "addMesh: empty index buffer", "graphics.raytracing"));
318 void* mapped = ib.map();
319 if (gpu->indexType == vk::IndexType::eUint16) {
320 const auto* src = static_cast<const uint16_t*>(mapped);
321 for (uint32_t i = 0; i < gpu->indexCount; ++i) indices[i] = src[i];
322 } else {
323 std::memcpy(indices.data(), mapped, size_t(gpu->indexCount) * sizeof(uint32_t));
324 }
325 ib.unmap();
326 }
327
328 return addTriangleMesh(positions.data(), int(gpu->vertexCount), indices.data(), int(gpu->indexCount), transform);
329}
330
331Result<void> VulkanRayTracing::buildBlas(TriangleMeshRecord& mesh) {
332 vk::AccelerationStructureGeometryTrianglesDataKHR triangles{};
333 triangles.vertexFormat = vk::Format::eR32G32B32Sfloat;
334 triangles.vertexData.deviceAddress = mesh.vertexBuffer.address;
335 triangles.vertexStride = sizeof(float) * 3;
336 triangles.maxVertex = mesh.vertexCount - 1;
337 triangles.indexType = vk::IndexType::eUint32;
338 triangles.indexData.deviceAddress = mesh.indexBuffer.address;
339
340 vk::AccelerationStructureGeometryKHR geometry{};
341 geometry.geometryType = vk::GeometryTypeKHR::eTriangles;
342 geometry.flags = vk::GeometryFlagBitsKHR::eOpaque;
343 geometry.geometry.triangles = triangles;
344
345 vk::AccelerationStructureBuildGeometryInfoKHR buildInfo{};
346 buildInfo.type = vk::AccelerationStructureTypeKHR::eBottomLevel;
347 buildInfo.flags = vk::BuildAccelerationStructureFlagBitsKHR::ePreferFastTrace;
348 buildInfo.mode = vk::BuildAccelerationStructureModeKHR::eBuild;
349 buildInfo.geometryCount = 1;
350 buildInfo.pGeometries = &geometry;
351
352 const uint32_t primitiveCount = mesh.indexCount / 3;
353 const auto sizes = (*device_)->getAccelerationStructureBuildSizesKHR(vk::AccelerationStructureBuildTypeKHR::eDevice,
354 buildInfo, primitiveCount);
355
356 auto storageResult = mesh.blas.storage.allocate(
357 *device_, sizes.accelerationStructureSize,
358 vk::BufferUsageFlagBits::eAccelerationStructureStorageKHR | vk::BufferUsageFlagBits::eShaderDeviceAddress);
359 if (!storageResult.ok()) return storageResult;
360
361 vk::AccelerationStructureCreateInfoKHR createInfo{};
362 createInfo.buffer = mesh.blas.storage.buffer;
363 createInfo.size = sizes.accelerationStructureSize;
364 createInfo.type = vk::AccelerationStructureTypeKHR::eBottomLevel;
365 mesh.blas.handle = (*device_)->createAccelerationStructureKHR(createInfo, device_->allocation_callbacks);
366 mesh.blas.device = device_;
367
368 vk::AccelerationStructureDeviceAddressInfoKHR addrInfo{};
369 addrInfo.accelerationStructure = mesh.blas.handle;
370 mesh.blas.deviceAddress = (*device_)->getAccelerationStructureAddressKHR(addrInfo);
371
372 DeviceAddressBuffer scratch;
373 if (auto r =
374 scratch.allocate(*device_, sizes.buildScratchSize,
375 vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eShaderDeviceAddress);
376 !r.ok())
377 return r;
378
379 buildInfo.dstAccelerationStructure = mesh.blas.handle;
380 buildInfo.scratchData.deviceAddress = scratch.address;
381
382 vk::AccelerationStructureBuildRangeInfoKHR range{};
383 range.primitiveCount = primitiveCount;
384 const vk::AccelerationStructureBuildRangeInfoKHR* ranges = &range;
385
386 auto cmd = (*device_)->allocateCommandBuffers({uploadPool_, vk::CommandBufferLevel::ePrimary, 1}).front();
387 cmd.begin({vk::CommandBufferUsageFlagBits::eOneTimeSubmit});
388 cmd.buildAccelerationStructuresKHR(buildInfo, ranges);
389 cmd.end();
390 vk::SubmitInfo submit{};
391 submit.commandBufferCount = 1;
392 submit.pCommandBuffers = &cmd;
393 graphicsQueue_.submit(submit, vk::Fence{});
394 graphicsQueue_.waitIdle();
395 (*device_)->freeCommandBuffers(uploadPool_, cmd);
396 return Result<void>::success();
397}
398
399Result<void> VulkanRayTracing::buildTlas() {
400 tlas_.release();
401 if (meshes_.empty()) return Result<void>::success();
402
403 std::vector<vk::AccelerationStructureInstanceKHR> instances;
404 instances.reserve(meshes_.size());
405 for (const auto& mesh : meshes_) {
406 vk::AccelerationStructureInstanceKHR inst{};
407 inst.transform = toTransformMatrix(mesh->transform);
408 inst.instanceCustomIndex = 0;
409 inst.mask = 0xFF;
410 inst.instanceShaderBindingTableRecordOffset = 0;
411 inst.flags = uint32_t(vk::GeometryInstanceFlagBitsKHR::eTriangleFacingCullDisable);
412 inst.accelerationStructureReference = mesh->blas.deviceAddress;
413 instances.push_back(inst);
414 }
415
416 DeviceAddressBuffer instanceBuffer;
417 const vk::DeviceSize bytes = vk::DeviceSize(instances.size()) * sizeof(instances[0]);
418 if (auto r = instanceBuffer.allocate(*device_, bytes,
419 vk::BufferUsageFlagBits::eAccelerationStructureBuildInputReadOnlyKHR |
420 vk::BufferUsageFlagBits::eShaderDeviceAddress |
421 vk::BufferUsageFlagBits::eTransferDst);
422 !r.ok())
423 return r;
424 if (auto r = instanceBuffer.upload(uploadPool_, graphicsQueue_, instances.data(), bytes); !r.ok()) return r;
425
426 vk::AccelerationStructureGeometryInstancesDataKHR instancesData{};
427 instancesData.arrayOfPointers = VK_FALSE;
428 instancesData.data.deviceAddress = instanceBuffer.address;
429
430 vk::AccelerationStructureGeometryKHR geometry{};
431 geometry.geometryType = vk::GeometryTypeKHR::eInstances;
432 geometry.geometry.instances = instancesData;
433
434 vk::AccelerationStructureBuildGeometryInfoKHR buildInfo{};
435 buildInfo.type = vk::AccelerationStructureTypeKHR::eTopLevel;
436 buildInfo.flags = vk::BuildAccelerationStructureFlagBitsKHR::ePreferFastTrace;
437 buildInfo.mode = vk::BuildAccelerationStructureModeKHR::eBuild;
438 buildInfo.geometryCount = 1;
439 buildInfo.pGeometries = &geometry;
440
441 const uint32_t primitiveCount = uint32_t(instances.size());
442 const auto sizes = (*device_)->getAccelerationStructureBuildSizesKHR(vk::AccelerationStructureBuildTypeKHR::eDevice,
443 buildInfo, primitiveCount);
444
445 if (auto r = tlas_.storage.allocate(
446 *device_, sizes.accelerationStructureSize,
447 vk::BufferUsageFlagBits::eAccelerationStructureStorageKHR | vk::BufferUsageFlagBits::eShaderDeviceAddress);
448 !r.ok())
449 return r;
450
451 vk::AccelerationStructureCreateInfoKHR createInfo{};
452 createInfo.buffer = tlas_.storage.buffer;
453 createInfo.size = sizes.accelerationStructureSize;
454 createInfo.type = vk::AccelerationStructureTypeKHR::eTopLevel;
455 tlas_.handle = (*device_)->createAccelerationStructureKHR(createInfo, device_->allocation_callbacks);
456 tlas_.device = device_;
457
458 vk::AccelerationStructureDeviceAddressInfoKHR addrInfo{};
459 addrInfo.accelerationStructure = tlas_.handle;
460 tlas_.deviceAddress = (*device_)->getAccelerationStructureAddressKHR(addrInfo);
461
462 DeviceAddressBuffer scratch;
463 if (auto r =
464 scratch.allocate(*device_, sizes.buildScratchSize,
465 vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eShaderDeviceAddress);
466 !r.ok())
467 return r;
468
469 buildInfo.dstAccelerationStructure = tlas_.handle;
470 buildInfo.scratchData.deviceAddress = scratch.address;
471
472 vk::AccelerationStructureBuildRangeInfoKHR range{};
473 range.primitiveCount = primitiveCount;
474 const vk::AccelerationStructureBuildRangeInfoKHR* ranges = &range;
475
476 auto cmd = (*device_)->allocateCommandBuffers({uploadPool_, vk::CommandBufferLevel::ePrimary, 1}).front();
477 cmd.begin({vk::CommandBufferUsageFlagBits::eOneTimeSubmit});
478 cmd.buildAccelerationStructuresKHR(buildInfo, ranges);
479 cmd.end();
480 vk::SubmitInfo submit{};
481 submit.commandBufferCount = 1;
482 submit.pCommandBuffers = &cmd;
483 graphicsQueue_.submit(submit, vk::Fence{});
484 graphicsQueue_.waitIdle();
485 (*device_)->freeCommandBuffers(uploadPool_, cmd);
486 return Result<void>::success();
487}
488
490 if (auto attached = ensureAttached(); !attached.ok()) return attached;
491 return buildTlas();
492}
493
494Result<void> VulkanRayTracing::ensurePipeline() {
495 if (pipeline_) return Result<void>::success();
497 DiagnosticCode::Unsupported, std::string("ensurePipeline") + ": hardware ray tracing unavailable", "graphics.raytracing"));
498
499 auto makeModule = [&](const uint32_t* words, size_t count) -> vk::ShaderModule {
500 vk::ShaderModuleCreateInfo ci{};
501 ci.codeSize = count * sizeof(uint32_t);
502 ci.pCode = words;
503 return (*device_)->createShaderModule(ci, device_->allocation_callbacks);
504 };
505 raygenModule_ = makeModule(rt_reflection_rgen_spv, rt_reflection_rgen_spv_count);
506 missModule_ = makeModule(rt_reflection_rmiss_spv, rt_reflection_rmiss_spv_count);
507 closestHitModule_ = makeModule(rt_reflection_rchit_spv, rt_reflection_rchit_spv_count);
508
509 vk::DescriptorSetLayoutBinding bindings[5]{};
510 bindings[0].binding = 0;
511 bindings[0].descriptorType = vk::DescriptorType::eAccelerationStructureKHR;
512 bindings[0].descriptorCount = 1;
513 bindings[0].stageFlags = vk::ShaderStageFlagBits::eRaygenKHR;
514 bindings[1].binding = 1;
515 bindings[1].descriptorType = vk::DescriptorType::eStorageImage;
516 bindings[1].descriptorCount = 1;
517 bindings[1].stageFlags = vk::ShaderStageFlagBits::eRaygenKHR;
518 bindings[2].binding = 2;
519 bindings[2].descriptorType = vk::DescriptorType::eCombinedImageSampler;
520 bindings[2].descriptorCount = 1;
521 bindings[2].stageFlags = vk::ShaderStageFlagBits::eRaygenKHR;
522 bindings[3].binding = 3;
523 bindings[3].descriptorType = vk::DescriptorType::eCombinedImageSampler;
524 bindings[3].descriptorCount = 1;
525 bindings[3].stageFlags = vk::ShaderStageFlagBits::eRaygenKHR;
526 bindings[4].binding = 4;
527 bindings[4].descriptorType = vk::DescriptorType::eCombinedImageSampler;
528 bindings[4].descriptorCount = 1;
529 bindings[4].stageFlags = vk::ShaderStageFlagBits::eRaygenKHR;
530
531 vk::DescriptorSetLayoutCreateInfo layoutCi{};
532 layoutCi.bindingCount = 5;
533 layoutCi.pBindings = bindings;
534 setLayout_ = (*device_)->createDescriptorSetLayout(layoutCi, device_->allocation_callbacks);
535
536 vk::PushConstantRange push{};
537 push.stageFlags = vk::ShaderStageFlagBits::eRaygenKHR;
538 push.offset = 0;
539 push.size = sizeof(PushConstants);
540
541 vk::PipelineLayoutCreateInfo plCi{};
542 plCi.setLayoutCount = 1;
543 plCi.pSetLayouts = &setLayout_;
544 plCi.pushConstantRangeCount = 1;
545 plCi.pPushConstantRanges = &push;
546 pipelineLayout_ = (*device_)->createPipelineLayout(plCi, device_->allocation_callbacks);
547
548 std::array<vk::PipelineShaderStageCreateInfo, 3> stages{};
549 stages[0].stage = vk::ShaderStageFlagBits::eRaygenKHR;
550 stages[0].module = raygenModule_;
551 stages[0].pName = "main";
552 stages[1].stage = vk::ShaderStageFlagBits::eMissKHR;
553 stages[1].module = missModule_;
554 stages[1].pName = "main";
555 stages[2].stage = vk::ShaderStageFlagBits::eClosestHitKHR;
556 stages[2].module = closestHitModule_;
557 stages[2].pName = "main";
558
559 std::array<vk::RayTracingShaderGroupCreateInfoKHR, 3> groups{};
560 groups[0].type = vk::RayTracingShaderGroupTypeKHR::eGeneral;
561 groups[0].generalShader = 0;
562 groups[0].closestHitShader = VK_SHADER_UNUSED_KHR;
563 groups[0].anyHitShader = VK_SHADER_UNUSED_KHR;
564 groups[0].intersectionShader = VK_SHADER_UNUSED_KHR;
565 groups[1].type = vk::RayTracingShaderGroupTypeKHR::eGeneral;
566 groups[1].generalShader = 1;
567 groups[1].closestHitShader = VK_SHADER_UNUSED_KHR;
568 groups[1].anyHitShader = VK_SHADER_UNUSED_KHR;
569 groups[1].intersectionShader = VK_SHADER_UNUSED_KHR;
570 groups[2].type = vk::RayTracingShaderGroupTypeKHR::eTrianglesHitGroup;
571 groups[2].generalShader = VK_SHADER_UNUSED_KHR;
572 groups[2].closestHitShader = 2;
573 groups[2].anyHitShader = VK_SHADER_UNUSED_KHR;
574 groups[2].intersectionShader = VK_SHADER_UNUSED_KHR;
575
576 vk::RayTracingPipelineCreateInfoKHR pipeCi{};
577 pipeCi.stageCount = uint32_t(stages.size());
578 pipeCi.pStages = stages.data();
579 pipeCi.groupCount = uint32_t(groups.size());
580 pipeCi.pGroups = groups.data();
581 pipeCi.maxPipelineRayRecursionDepth =
582 std::max(1u, std::min(1u, caps_.maxRecursionDepth ? caps_.maxRecursionDepth : 1u));
583 pipeCi.layout = pipelineLayout_;
584
585 auto created = (*device_)->createRayTracingPipelinesKHR(vk::DeferredOperationKHR{}, vk::PipelineCache{}, pipeCi,
586 device_->allocation_callbacks);
587 if (created.result != vk::Result::eSuccess || created.value.empty())
589 Diagnostic::error(DiagnosticCode::Failed, std::string("ensurePipeline") + ": " + "createRayTracingPipelinesKHR failed", "graphics.raytracing"));
590 pipeline_ = created.value.front();
591
592 return createShaderBindingTable();
593}
594
595Result<void> VulkanRayTracing::createShaderBindingTable() {
596 const uint32_t handleSize = caps_.shaderGroupHandleSize;
597 const uint32_t handleAlignment = caps_.shaderGroupHandleAlignment ? caps_.shaderGroupHandleAlignment : handleSize;
598 const uint32_t baseAlignment = caps_.shaderGroupBaseAlignment ? caps_.shaderGroupBaseAlignment : handleAlignment;
599 const uint32_t handleSizeAligned = alignedSize(handleSize, handleAlignment);
600 const uint32_t groupCount = 3;
601 const uint32_t sbtSize = groupCount * handleSizeAligned;
602
603 std::vector<uint8_t> handles(sbtSize);
604 auto result = (*device_)->getRayTracingShaderGroupHandlesKHR(pipeline_, 0, groupCount, sbtSize, handles.data());
605 if (result != vk::Result::eSuccess)
607 Diagnostic::error(DiagnosticCode::Failed, std::string("createShaderBindingTable") + ": " + "getRayTracingShaderGroupHandlesKHR failed", "graphics.raytracing"));
608
609 // Pack groups with base alignment between regions: raygen | miss | hit
610 const uint32_t raygenSize = alignedSize(handleSizeAligned, baseAlignment);
611 const uint32_t missSize = alignedSize(handleSizeAligned, baseAlignment);
612 const uint32_t hitSize = alignedSize(handleSizeAligned, baseAlignment);
613 const uint32_t total = raygenSize + missSize + hitSize;
614
615 std::vector<uint8_t> sbt(total, 0);
616 std::memcpy(sbt.data(), handles.data(), handleSize);
617 std::memcpy(sbt.data() + raygenSize, handles.data() + handleSizeAligned, handleSize);
618 std::memcpy(sbt.data() + raygenSize + missSize, handles.data() + 2 * handleSizeAligned, handleSize);
619
620 if (auto r = sbtBuffer_.allocate(*device_, total,
621 vk::BufferUsageFlagBits::eShaderBindingTableKHR |
622 vk::BufferUsageFlagBits::eShaderDeviceAddress |
623 vk::BufferUsageFlagBits::eTransferDst);
624 !r.ok())
625 return r;
626 if (auto r = sbtBuffer_.upload(uploadPool_, graphicsQueue_, sbt.data(), total); !r.ok()) return r;
627
628 raygenRegion_.deviceAddress = sbtBuffer_.address;
629 raygenRegion_.stride = raygenSize;
630 raygenRegion_.size = raygenSize;
631
632 missRegion_.deviceAddress = sbtBuffer_.address + raygenSize;
633 missRegion_.stride = handleSizeAligned;
634 missRegion_.size = missSize;
635
636 hitRegion_.deviceAddress = sbtBuffer_.address + raygenSize + missSize;
637 hitRegion_.stride = handleSizeAligned;
638 hitRegion_.size = hitSize;
639
640 callableRegion_ = vk::StridedDeviceAddressRegionKHR{};
641 return Result<void>::success();
642}
643
644Result<void> VulkanRayTracing::ensureDescriptorSets(vk::ImageView outputView, vk::ImageView sceneView,
645 vk::ImageView depthView, vk::ImageView normalView) {
646 if (!descriptorPool_) {
647 std::array<vk::DescriptorPoolSize, 3> sizes{};
648 sizes[0] = {vk::DescriptorType::eAccelerationStructureKHR, 4};
649 sizes[1] = {vk::DescriptorType::eStorageImage, 4};
650 sizes[2] = {vk::DescriptorType::eCombinedImageSampler, 12};
651 vk::DescriptorPoolCreateInfo poolCi{};
652 poolCi.maxSets = 4;
653 poolCi.poolSizeCount = uint32_t(sizes.size());
654 poolCi.pPoolSizes = sizes.data();
655 descriptorPool_ = (*device_)->createDescriptorPool(poolCi, device_->allocation_callbacks);
656 }
657 if (!descriptorSet_) {
658 vk::DescriptorSetAllocateInfo alloc{};
659 alloc.descriptorPool = descriptorPool_;
660 alloc.descriptorSetCount = 1;
661 alloc.pSetLayouts = &setLayout_;
662 descriptorSet_ = (*device_)->allocateDescriptorSets(alloc).front();
663 }
664
665 vk::WriteDescriptorSetAccelerationStructureKHR asWrite{};
666 asWrite.accelerationStructureCount = 1;
667 asWrite.pAccelerationStructures = &tlas_.handle;
668
669 vk::WriteDescriptorSet writes[5]{};
670 writes[0].pNext = &asWrite;
671 writes[0].dstSet = descriptorSet_;
672 writes[0].dstBinding = 0;
673 writes[0].descriptorCount = 1;
674 writes[0].descriptorType = vk::DescriptorType::eAccelerationStructureKHR;
675
676 // Storage image for output — sampler unused
677 vk::DescriptorImageInfo outInfo{};
678 outInfo.imageView = outputView;
679 outInfo.imageLayout = vk::ImageLayout::eGeneral;
680 writes[1].dstSet = descriptorSet_;
681 writes[1].dstBinding = 1;
682 writes[1].descriptorCount = 1;
683 writes[1].descriptorType = vk::DescriptorType::eStorageImage;
684 writes[1].pImageInfo = &outInfo;
685
686 if (auto r = ensureSampler(); !r.ok()) return r;
687
688 vk::DescriptorImageInfo sceneInfo{};
689 sceneInfo.sampler = sampler_;
690 sceneInfo.imageView = sceneView;
691 sceneInfo.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
692 writes[2].dstSet = descriptorSet_;
693 writes[2].dstBinding = 2;
694 writes[2].descriptorCount = 1;
695 writes[2].descriptorType = vk::DescriptorType::eCombinedImageSampler;
696 writes[2].pImageInfo = &sceneInfo;
697
698 vk::DescriptorImageInfo depthInfo{};
699 depthInfo.sampler = sampler_;
700 depthInfo.imageView = depthView;
701 depthInfo.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
702 writes[3].dstSet = descriptorSet_;
703 writes[3].dstBinding = 3;
704 writes[3].descriptorCount = 1;
705 writes[3].descriptorType = vk::DescriptorType::eCombinedImageSampler;
706 writes[3].pImageInfo = &depthInfo;
707
708 vk::DescriptorImageInfo normalInfo{};
709 normalInfo.sampler = sampler_;
710 normalInfo.imageView = normalView;
711 normalInfo.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
712 writes[4].dstSet = descriptorSet_;
713 writes[4].dstBinding = 4;
714 writes[4].descriptorCount = 1;
715 writes[4].descriptorType = vk::DescriptorType::eCombinedImageSampler;
716 writes[4].pImageInfo = &normalInfo;
717
718 (*device_)->updateDescriptorSets(writes, {});
719 return Result<void>::success();
720}
721
722Result<void> VulkanRayTracing::ensureSampler() {
723 if (sampler_) return Result<void>::success();
724 if (!device_) return Result<void>::failure(Diagnostic::error(
725 DiagnosticCode::Unsupported, std::string("ensureSampler") + ": hardware ray tracing unavailable", "graphics.raytracing"));
726 vk::SamplerCreateInfo sci{};
727 sci.magFilter = vk::Filter::eNearest;
728 sci.minFilter = vk::Filter::eNearest;
729 sci.mipmapMode = vk::SamplerMipmapMode::eNearest;
730 sci.addressModeU = vk::SamplerAddressMode::eClampToEdge;
731 sci.addressModeV = vk::SamplerAddressMode::eClampToEdge;
732 sci.addressModeW = vk::SamplerAddressMode::eClampToEdge;
733 sampler_ = (*device_)->createSampler(sci, device_->allocation_callbacks);
734 return Result<void>::success();
735}
736
737Result<void> VulkanRayTracing::ensureOutputImage(uint32_t width, uint32_t height) {
738 if (!device_) return Result<void>::failure(Diagnostic::error(
739 DiagnosticCode::Unsupported, std::string("ensureOutputImage") + ": hardware ray tracing unavailable", "graphics.raytracing"));
740 if (width == 0 || height == 0) return Result<void>::failure(
741 Diagnostic::error(DiagnosticCode::InvalidArgument, std::string("ensureOutputImage") + ": " + "zero extent", "graphics.raytracing"));
742 if (outputImage_.image() && outputWidth_ == width && outputHeight_ == height) return Result<void>::success();
743 outputImage_ = StorageColorImage{};
744 outputWidth_ = width;
745 outputHeight_ = height;
746 outputImage_.allocate(*device_, width, height);
747 return Result<void>::success();
748}
749
751 Texture* worldNormal, Canvas* dest, const glm::mat4& invViewProj,
752 const glm::mat4& viewProj, const glm::vec3& eyeWorld) {
753 if (auto attached = ensureAttached(); !attached.ok()) return attached;
754 if (!gfx || !sceneColor || !hwDepth || !worldNormal || !dest)
756 Diagnostic::error(DiagnosticCode::InvalidArgument, std::string("applyReflections") + ": " + "null argument", "graphics.raytracing"));
757 if (meshes_.empty() || !tlas_.handle) {
758 // Empty scene: no usable RT output — callers must fall through to SSR.
760 }
761
762 auto* vkGfx = dynamic_cast<eve::graphics::vulkan::Graphics*>(gfx);
763 if (!vkGfx) return Result<void>::failure(
764 Diagnostic::error(DiagnosticCode::Failed, std::string("applyReflections") + ": " + "requires Vulkan Graphics backend", "graphics.raytracing"));
765
766 if (auto r = ensurePipeline(); !r.ok()) return r;
767
768 auto textureView = [](Texture* tex) -> vk::ImageView {
769 if (!tex || !tex->gpuHandle) return {};
770 auto* img = static_cast<eve::graphics::vulkan::GpuTexture*>(tex->gpuHandle);
771 return img->imageView();
772 };
773
774 Texture* destTex = dest->getTexture();
775 if (!destTex) return Result<void>::failure(
776 Diagnostic::error(DiagnosticCode::Failed, std::string("applyReflections") + ": " + "destination canvas has no texture", "graphics.raytracing"));
777 auto* destCanvas = dynamic_cast<eve::graphics::vulkan::OffscreenCanvas*>(dest);
778 if (!destCanvas) return Result<void>::failure(
779 Diagnostic::error(DiagnosticCode::Failed, std::string("applyReflections") + ": " + "destination must be an OffscreenCanvas", "graphics.raytracing"));
780
781 const uint32_t width = uint32_t(dest->getWidth());
782 const uint32_t height = uint32_t(dest->getHeight());
783 if (auto r = ensureOutputImage(width, height); !r.ok()) return r;
784
785 const vk::ImageView outView = outputImage_.imageView();
786 const vk::ImageView sceneView = textureView(sceneColor);
787 const vk::ImageView depthView = textureView(hwDepth);
788 const vk::ImageView normalView = textureView(worldNormal);
789 if (!outView || !sceneView || !depthView || !normalView)
791 Diagnostic::error(DiagnosticCode::Failed, std::string("applyReflections") + ": " + "missing texture image view", "graphics.raytracing"));
792
793 if (auto r = ensureDescriptorSets(outView, sceneView, depthView, normalView); !r.ok()) return r;
794
795 PushConstants push{};
796 push.invViewProj = invViewProj;
797 push.viewProj = viewProj;
798 push.eye = glm::vec4(eyeWorld, 0.f);
799
800 auto record = [&](vk::CommandBuffer cmd) {
801 outputImage_.setLayout(cmd, vk::ImageLayout::eGeneral);
802
803 vk::MemoryBarrier memBarrier{};
804 memBarrier.srcAccessMask =
805 vk::AccessFlagBits::eColorAttachmentWrite | vk::AccessFlagBits::eDepthStencilAttachmentWrite;
806 memBarrier.dstAccessMask = vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite;
807 cmd.pipelineBarrier(
808 vk::PipelineStageFlagBits::eColorAttachmentOutput | vk::PipelineStageFlagBits::eLateFragmentTests,
809 vk::PipelineStageFlagBits::eRayTracingShaderKHR, {}, memBarrier, {}, {});
810
811 cmd.bindPipeline(vk::PipelineBindPoint::eRayTracingKHR, pipeline_);
812 cmd.bindDescriptorSets(vk::PipelineBindPoint::eRayTracingKHR, pipelineLayout_, 0, descriptorSet_, {});
813 cmd.pushConstants(pipelineLayout_, vk::ShaderStageFlagBits::eRaygenKHR, 0, sizeof(PushConstants), &push);
814 cmd.traceRaysKHR(raygenRegion_, missRegion_, hitRegion_, callableRegion_, width, height, 1);
815
816 // Copy RT storage result into the reflection canvas for composite sampling.
817 outputImage_.setLayout(cmd, vk::ImageLayout::eTransferSrcOptimal);
818 destCanvas->colorImage().setLayout(cmd, vk::ImageLayout::eTransferDstOptimal);
819 vk::ImageCopy region{};
820 region.srcSubresource = {vk::ImageAspectFlagBits::eColor, 0, 0, 1};
821 region.dstSubresource = {vk::ImageAspectFlagBits::eColor, 0, 0, 1};
822 region.extent = vk::Extent3D{width, height, 1};
823 cmd.copyImage(outputImage_.image(), vk::ImageLayout::eTransferSrcOptimal, destCanvas->colorImage().image(),
824 vk::ImageLayout::eTransferDstOptimal, region);
825 destCanvas->colorImage().setLayout(cmd, vk::ImageLayout::eShaderReadOnlyOptimal);
826 };
827
828 if (vkGfx->canRecordPostSceneGpuWork()) {
829 record(vkGfx->postSceneCommandBuffer());
830 } else {
831 vkGfx->waitForSharedGpuResources();
832 vkb::executeImmediately(device_->instance, uploadPool_, graphicsQueue_, record);
833 }
834 return Result<void>::success();
835}
836
837namespace {
838
839struct RegisterCapability {
840 RegisterCapability() { eve::cap::provide<IRayTracing>(&vulkanRayTracing()); }
841} g_register;
842
843} // namespace
844
845} // namespace eve::graphics::raytracing
double value
std::string usage
std::vector< std::uint32_t > verts
Definition Builder.cpp:27
std::vector< BuildingInstanceSnapshot > instances
Stable, structured diagnostics shared by engine modules.
std::uint32_t vertexCount
std::uint32_t indexCount
vkb::Device & device
vk::UniqueDeviceMemory memory
float u
Definition Grass.cpp:233
double r
std::vector< std::uint32_t > indices
std::vector< float > positions
std::uint32_t height
std::uint32_t width
Range range
std::uint64_t bytes
std::vector< TriangleRef > triangles
uint32_t groups
Definition OnnxGpgpu.cpp:39
std::vector< std::shared_ptr< DeviceBytes > > bindings
Definition OnnxGpgpu.cpp:38
std::unique_ptr< gpgpu::GpuBuffer > buffer
Definition OnnxGpgpu.cpp:26
PrimitiveHandle handle
Mesh * mesh
glm::mat4 viewProj
int created
double current
std::uint32_t count
Structured operation status used by the common Result foundation.
float size
Definition TreeMesh.cpp:156
float m[16]
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
bool ok() const noexcept
Whether this result represents a non-failure outcome.
Definition Result.h:480
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
static Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
Definition Status.h:81
Canvas public API.
Definition Canvas.h:17
virtual int getWidth() const =0
Returns the width.
virtual Texture * getTexture()=0
Sampleable color buffer; screen Canvas returns nullptr.
virtual int getHeight() const =0
Returns the height.
GPU mesh handle (+ optional CPU morph targets).
Definition Mesh.h:25
GPU texture created via Graphics::newTexture. Owns GPU resources through an opaque backend handle.
Definition Texture.h:18
Vulkan implementation of IRayTracing (KHR ray-tracing pipeline).
Result< void > rebuildScene() override
Rebuild scene.
~VulkanRayTracing() override
Releases VulkanRayTracing resources.
Result< uint32_t > addMesh(Mesh *mesh, const glm::mat4 &transform) override
Adds mesh.
RayTracingCaps caps() const override
Caps.
void attachDevice(vkb::Device *device, const RayTracingCaps &caps, vk::CommandPool uploadPool, vk::Queue graphicsQueue)
Bind to the live Vulkan Graphics device (call after init). @ownership device is borrowed; Graphics re...
Result< void > ensureAttached()
Lazily bind to the process Graphics singleton when RT is available.
bool isAvailable() const override
True when available.
void detachDevice()
Drop GPU resources when Graphics tears down.
Result< uint32_t > addTriangleMesh(const float *positionsXYZ, int vertexCount, const uint32_t *indices, int indexCount, const glm::mat4 &transform) override
Adds triangle mesh.
Result< void > applyReflections(Graphics *gfx, Texture *sceneColor, Texture *hwDepth, Texture *worldNormal, Canvas *dest, const glm::mat4 &invViewProj, const glm::mat4 &viewProj, const glm::vec3 &eyeWorld) override
Applies reflections.
EVENGINE_API_BACKENDS public API.
Definition Graphics.h:342
bool supportsRayTracing() const override
Supports ray tracing.
Definition Graphics.h:422
RayTracingCaps rayTracingCaps() const override
Ray tracing caps.
Definition Graphics.h:420
OffscreenCanvas public API.
Definition Canvas.h:15
VulkanRayTracing & vulkanRayTracing()
Process-wide Vulkan RT backend; also registered as IRayTracing.
std::string buildInfo()
One-line build metadata for diagnostics (printed by eve --version).
Definition Version.cpp:20
Hardware ray-tracing capabilities probed once at Vulkan device creation.
bool rayTracingAvailable() const
True when BLAS/TLAS + ray-tracing pipelines can be created.
Bottom- or top-level acceleration structure handle.
void steal(AccelerationStructure &o) noexcept
Steal.
Device-local buffer with shader device address (for AS builds / SBT). @ownership Owns the Vulkan buff...
Result< void > allocate(vkb::Device &dev, vk::DeviceSize bytes, vk::BufferUsageFlags usage)
Allocate a buffer usage that includes SHADER_DEVICE_ADDRESS.
Result< void > upload(vk::CommandPool pool, vk::Queue queue, const void *data, vk::DeviceSize bytes)
Upload host bytes via a staging buffer (stalls the queue).
void steal(DeviceAddressBuffer &o) noexcept
Steal.
GpuMesh public API.
Definition Graphics.h:289
GpuTexture public API.
Definition Graphics.h:257
vk::ImageView imageView() const
Image view.
Definition Graphics.h:280
MeshVertex public API.
Definition Graphics.h:141
glm::mat4 invViewProj