Skip to content

Commit 6740ddd

Browse files
committed
Windows should now work completely.
Add material index support and optimize ray query paths - Introduced `materialIndex` in `InstanceData` to streamline per-instance material assignments in ray queries. - Enhanced Vulkan descriptor bindings, adding new storage buffers for material and geometry data (Bindings 12 and 13). - Extended `ModelLoader` with material index mapping and lookup functions for improved glTF material handling. - Implemented robust material index resolution logic and authoritative mappings in TLAS build paths. - Improved handling of alpha-masked and environment objects with dynamic ray-query classifications. - Optimized geometry info and material buffer generation for better consistency in ray tracing workflows. - Addressed descriptor flag size mismatches and resolved instances of unsafe type casting in Vulkan resource bindings.
1 parent b4b52a5 commit 6740ddd

18 files changed

+1053
-866
lines changed

attachments/simple_engine/engine.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -906,6 +906,10 @@ void Engine::ProcessPendingBalls() {
906906
// Set bounciness from material
907907
rigidBody->SetRestitution(ballMaterial.bounciness);
908908

909+
// Request an acceleration structure build so the new ball is included in Ray Query mode.
910+
// We do this after creating the rigid body and initializing the entity.
911+
renderer->RequestAccelerationStructureBuild("Ball spawned");
912+
909913
// Apply throw force and spin
910914
glm::vec3 throwImpulse = pendingBall.throwDirection * pendingBall.throwForce;
911915
rigidBody->ApplyImpulse(throwImpulse, glm::vec3(0.0f));

attachments/simple_engine/imgui_system.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -208,8 +208,6 @@ void ImGuiSystem::NewFrame() {
208208
ImGui::PopStyleVar();
209209
}
210210
ImGui::End();
211-
ImGui::Render();
212-
frameAlreadyRendered = true;
213211
return;
214212
}
215213
}
@@ -1034,7 +1032,7 @@ void ImGuiSystem::updateBuffers(uint32_t frameIndex) {
10341032
if (frameIndex >= vertexCounts.size())
10351033
return; // Safety
10361034

1037-
if (drawData->TotalVtxCount > vertexCounts[frameIndex]) {
1035+
if (static_cast<uint32_t>(drawData->TotalVtxCount) > vertexCounts[frameIndex]) {
10381036
// Clean up old buffer
10391037
vertexBuffers[frameIndex] = vk::raii::Buffer(nullptr);
10401038
vertexBufferMemories[frameIndex] = vk::raii::DeviceMemory(nullptr);
@@ -1059,7 +1057,7 @@ void ImGuiSystem::updateBuffers(uint32_t frameIndex) {
10591057
vertexCounts[frameIndex] = drawData->TotalVtxCount;
10601058
}
10611059

1062-
if (drawData->TotalIdxCount > indexCounts[frameIndex]) {
1060+
if (static_cast<uint32_t>(drawData->TotalIdxCount) > indexCounts[frameIndex]) {
10631061
// Clean up old buffer
10641062
indexBuffers[frameIndex] = vk::raii::Buffer(nullptr);
10651063
indexBufferMemories[frameIndex] = vk::raii::DeviceMemory(nullptr);

attachments/simple_engine/mesh_component.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,16 @@ struct InstanceData {
3838
// Normal matrix as glm::mat3x4 (3 columns of vec4: xyz = normal matrix columns, w unused)
3939
glm::mat3x4 normalMatrix{};
4040

41+
// Material index for this instance
42+
uint32_t materialIndex{0};
43+
4144
InstanceData() {
4245
// Initialize as identity matrices
4346
modelMatrix = glm::mat4(1.0f);
4447
normalMatrix[0] = glm::vec4(1.0f, 0.0f, 0.0f, 0.0f);
4548
normalMatrix[1] = glm::vec4(0.0f, 1.0f, 0.0f, 0.0f);
4649
normalMatrix[2] = glm::vec4(0.0f, 0.0f, 1.0f, 0.0f);
50+
materialIndex = 0;
4751
}
4852

4953
explicit InstanceData(const glm::mat4& transform, uint32_t matIndex = 0) {
@@ -56,7 +60,7 @@ struct InstanceData {
5660
normalMatrix[1] = glm::vec4(normalMat3[1], 0.0f);
5761
normalMatrix[2] = glm::vec4(normalMat3[2], 0.0f);
5862

59-
// Note: matIndex parameter ignored since materialIndex field was removed
63+
materialIndex = matIndex;
6064
}
6165

6266
// Helper methods for backward compatibility

attachments/simple_engine/model_loader.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,10 @@ static bool LoadKTX2Image(tinygltf::Image* image,
222222
void ModelLoader::ProcessMaterials(const tinygltf::Model& gltfModel,
223223
const std::string& baseTexturePath,
224224
std::set<std::string>& loadedTextures) {
225+
// Build/refresh an index -> material mapping that matches glTF material indices.
226+
materialsByIndex.clear();
227+
materialsByIndex.resize(gltfModel.materials.size(), nullptr);
228+
225229
// Process materials first
226230
for (size_t i = 0; i < gltfModel.materials.size(); ++i) {
227231
const auto& gltfMaterial = gltfModel.materials[i];
@@ -570,7 +574,11 @@ void ModelLoader::ProcessMaterials(const tinygltf::Model& gltfModel,
570574
}
571575

572576
// Store the material
577+
Material* rawPtr = material.get();
573578
materials[material->GetName()] = std::move(material);
579+
if (i < materialsByIndex.size()) {
580+
materialsByIndex[i] = rawPtr;
581+
}
574582
}
575583

576584
// Handle KHR_materials_pbrSpecularGlossiness.diffuseTexture for baseColor when still missing
@@ -1617,7 +1625,7 @@ bool ModelLoader::ParseGLTF(const std::string& filename, Model* model) {
16171625
}
16181626

16191627
for (uint32_t index : materialMesh.indices) {
1620-
combinedIndices.push_back(index + vertexOffset);
1628+
combinedIndices.push_back(index + static_cast<uint32_t>(vertexOffset));
16211629
}
16221630
}
16231631
}
@@ -1785,6 +1793,13 @@ const Material* ModelLoader::GetMaterial(const std::string& materialName) const
17851793
return nullptr;
17861794
}
17871795

1796+
const Material* ModelLoader::GetMaterialByIndex(uint32_t materialIndex) const {
1797+
if (materialIndex < materialsByIndex.size()) {
1798+
return materialsByIndex[materialIndex];
1799+
}
1800+
return nullptr;
1801+
}
1802+
17881803
const std::vector<Animation>& ModelLoader::GetAnimations(const std::string& modelName) const {
17891804
auto it = models.find(modelName);
17901805
if (it != models.end() && it->second) {

attachments/simple_engine/model_loader.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,13 @@ class ModelLoader {
375375
*/
376376
const Material* GetMaterial(const std::string& materialName) const;
377377

378+
/**
379+
* @brief Get a material by its glTF material index.
380+
* @param materialIndex The material index as used by glTF primitives/instances.
381+
* @return Pointer to the material, or nullptr if out of range / not available.
382+
*/
383+
const Material* GetMaterialByIndex(uint32_t materialIndex) const;
384+
378385
/**
379386
* @brief Get animations from a loaded model.
380387
* @param modelName The name of the model.
@@ -441,6 +448,9 @@ class ModelLoader {
441448
// Loaded materials
442449
std::unordered_map<std::string, std::unique_ptr<Material>> materials;
443450

451+
// Mapping from glTF material index -> Material pointer (rebuilt on each model load).
452+
std::vector<Material*> materialsByIndex;
453+
444454
// Extracted lights per model
445455
std::unordered_map<std::string, std::vector<ExtractedLight>> extractedLights;
446456

attachments/simple_engine/physics_system.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1276,7 +1276,7 @@ void PhysicsSystem::SimulatePhysicsOnGPU(const std::chrono::milliseconds deltaTi
12761276

12771277
// Step 1: Integrate forces and velocities
12781278
vulkanResources.commandBuffer.bindPipeline(vk::PipelineBindPoint::eCompute, *vulkanResources.integratePipeline);
1279-
vulkanResources.commandBuffer.dispatch((rigidBodies.size() + 63) / 64, 1, 1);
1279+
vulkanResources.commandBuffer.dispatch(static_cast<uint32_t>((rigidBodies.size() + 63) / 64), 1, 1);
12801280

12811281
// Memory barrier to ensure integration is complete before collision detection
12821282
vk::MemoryBarrier memoryBarrier;
@@ -1293,7 +1293,7 @@ void PhysicsSystem::SimulatePhysicsOnGPU(const std::chrono::milliseconds deltaTi
12931293

12941294
// Step 2: Broad-phase collision detection
12951295
vulkanResources.commandBuffer.bindPipeline(vk::PipelineBindPoint::eCompute, *vulkanResources.broadPhasePipeline);
1296-
uint32_t numPairs = (rigidBodies.size() * (rigidBodies.size() - 1)) / 2;
1296+
uint32_t numPairs = static_cast<uint32_t>((rigidBodies.size() * (rigidBodies.size() - 1)) / 2);
12971297
// Dispatch number of workgroups matching [numthreads(64,1,1)] in BroadPhaseCS
12981298
// One workgroup has 64 threads, each processes one pair by index
12991299
uint32_t broadPhaseThreads = (numPairs + 63) / 64;

attachments/simple_engine/renderdoc_debug_system.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
#include <iostream>
2121

2222
#if defined(_WIN32)
23-
# define WIN32_LEAN_AND_MEAN
23+
# ifndef WIN32_LEAN_AND_MEAN
24+
# define WIN32_LEAN_AND_MEAN
25+
# endif
2426
# include <windows.h>
2527
#elif defined(__APPLE__) || defined(__linux__)
2628
# include <dlfcn.h>

attachments/simple_engine/renderer.h

Lines changed: 66 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,15 @@ struct LightData {
9696
alignas(4) float outerConeAngle; // For spotlights
9797
};
9898

99+
struct ShadowUniforms {
100+
alignas(16) glm::mat4 view;
101+
alignas(16) glm::mat4 proj;
102+
};
103+
104+
struct ShadowPushConstants {
105+
alignas(16) glm::mat4 model;
106+
};
107+
99108
/**
100109
* @brief Structure for the uniform buffer object (now without fixed light arrays).
101110
*/
@@ -598,15 +607,9 @@ class Renderer {
598607
}
599608
}
600609

601-
// Descriptor set deferred update machinery
602-
void MarkEntityDescriptorsDirty(Entity* entity);
603-
void ProcessDirtyDescriptorsForFrame(uint32_t frameIndex);
604-
bool updateDescriptorSetsForFrame(Entity* entity,
605-
const std::string& texturePath,
606-
bool usePBR,
607-
uint32_t frameIndex,
608-
bool imagesOnly = false,
609-
bool uboOnly = false);
610+
// Descriptor set deferred update machinery
611+
void MarkEntityDescriptorsDirty(Entity *entity);
612+
void ProcessDirtyDescriptorsForFrame(uint32_t frameIndex);
610613

611614
// Texture aliasing: map canonical IDs to actual loaded keys (e.g., file paths) to avoid duplicates
612615
inline void RegisterTextureAlias(const std::string& aliasId, const std::string& targetId) {
@@ -822,10 +825,16 @@ class Renderer {
822825
.count());
823826
asBuildRequestStartNs.store(nowNs, std::memory_order_relaxed);
824827
}
825-
if (reason)
828+
if (reason) {
826829
lastASBuildRequestReason = reason;
827-
else
830+
std::cout << "[AS] Requesting rebuild. Reason: " << reason << std::endl;
831+
} else {
828832
lastASBuildRequestReason = "(no reason)";
833+
}
834+
835+
// Explicit requests bypass the freeze to ensure dynamic objects (like balls) are added
836+
asDevOverrideAllowRebuild = true;
837+
829838
watchdogSuppressed.store(true, std::memory_order_relaxed);
830839
asBuildRequested.store(true, std::memory_order_release);
831840
}
@@ -860,7 +869,7 @@ class Renderer {
860869
* @param lights The light data to upload.
861870
* @return True if successful, false otherwise.
862871
*/
863-
bool updateLightStorageBuffer(uint32_t frameIndex, const std::vector<ExtractedLight>& lights);
872+
bool updateLightStorageBuffer(uint32_t frameIndex, const std::vector<ExtractedLight>& lights, CameraComponent* camera = nullptr);
864873

865874
/**
866875
* @brief Update all existing descriptor sets with new light storage buffer references.
@@ -1514,13 +1523,24 @@ class Renderer {
15141523
bool materialCacheValid = false;
15151524
const Material* cachedMaterial = nullptr;
15161525
// Derived flags used by render queues and sorting heuristics
1517-
bool cachedIsBlended = false;
1518-
bool cachedIsGlass = false;
1519-
bool cachedIsLiquid = false;
1520-
// Material-derived push constants defaults (static per-entity unless material changes)
1521-
MaterialProperties cachedMaterialProps{};
1522-
};
1523-
std::unordered_map<Entity *, EntityResources> entityResources;
1526+
bool cachedIsBlended = false;
1527+
bool cachedIsGlass = false;
1528+
bool cachedIsLiquid = false;
1529+
// Material-derived push constants defaults (static per-entity unless material changes)
1530+
MaterialProperties cachedMaterialProps{};
1531+
};
1532+
1533+
// Cached job for rendering a single entity in a frame
1534+
struct RenderJob
1535+
{
1536+
Entity *entity;
1537+
EntityResources *entityRes;
1538+
MeshResources *meshRes;
1539+
MeshComponent *meshComp;
1540+
TransformComponent *transformComp;
1541+
bool isAlphaMasked;
1542+
};
1543+
std::unordered_map<Entity *, EntityResources> entityResources;
15241544

15251545
// Descriptor pool (declared after entity resources to ensure proper destruction order)
15261546
vk::raii::DescriptorPool descriptorPool = nullptr;
@@ -1639,7 +1659,7 @@ class Renderer {
16391659
const uint32_t MAX_FRAMES_IN_FLIGHT = 2u;
16401660

16411661
// --- Performance & diagnostics ---
1642-
// CPU-side frustum culling toggle and last-frame stats
1662+
UniformBufferObject frameUboTemplate{};
16431663
bool enableFrustumCulling = true;
16441664
uint32_t lastCullingVisibleCount = 0;
16451665
uint32_t lastCullingCulledCount = 0;
@@ -1756,12 +1776,26 @@ class Renderer {
17561776
bool createUniformBuffers(Entity* entity);
17571777
bool createDescriptorPool();
17581778
bool createDescriptorSets(Entity* entity, const std::string& texturePath, bool usePBR = false);
1759-
// Refresh only the currentFrame PBR descriptor set bindings that Forward+ relies on
1760-
// (b6 = lights SSBO, b7 = tile headers, b8 = tile indices). Safe to call after
1761-
// we've waited on the frame fence at the start of Render().
1762-
void refreshPBRForwardPlusBindingsForFrame(uint32_t frameIndex);
1763-
bool createCommandBuffers();
1764-
bool createSyncObjects();
1779+
bool createDescriptorSets(Entity *entity, EntityResources &res, const std::string &texturePath, bool usePBR = false);
1780+
bool updateDescriptorSetsForFrame(Entity *entity,
1781+
const std::string &texturePath,
1782+
bool usePBR,
1783+
uint32_t frameIndex,
1784+
bool imagesOnly = false,
1785+
bool uboOnly = false);
1786+
bool updateDescriptorSetsForFrame(Entity *entity,
1787+
EntityResources &res,
1788+
const std::string &texturePath,
1789+
bool usePBR,
1790+
uint32_t frameIndex,
1791+
bool imagesOnly = false,
1792+
bool uboOnly = false);
1793+
// Refresh only the currentFrame PBR descriptor set bindings that Forward+ relies on
1794+
// (b6 = lights SSBO, b7 = tile headers, b8 = tile indices). Safe to call after
1795+
// we've waited on the frame fence at the start of Render().
1796+
void refreshPBRForwardPlusBindingsForFrame(uint32_t frameIndex);
1797+
bool createCommandBuffers();
1798+
bool createSyncObjects();
17651799

17661800
void cleanupSwapChain();
17671801

@@ -1772,14 +1806,14 @@ class Renderer {
17721806
void renderReflectionPass(vk::raii::CommandBuffer& cmd,
17731807
const glm::vec4& planeWS,
17741808
CameraComponent* camera,
1775-
const std::vector<Entity *>& entities);
1809+
const std::vector<RenderJob> &jobs);
17761810

17771811
// Ensure Vulkan-Hpp dispatcher is initialized for the current thread when using RAII objects on worker threads
17781812
void ensureThreadLocalVulkanInit() const;
17791813

17801814
// Cache and classify an entity's material for raster rendering (opaque vs blended, glass/liquid flags,
17811815
// and push-constant defaults). This avoids repeated per-frame string parsing and material lookups.
1782-
void ensureEntityMaterialCache(Entity* entity);
1816+
void ensureEntityMaterialCache(Entity* entity, EntityResources &res);
17831817

17841818
// ===================== Culling helpers =====================
17851819
struct FrustumPlanes {
@@ -1800,9 +1834,10 @@ class Renderer {
18001834
const FrustumPlanes& frustum);
18011835
void recreateSwapChain();
18021836

1803-
void updateUniformBuffer(uint32_t currentImage, Entity* entity, CameraComponent* camera);
1804-
void updateUniformBuffer(uint32_t currentImage, Entity* entity, CameraComponent* camera, const glm::mat4& customTransform);
1805-
void updateUniformBufferInternal(uint32_t currentImage, Entity* entity, CameraComponent* camera, UniformBufferObject& ubo);
1837+
void updateUniformBuffer(uint32_t currentImage, Entity* entity, EntityResources *entityRes, CameraComponent* camera, TransformComponent *tc = nullptr);
1838+
void updateUniformBuffer(uint32_t currentImage, Entity* entity, EntityResources *entityRes, CameraComponent* camera, const glm::mat4& customTransform);
1839+
void updateUniformBufferInternal(uint32_t currentImage, Entity* entity, EntityResources *entityRes, CameraComponent* camera, UniformBufferObject& ubo);
1840+
void prepareFrameUboTemplate(CameraComponent *camera);
18061841

18071842
vk::raii::ShaderModule createShaderModule(const std::vector<char>& code);
18081843

attachments/simple_engine/renderer_pipelines.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,13 +172,29 @@ bool Renderer::createPBRDescriptorSetLayout() {
172172
.descriptorCount = 1,
173173
.stageFlags = vk::ShaderStageFlagBits::eFragment,
174174
.pImmutableSamplers = nullptr
175+
},
176+
// Binding 12: Ray-query geometry info buffer (per-instance addresses + material indices)
177+
vk::DescriptorSetLayoutBinding{
178+
.binding = 12,
179+
.descriptorType = vk::DescriptorType::eStorageBuffer,
180+
.descriptorCount = 1,
181+
.stageFlags = vk::ShaderStageFlagBits::eFragment,
182+
.pImmutableSamplers = nullptr
183+
},
184+
// Binding 13: Ray-query material buffer (PBR material properties)
185+
vk::DescriptorSetLayoutBinding{
186+
.binding = 13,
187+
.descriptorType = vk::DescriptorType::eStorageBuffer,
188+
.descriptorCount = 1,
189+
.stageFlags = vk::ShaderStageFlagBits::eFragment,
190+
.pImmutableSamplers = nullptr
175191
}
176192
};
177193

178194
// Create a descriptor set layout
179195
// Descriptor indexing: set per-binding flags for UPDATE_AFTER_BIND on UBO (0) and sampled images (1..5)
180196
vk::DescriptorSetLayoutBindingFlagsCreateInfo bindingFlagsInfo{};
181-
std::array<vk::DescriptorBindingFlags, 12> bindingFlags{};
197+
std::array<vk::DescriptorBindingFlags, 14> bindingFlags{};
182198
if (descriptorIndexingEnabled) {
183199
bindingFlags[0] = vk::DescriptorBindingFlagBits::eUpdateAfterBind | vk::DescriptorBindingFlagBits::eUpdateUnusedWhilePending;
184200
bindingFlags[1] = vk::DescriptorBindingFlagBits::eUpdateAfterBind | vk::DescriptorBindingFlagBits::eUpdateUnusedWhilePending;

0 commit comments

Comments
 (0)