diff --git a/destrum/CMakeLists.txt b/destrum/CMakeLists.txt index 2e1d146..32b05a4 100644 --- a/destrum/CMakeLists.txt +++ b/destrum/CMakeLists.txt @@ -50,6 +50,7 @@ set(SRC_FILES "src/Graphics/Pipelines/QuadRendererPass.cpp" "src/Graphics/Pipelines/SkyboxPipeline.cpp" "src/Graphics/Pipelines/LightCullingPipeline.cpp" + "src/Graphics/Pipelines/ShadowMapPipeline.cpp" "src/Graphics/Pipelines/SkinningPipeline.cpp" "src/Graphics/Pipelines/ImguiPass.cpp" diff --git a/destrum/assets_src/shaders/bindless.glsl b/destrum/assets_src/shaders/bindless.glsl index 74da315..14d0f2a 100644 --- a/destrum/assets_src/shaders/bindless.glsl +++ b/destrum/assets_src/shaders/bindless.glsl @@ -35,6 +35,10 @@ vec4 sampleTextureCubeLinear(uint texID, vec3 p) { return texture(nonuniformEXT(samplerCube(textureCubes[texID], samplers[LINEAR_SAMPLER_ID])), p); } +float sampleTexture2DShadow(uint texID, vec3 p) { + return texture(nonuniformEXT(sampler2DShadow(textures[texID], samplers[SHADOW_SAMPLER_ID])), p); +} + float sampleTextureArrayShadow(uint texID, vec4 p) { return texture(nonuniformEXT(sampler2DArrayShadow(textureArrays[texID], samplers[SHADOW_SAMPLER_ID])), p); } diff --git a/destrum/assets_src/shaders/lighting.glsl b/destrum/assets_src/shaders/lighting.glsl index 0e5a322..6b5f95e 100644 --- a/destrum/assets_src/shaders/lighting.glsl +++ b/destrum/assets_src/shaders/lighting.glsl @@ -44,7 +44,7 @@ float calculateSpotAttenuation(Light light, vec3 l) } vec3 calculateLight(Light light, vec3 fragPos, vec3 n, vec3 v, - vec3 diffuseColor, float roughness, float metallic, vec3 f0) + vec3 diffuseColor, float roughness, float metallic, vec3 f0, float occlusion) { vec3 l; float attenuation = 1.0; @@ -80,7 +80,7 @@ vec3 calculateLight(Light light, vec3 fragPos, vec3 n, vec3 v, vec3 kd = (vec3(1.0) - F) * (1.0 - metallic); vec3 diffuse = kd * diffuseColor / PI; - return (diffuse + specular) * light.color * light.intensity * attenuation * NoL; + return (diffuse + specular) * light.color * light.intensity * attenuation * NoL * occlusion; } // blue -> green -> red, used to visualise how many lights a tile has diff --git a/destrum/assets_src/shaders/lights.glsl b/destrum/assets_src/shaders/lights.glsl index bc26b82..1f269e2 100644 --- a/destrum/assets_src/shaders/lights.glsl +++ b/destrum/assets_src/shaders/lights.glsl @@ -27,7 +27,8 @@ struct Light { float spotInnerCos; float spotOuterCos; - vec2 padding; + uint castShadows; + float padding; }; layout (buffer_reference, std430) readonly buffer LightsBuffer { diff --git a/destrum/assets_src/shaders/mesh.frag b/destrum/assets_src/shaders/mesh.frag index 397be3c..c971066 100644 --- a/destrum/assets_src/shaders/mesh.frag +++ b/destrum/assets_src/shaders/mesh.frag @@ -6,6 +6,7 @@ #include "scene_data.glsl" #include "mesh_pcs.glsl" #include "lighting.glsl" +#include "shadows.glsl" layout (location = 0) in vec3 inPos; layout (location = 1) in vec2 inUV; @@ -69,8 +70,17 @@ void main() uint lightIndex = pcs.sceneData.tileLightIndices.data[tileOffset + 1 + i]; Light light = pcs.sceneData.lights.data[lightIndex]; + float occlusion = 1.0; + if (light.castShadows != 0) { + occlusion = calculateDirectionalShadow( + fragPos, n, + pcs.sceneData.lightViewProj, + pcs.sceneData.shadowMapId, + pcs.sceneData.shadowNormalOffset); + } + fragColor += calculateLight(light, fragPos, n, v, - diffuseColor, roughness, metallic, f0); + diffuseColor, roughness, metallic, f0, occlusion); } // emissive diff --git a/destrum/assets_src/shaders/scene_data.glsl b/destrum/assets_src/shaders/scene_data.glsl index 251241e..54d62ca 100644 --- a/destrum/assets_src/shaders/scene_data.glsl +++ b/destrum/assets_src/shaders/scene_data.glsl @@ -33,7 +33,12 @@ layout (buffer_reference, scalar) readonly buffer SceneDataBuffer { uint numLights; uint tileCountX; uint debugLightHeatmap; + + // directional shadows + uint shadowMapId; + float shadowNormalOffset; uint padding; + mat4 lightViewProj; } sceneDataBuffer; #endif // SCENE_DATA_GLSL diff --git a/destrum/assets_src/shaders/shadow.vert b/destrum/assets_src/shaders/shadow.vert new file mode 100644 index 0000000..667caae --- /dev/null +++ b/destrum/assets_src/shaders/shadow.vert @@ -0,0 +1,12 @@ +#version 460 + +#extension GL_GOOGLE_include_directive : require + +#include "mesh_pcs.glsl" + +void main() +{ + Vertex v = pcs.vertexBuffer.vertices[gl_VertexIndex]; + + gl_Position = pcs.sceneData.lightViewProj * pcs.transform * vec4(v.position, 1.0f); +} diff --git a/destrum/assets_src/shaders/shadows.glsl b/destrum/assets_src/shaders/shadows.glsl new file mode 100644 index 0000000..e27e2c4 --- /dev/null +++ b/destrum/assets_src/shaders/shadows.glsl @@ -0,0 +1,34 @@ +#ifndef SHADOWS_GLSL +#define SHADOWS_GLSL + +// expects bindless.glsl to be included before this file (it has no include guard) + +// Returns 1.0 when fully lit, 0.0 when fully in shadow +float calculateDirectionalShadow(vec3 fragPos, vec3 n, mat4 lightViewProj, uint shadowMapID, float normalOffset) +{ + // pushing the sample point along the normal hides most of the shadow acne + vec4 lightClip = lightViewProj * vec4(fragPos + n * normalOffset, 1.0); + vec3 lightNDC = lightClip.xyz / lightClip.w; + vec2 uv = lightNDC.xy * 0.5 + 0.5; + + // outside the shadow map counts as lit + if (any(lessThan(uv, vec2(0.0))) || any(greaterThan(uv, vec2(1.0))) || lightNDC.z > 1.0) { + return 1.0; + } + + vec2 texelSize = 1.0 / vec2(textureSize(sampler2DShadow(textures[shadowMapID], samplers[SHADOW_SAMPLER_ID]), 0)); + + // 3x3 PCF, every tap is already bilinearly filtered by the compare sampler + float shadowed = 0.0; + for (int x = -1; x <= 1; x++) { + for (int y = -1; y <= 1; y++) { + shadowed += sampleTexture2DShadow(shadowMapID, vec3(uv + vec2(x, y) * texelSize, lightNDC.z)); + } + } + + // the shadow sampler compares with GREATER_OR_EQUAL (it was set up for reversed Z), + // with regular depth that returns 1 when the fragment is behind the occluder + return 1.0 - shadowed / 9.0; +} + +#endif // SHADOWS_GLSL diff --git a/destrum/include/destrum/Components/LightComponent.h b/destrum/include/destrum/Components/LightComponent.h index f051bdf..d3d8eb3 100644 --- a/destrum/include/destrum/Components/LightComponent.h +++ b/destrum/include/destrum/Components/LightComponent.h @@ -39,6 +39,9 @@ public: [[nodiscard]] float GetInnerConeAngle() const { return m_InnerConeAngle; } [[nodiscard]] float GetOuterConeAngle() const { return m_OuterConeAngle; } + void SetCastShadows(bool castShadows) { m_CastShadows = castShadows; } + [[nodiscard]] bool GetCastShadows() const { return m_CastShadows; } + private: LightType m_Type{LightType::Point}; glm::vec3 m_Color{1.0f}; @@ -46,6 +49,7 @@ private: float m_Range{10.0f}; float m_InnerConeAngle{0.35f}; float m_OuterConeAngle{0.5f}; + bool m_CastShadows{false}; }; #endif // LIGHTCOMPONENT_H diff --git a/destrum/include/destrum/Graphics/Light.h b/destrum/include/destrum/Graphics/Light.h index 67d485e..e196535 100644 --- a/destrum/include/destrum/Graphics/Light.h +++ b/destrum/include/destrum/Graphics/Light.h @@ -25,6 +25,9 @@ struct Light { float innerConeAngle{0.35f}; float outerConeAngle{0.5f}; + + // directional lights only, the first one submitted each frame gets the shadow map + bool castShadows{false}; }; struct GPULight { @@ -39,7 +42,8 @@ struct GPULight { float spotInnerCos; float spotOuterCos; - glm::vec2 padding; + std::uint32_t castShadows; + float padding; }; static_assert(sizeof(GPULight) == 64, "GPULight must match the struct in lights.glsl"); diff --git a/destrum/include/destrum/Graphics/Pipelines/ShadowMapPipeline.h b/destrum/include/destrum/Graphics/Pipelines/ShadowMapPipeline.h new file mode 100644 index 0000000..f54e6d3 --- /dev/null +++ b/destrum/include/destrum/Graphics/Pipelines/ShadowMapPipeline.h @@ -0,0 +1,65 @@ +#ifndef SHADOWMAPPIPELINE_H +#define SHADOWMAPPIPELINE_H + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +class GfxDevice; +class RenderResources; + +// Renders the depth of the scene as seen from a directional light, centered around the camera +class ShadowMapPipeline final +{ +public: + static constexpr std::uint32_t SHADOW_MAP_SIZE = 2048; + + void init(GfxDevice& gfxDevice, RenderResources& resources); + void cleanup(VkDevice device); + + void draw( + VkCommandBuffer cmd, + const RenderResources& resources, + const GPUBuffer& sceneDataBuffer, + const std::vector& drawCommands, + const std::vector& sortedDrawCommands); + + [[nodiscard]] glm::mat4 calculateLightViewProj(const glm::vec3& lightDirection, const glm::vec3& center) const; + + [[nodiscard]] ImageID getShadowMapId() const { return shadowMapId; } + [[nodiscard]] float getNormalOffset() const; + + // half the width of the area around the camera that receives shadows + void setShadowDistance(float distance) { shadowDistance = glm::max(distance, 1.0f); } + [[nodiscard]] float getShadowDistance() const { return shadowDistance; } + +private: + struct PushConstants + { + glm::mat4 transform; + VkDeviceAddress sceneDataBuffer; + VkDeviceAddress vertexBuffer; + std::uint32_t materialId; + std::uint32_t padding; + }; + + VkPipelineLayout m_pipelineLayout{VK_NULL_HANDLE}; + std::unique_ptr m_pipeline; + + VkFormat shadowMapFormat{VK_FORMAT_D32_SFLOAT}; + ImageID shadowMapId{NULL_IMAGE_ID}; + + float shadowDistance{25.0f}; + // how far in front of / behind the camera (along the light) casters are still captured + float shadowDepthRange{100.0f}; +}; + +#endif //SHADOWMAPPIPELINE_H diff --git a/destrum/include/destrum/Graphics/Renderer.h b/destrum/include/destrum/Graphics/Renderer.h index 1bab387..2c41691 100644 --- a/destrum/include/destrum/Graphics/Renderer.h +++ b/destrum/include/destrum/Graphics/Renderer.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -17,6 +18,7 @@ #include +#include #include @@ -87,6 +89,16 @@ public: return renderLightHeatmap; } + void setShadowDistance(float distance) + { + shadowMapPipeline->setShadowDistance(distance); + } + + float getShadowDistance() const + { + return shadowMapPipeline->getShadowDistance(); + } + RenderResources* getResources() { return resources; @@ -138,7 +150,11 @@ private: std::uint32_t numLights; std::uint32_t tileCountX; std::uint32_t debugLightHeatmap; + + std::uint32_t shadowMapId; + float shadowNormalOffset; std::uint32_t padding; + glm::mat4 lightViewProj; }; NBuffer sceneDataBuffer; @@ -150,6 +166,9 @@ private: std::unique_ptr skinningPipeline; std::unique_ptr lightCullingPipeline; + std::unique_ptr shadowMapPipeline; + + std::optional shadowLightDirection; bool renderLightHeatmap{false}; bool initialized{false}; diff --git a/destrum/include/destrum/Graphics/Swapchain.h b/destrum/include/destrum/Graphics/Swapchain.h index ec413f4..2700d88 100644 --- a/destrum/include/destrum/Graphics/Swapchain.h +++ b/destrum/include/destrum/Graphics/Swapchain.h @@ -77,6 +77,8 @@ public: [[nodiscard]] VkFormat getFormat() const { return m_swapchain.image_format; } private: + void nameSwapchainObjects(VkDevice device) const; + struct FrameData { VkSemaphore swapchainSemaphore{VK_NULL_HANDLE}; VkFence renderFence{VK_NULL_HANDLE}; diff --git a/destrum/include/destrum/Graphics/Util.h b/destrum/include/destrum/Graphics/Util.h index 48017df..65bbce6 100644 --- a/destrum/include/destrum/Graphics/Util.h +++ b/destrum/include/destrum/Graphics/Util.h @@ -1,6 +1,8 @@ #ifndef UTIL_H #define UTIL_H +#include +#include #include #include #include @@ -87,6 +89,9 @@ namespace vkutil { VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE); + // Object names (VK_EXT_debug_utils), shown in RenderDoc, Nsight and validation messages. + // All of these are no-ops when the extension isn't loaded. + void addDebugLabel(VkDevice device, VkObjectType objectType, std::uint64_t objectHandle, const char* label); void addDebugLabel(VkDevice device, VkImage image, const char* label); void addDebugLabel(VkDevice device, VkImageView imageView, const char* label); void addDebugLabel(VkDevice device, VkShaderModule shaderModule, const char* label); @@ -94,6 +99,62 @@ namespace vkutil { void addDebugLabel(VkDevice device, VkPipelineLayout layout, const char* label); void addDebugLabel(VkDevice device, VkBuffer buffer, const char* label); void addDebugLabel(VkDevice device, VkSampler sampler, const char* label); + void addDebugLabel(VkDevice device, VkDescriptorPool descriptorPool, const char* label); + void addDebugLabel(VkDevice device, VkDescriptorSetLayout descriptorSetLayout, const char* label); + void addDebugLabel(VkDevice device, VkDescriptorSet descriptorSet, const char* label); + void addDebugLabel(VkDevice device, VkCommandPool commandPool, const char* label); + void addDebugLabel(VkDevice device, VkCommandBuffer commandBuffer, const char* label); + void addDebugLabel(VkDevice device, VkFence fence, const char* label); + void addDebugLabel(VkDevice device, VkSemaphore semaphore, const char* label); + void addDebugLabel(VkDevice device, VkQueue queue, const char* label); + + // Command buffer regions, they nest like a stack: every begin needs a matching end + // in the same command buffer. + [[nodiscard]] bool debugLabelsEnabled(); + void cmdBeginLabel(VkCommandBuffer cmd, const char* label, const glm::vec4& color = glm::vec4{1.0f}); + void cmdEndLabel(VkCommandBuffer cmd); + void cmdInsertLabel(VkCommandBuffer cmd, const char* label, const glm::vec4& color = glm::vec4{1.0f}); + + // printf style, only formats when labels are actually recorded (cheap enough for per draw labels) + template + void cmdBeginLabelFormatted(VkCommandBuffer cmd, const glm::vec4& color, const char* format, Args... args) + { + if (!debugLabelsEnabled()) { + return; + } + + char label[256]; + std::snprintf(label, sizeof(label), format, args...); + cmdBeginLabel(cmd, label, color); + } + + class ScopedCmdLabel { + public: + ScopedCmdLabel(VkCommandBuffer cmd, const char* label, const glm::vec4& color = glm::vec4{1.0f}); + ~ScopedCmdLabel(); + + ScopedCmdLabel(const ScopedCmdLabel& other) = delete; + ScopedCmdLabel(ScopedCmdLabel&& other) noexcept = delete; + ScopedCmdLabel& operator=(const ScopedCmdLabel& other) = delete; + ScopedCmdLabel& operator=(ScopedCmdLabel&& other) noexcept = delete; + + private: + VkCommandBuffer cmd{VK_NULL_HANDLE}; + }; + + // Colors used for the renderer's labels, so passes are easy to tell apart in a capture + namespace labelColors { + inline const glm::vec4 frame{0.8f, 0.8f, 0.8f, 1.0f}; + inline const glm::vec4 compute{0.9f, 0.5f, 0.1f, 1.0f}; + inline const glm::vec4 shadow{0.3f, 0.3f, 0.6f, 1.0f}; + inline const glm::vec4 depth{0.4f, 0.4f, 0.4f, 1.0f}; + inline const glm::vec4 opaque{0.2f, 0.7f, 0.3f, 1.0f}; + inline const glm::vec4 sky{0.3f, 0.6f, 0.9f, 1.0f}; + inline const glm::vec4 debug{0.9f, 0.2f, 0.6f, 1.0f}; + inline const glm::vec4 ui{0.9f, 0.9f, 0.2f, 1.0f}; + inline const glm::vec4 transfer{0.6f, 0.4f, 0.2f, 1.0f}; + inline const glm::vec4 draw{0.5f, 0.8f, 0.5f, 1.0f}; + } struct RenderingInfoParams { VkExtent2D renderExtent; diff --git a/destrum/src/Components/LightComponent.cpp b/destrum/src/Components/LightComponent.cpp index b28052a..9266139 100644 --- a/destrum/src/Components/LightComponent.cpp +++ b/destrum/src/Components/LightComponent.cpp @@ -47,6 +47,7 @@ void LightComponent::Render(const RenderContext& ctx) .range = m_Range, .innerConeAngle = m_InnerConeAngle, .outerConeAngle = m_OuterConeAngle, + .castShadows = m_CastShadows, }); } @@ -57,7 +58,8 @@ nlohmann::json LightComponent::Serialize() const { {"intensity", m_Intensity}, {"range", m_Range}, {"innerConeAngle", m_InnerConeAngle}, - {"outerConeAngle", m_OuterConeAngle} + {"outerConeAngle", m_OuterConeAngle}, + {"castShadows", m_CastShadows} }; } @@ -88,6 +90,10 @@ void LightComponent::Deserialize(const nlohmann::json& data) { outerConeAngle = data.at("outerConeAngle").get(); } SetConeAngles(innerConeAngle, outerConeAngle); + + if (data.contains("castShadows")) { + m_CastShadows = data.at("castShadows").get(); + } } void LightComponent::ImGuiInspector() { @@ -106,6 +112,9 @@ void LightComponent::ImGuiInspector() { } if (m_Type == LightType::Directional) { + ImGui::Checkbox("Cast Shadows", &m_CastShadows); + ImGui::SameLine(); + ImGui::TextDisabled("(first directional light only)"); return; } diff --git a/destrum/src/Components/MeshRendererComponent.cpp b/destrum/src/Components/MeshRendererComponent.cpp index 9e53686..c18a679 100644 --- a/destrum/src/Components/MeshRendererComponent.cpp +++ b/destrum/src/Components/MeshRendererComponent.cpp @@ -33,6 +33,9 @@ void MeshRendererComponent::Start() { VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT ); + + const std::string bufferName = GetGameObject()->GetName() + " (skinned vertices)"; + vkutil::addDebugLabel(gfxDevice.getDevice(), m_skinnedMesh->skinnedVertexBuffer.buffer, bufferName.c_str()); } } diff --git a/destrum/src/Graphics/BindlessSetManager.cpp b/destrum/src/Graphics/BindlessSetManager.cpp index 7da332f..9c33ca5 100644 --- a/destrum/src/Graphics/BindlessSetManager.cpp +++ b/destrum/src/Graphics/BindlessSetManager.cpp @@ -74,6 +74,7 @@ void BindlessSetManager::init( }; VK_CHECK(vkCreateDescriptorPool(device, &poolInfo, nullptr, &descPool)); + vkutil::addDebugLabel(device, descPool, "bindless descriptor pool"); } { // build desc set layout @@ -111,6 +112,7 @@ void BindlessSetManager::init( }; VK_CHECK(vkCreateDescriptorSetLayout(device, &info, nullptr, &descSetLayout)); + vkutil::addDebugLabel(device, descSetLayout, "bindless descriptor set layout"); } { // alloc desc set @@ -122,6 +124,7 @@ void BindlessSetManager::init( }; VK_CHECK(vkAllocateDescriptorSets(device, &allocInfo, &descSet)); + vkutil::addDebugLabel(device, descSet, "bindless descriptor set"); } initDefaultSamplers(device, maxAnisotropy); diff --git a/destrum/src/Graphics/ComputePipeline.cpp b/destrum/src/Graphics/ComputePipeline.cpp index 7a0e4d6..719ecd3 100644 --- a/destrum/src/Graphics/ComputePipeline.cpp +++ b/destrum/src/Graphics/ComputePipeline.cpp @@ -99,8 +99,13 @@ void ComputePipeline::CreateComputePipeline(const std::string& compPath, throw std::runtime_error("Can't make compute pipeline!"); } + vkutil::addDebugLabel(m_device.getDevice(), m_compShaderModule, compFileName.c_str()); + if (!configInfo.name.empty()) { - vkutil::addDebugLabel(GameState::GetInstance().Gfx().getDevice(), m_computePipeline, configInfo.name.c_str()); + vkutil::addDebugLabel(m_device.getDevice(), m_computePipeline, configInfo.name.c_str()); + + const std::string layoutName = configInfo.name + " Layout"; + vkutil::addDebugLabel(m_device.getDevice(), configInfo.pipelineLayout, layoutName.c_str()); } } diff --git a/destrum/src/Graphics/GfxDevice.cpp b/destrum/src/Graphics/GfxDevice.cpp index 87010ea..dfee15a 100644 --- a/destrum/src/Graphics/GfxDevice.cpp +++ b/destrum/src/Graphics/GfxDevice.cpp @@ -156,6 +156,7 @@ void GfxDevice::endFrame(VkCommandBuffer cmd, const GPUImage& drawImage, const E auto swapchainLayout = VK_IMAGE_LAYOUT_UNDEFINED; { ZoneScopedN("Clear Swapchain Image"); + vkutil::ScopedCmdLabel label(cmd, "Clear Swapchain Image", vkutil::labelColors::transfer); const VkImageSubresourceRange clearRange = vkinit::imageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT); vkutil::transitionImage(cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_GENERAL); @@ -167,6 +168,7 @@ void GfxDevice::endFrame(VkCommandBuffer cmd, const GPUImage& drawImage, const E if (true) { ZoneScopedN("Copy DrawImage To Swapchain"); + vkutil::ScopedCmdLabel label(cmd, "Copy DrawImage To Swapchain", vkutil::labelColors::transfer); vkutil::transitionImage( cmd, @@ -190,6 +192,7 @@ void GfxDevice::endFrame(VkCommandBuffer cmd, const GPUImage& drawImage, const E if (props.imguiPass) { ZoneScopedN("ImGui Render"); + vkutil::ScopedCmdLabel label(cmd, "ImGui Render", vkutil::labelColors::ui); props.imguiPass->render( cmd, @@ -202,6 +205,7 @@ void GfxDevice::endFrame(VkCommandBuffer cmd, const GPUImage& drawImage, const E { ZoneScopedN("Transition Swapchain To Present"); + vkutil::ScopedCmdLabel label(cmd, "Transition Swapchain To Present", vkutil::labelColors::transfer); vkutil::transitionImage(cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); swapchainLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; diff --git a/destrum/src/Graphics/ImmediateExecuter.cpp b/destrum/src/Graphics/ImmediateExecuter.cpp index 602d0a5..06e04f8 100644 --- a/destrum/src/Graphics/ImmediateExecuter.cpp +++ b/destrum/src/Graphics/ImmediateExecuter.cpp @@ -40,6 +40,10 @@ void VulkanImmediateExecutor::init( }; VK_CHECK(vkCreateFence(deviceHandle, &fenceCreateInfo, nullptr, &immFence)); + vkutil::addDebugLabel(deviceHandle, immCommandPool, "immediate command pool"); + vkutil::addDebugLabel(deviceHandle, immCommandBuffer, "immediate command buffer"); + vkutil::addDebugLabel(deviceHandle, immFence, "immediate fence"); + initialized = true; } catch (...) { cleanup(deviceHandle); diff --git a/destrum/src/Graphics/Managers/FrameManager.cpp b/destrum/src/Graphics/Managers/FrameManager.cpp index 6c09a92..d42cae3 100644 --- a/destrum/src/Graphics/Managers/FrameManager.cpp +++ b/destrum/src/Graphics/Managers/FrameManager.cpp @@ -26,6 +26,11 @@ void FrameManager::init(VkDevice dev, std::uint32_t queueFamily) { const auto cmdAllocInfo = vkinit::commandBufferAllocateInfo(commandPool, 1); auto& mainCommandBuffer = frames[i].commandBuffer; VK_CHECK(vkAllocateCommandBuffers(device, &cmdAllocInfo, &mainCommandBuffer)); + + const std::string poolName = "frame " + std::to_string(i) + " command pool"; + const std::string cmdName = "frame " + std::to_string(i) + " command buffer"; + vkutil::addDebugLabel(device, commandPool, poolName.c_str()); + vkutil::addDebugLabel(device, mainCommandBuffer, cmdName.c_str()); } } catch (...) { cleanup(dev); diff --git a/destrum/src/Graphics/Managers/ImageManager.cpp b/destrum/src/Graphics/Managers/ImageManager.cpp index deb7e08..1d556fc 100644 --- a/destrum/src/Graphics/Managers/ImageManager.cpp +++ b/destrum/src/Graphics/Managers/ImageManager.cpp @@ -178,7 +178,9 @@ std::optional ImageManager::loadImageFromFile( } image.debugName = path.string(); - vkutil::addDebugLabel(device, image.image, path.string().c_str()); + vkutil::addDebugLabel(device, image.image, image.debugName.c_str()); + const std::string viewName = image.debugName + " (view)"; + vkutil::addDebugLabel(device, image.imageView, viewName.c_str()); return image; } diff --git a/destrum/src/Graphics/MeshCache.cpp b/destrum/src/Graphics/MeshCache.cpp index 4171a61..8793ade 100644 --- a/destrum/src/Graphics/MeshCache.cpp +++ b/destrum/src/Graphics/MeshCache.cpp @@ -156,6 +156,9 @@ void MeshCache::uploadMesh(GfxDevice& gfxDevice, const CPUMesh& cpuMesh, GPUMesh VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE); + const auto skinningBufferName = cpuMesh.name + " (skinning data)"; + vkutil::addDebugLabel(gfxDevice.getDevice(), gpuMesh.skinningDataBuffer.buffer, skinningBufferName.c_str()); + auto skinningStaging = gfxDevice.createBuffer( skinningDataSize, diff --git a/destrum/src/Graphics/Pipeline.cpp b/destrum/src/Graphics/Pipeline.cpp index cb3a940..98c5d42 100644 --- a/destrum/src/Graphics/Pipeline.cpp +++ b/destrum/src/Graphics/Pipeline.cpp @@ -146,11 +146,11 @@ void Pipeline::CreateGraphicsPipeline(const std::string& vertPath, const std::st CreateShaderModule(vertCode, &m_vertShaderModule); - // vkutil::addDebugLabel( - // m_device.getDevice(), - // m_fragShaderModule, - // VertFileName.c_str() - // ); + vkutil::addDebugLabel( + m_device.getDevice(), + m_vertShaderModule, + VertFileName.c_str() + ); VkPipelineShaderStageCreateInfo vertShaderStageInfo{}; vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; @@ -173,11 +173,11 @@ void Pipeline::CreateGraphicsPipeline(const std::string& vertPath, const std::st CreateShaderModule(fragCode, &m_fragShaderModule); - // vkutil::addDebugLabel( - // m_device.getDevice(), - // m_fragShaderModule, - // FragFileName.c_str() - // ); + vkutil::addDebugLabel( + m_device.getDevice(), + m_fragShaderModule, + FragFileName.c_str() + ); VkPipelineShaderStageCreateInfo fragShaderStageInfo{}; @@ -231,18 +231,17 @@ void Pipeline::CreateGraphicsPipeline(const std::string& vertPath, const std::st pipelineInfo.basePipelineIndex = -1; pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; - if (!configInfo.name.empty()) { - // DebugLabel::SetObjectName( - // reinterpret_cast(pipelineInfo.layout), - // VK_OBJECT_TYPE_PIPELINE_LAYOUT, - // configInfo.name.c_str() - // ); - } - if (vkCreateGraphicsPipelines(m_device.getDevice(), VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &m_graphicsPipeline) != VK_SUCCESS) { throw std::runtime_error("Can't make pipeline!"); } + + if (!configInfo.name.empty()) { + vkutil::addDebugLabel(m_device.getDevice(), m_graphicsPipeline, configInfo.name.c_str()); + + const std::string layoutName = configInfo.name + " Layout"; + vkutil::addDebugLabel(m_device.getDevice(), configInfo.pipelineLayout, layoutName.c_str()); + } } void Pipeline::CreateShaderModule(const std::vector& code, VkShaderModule* shaderModule) const { diff --git a/destrum/src/Graphics/Pipelines/LightCullingPipeline.cpp b/destrum/src/Graphics/Pipelines/LightCullingPipeline.cpp index 6a9b3fc..cec7660 100644 --- a/destrum/src/Graphics/Pipelines/LightCullingPipeline.cpp +++ b/destrum/src/Graphics/Pipelines/LightCullingPipeline.cpp @@ -158,7 +158,8 @@ void LightCullingPipeline::appendLight(const Light& light) .type = static_cast(light.type), .spotInnerCos = std::cos(light.innerConeAngle), .spotOuterCos = std::cos(light.outerConeAngle), - .padding = glm::vec2{0.0f}, + .castShadows = light.castShadows ? 1u : 0u, + .padding = 0.0f, }; lightsBuffer.append(gpuLight); } diff --git a/destrum/src/Graphics/Pipelines/MeshPipeline.cpp b/destrum/src/Graphics/Pipelines/MeshPipeline.cpp index c9043fa..3455db7 100644 --- a/destrum/src/Graphics/Pipelines/MeshPipeline.cpp +++ b/destrum/src/Graphics/Pipelines/MeshPipeline.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include "volk.h" #include "spdlog/spdlog.h" @@ -219,6 +220,16 @@ void MeshPipeline::drawMeshes( const auto& mesh = meshCache.getMesh(dc.meshId); + const std::string& meshKey = meshCache.getMeshKey(dc.meshId); + vkutil::cmdBeginLabelFormatted( + cmd, + vkutil::labelColors::draw, + "%s [mesh %zu, material %u%s]", + meshKey.empty() ? "Mesh" : meshKey.c_str(), + dc.meshId, + dc.materialId, + dc.skinnedMesh != nullptr ? ", skinned" : ""); + if (dc.meshId != prevMeshId) { prevMeshId = dc.meshId; @@ -254,6 +265,8 @@ void MeshPipeline::drawMeshes( ); vkCmdDrawIndexed(cmd, mesh.numIndices, 1, 0, 0, 0); + + vkutil::cmdEndLabel(cmd); }; if (!sortedDrawCommands.empty()) diff --git a/destrum/src/Graphics/Pipelines/QuadRendererPass.cpp b/destrum/src/Graphics/Pipelines/QuadRendererPass.cpp index 06fe5eb..c9c9cf6 100644 --- a/destrum/src/Graphics/Pipelines/QuadRendererPass.cpp +++ b/destrum/src/Graphics/Pipelines/QuadRendererPass.cpp @@ -42,12 +42,14 @@ void QuadRendererPass::init(GfxDevice& gfxDevice, RenderResources& resources, Vk vertexBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_HOST); + vkutil::addDebugLabel(gfxDevice.getDevice(), vertexBuffer.buffer, "quad vertices"); const std::size_t indexBytes = InitialQuadCapacity * IndicesPerQuad * sizeof(std::uint32_t); indexBuffer = gfxDevice.createBuffer( indexBytes, VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_HOST); + vkutil::addDebugLabel(gfxDevice.getDevice(), indexBuffer.buffer, "quad indices"); quadCapacity = InitialQuadCapacity; @@ -321,6 +323,7 @@ void QuadRendererPass::ensureCapacity( newCapacity * VerticesPerQuad * sizeof(QuadVertex), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_HOST); + vkutil::addDebugLabel(gfxDevice.getDevice(), vertexBuffer.buffer, "quad vertices"); if (indexBuffer.buffer != VK_NULL_HANDLE) { gfxDevice.destroyBuffer(indexBuffer); @@ -329,6 +332,7 @@ void QuadRendererPass::ensureCapacity( newCapacity * IndicesPerQuad * sizeof(std::uint32_t), VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_HOST); + vkutil::addDebugLabel(gfxDevice.getDevice(), indexBuffer.buffer, "quad indices"); quadCapacity = newCapacity; } \ No newline at end of file diff --git a/destrum/src/Graphics/Pipelines/ShadowMapPipeline.cpp b/destrum/src/Graphics/Pipelines/ShadowMapPipeline.cpp new file mode 100644 index 0000000..ea67194 --- /dev/null +++ b/destrum/src/Graphics/Pipelines/ShadowMapPipeline.cpp @@ -0,0 +1,265 @@ +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "volk.h" +#include "spdlog/spdlog.h" + +void ShadowMapPipeline::init(GfxDevice& gfxDevice, RenderResources& resources) +{ + try + { + const auto vertexShader = + AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/shadow.vert"); + + const auto bufferRange = VkPushConstantRange{ + .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, + .offset = 0, + .size = sizeof(PushConstants), + }; + + const auto pushConstantRanges = std::array{bufferRange}; + + const auto layouts = std::array{ + resources.getBindlessDescSetLayout() + }; + + VkPipelineLayoutCreateInfo pipelineLayoutInfo{}; + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.setLayoutCount = static_cast(layouts.size()); + pipelineLayoutInfo.pSetLayouts = layouts.data(); + pipelineLayoutInfo.pushConstantRangeCount = static_cast(pushConstantRanges.size()); + pipelineLayoutInfo.pPushConstantRanges = pushConstantRanges.data(); + + if (vkCreatePipelineLayout( + gfxDevice.getDevice(), + &pipelineLayoutInfo, + nullptr, + &m_pipelineLayout) != VK_SUCCESS) + { + throw std::runtime_error("Could not create shadow map pipeline layout"); + } + + PipelineConfigInfo pipelineConfig{}; + Pipeline::DefaultPipelineConfigInfo(pipelineConfig); + + pipelineConfig.name = "Shadow Map Pipeline"; + pipelineConfig.pipelineLayout = m_pipelineLayout; + + pipelineConfig.vertexAttributeDescriptions = {}; + pipelineConfig.vertexBindingDescriptions = {}; + + pipelineConfig.colorAttachments = {}; + pipelineConfig.colorBlendInfo.attachmentCount = 0; + pipelineConfig.depthAttachment = shadowMapFormat; + + // the light projection isn't Y flipped like the camera's, so the winding is mirrored + pipelineConfig.rasterizationInfo.cullMode = VK_CULL_MODE_NONE; + pipelineConfig.rasterizationInfo.depthBiasEnable = VK_TRUE; + pipelineConfig.rasterizationInfo.depthBiasConstantFactor = 0.0f; + pipelineConfig.rasterizationInfo.depthBiasSlopeFactor = 2.0f; + + m_pipeline = std::make_unique( + gfxDevice, + vertexShader.string(), + "", + pipelineConfig + ); + + const auto createInfo = vkutil::CreateImageInfo{ + .format = shadowMapFormat, + .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + .extent = VkExtent3D{SHADOW_MAP_SIZE, SHADOW_MAP_SIZE, 1}, + }; + + shadowMapId = resources.createImage(gfxDevice, createInfo, "shadow map", nullptr, shadowMapId); + spdlog::info("Created {0}x{0} shadow map with id {1}", SHADOW_MAP_SIZE, shadowMapId); + } + catch (...) + { + cleanup(gfxDevice.getDevice()); + throw; + } +} + +void ShadowMapPipeline::cleanup(VkDevice device) +{ + m_pipeline.reset(); + + if (m_pipelineLayout != VK_NULL_HANDLE && device != VK_NULL_HANDLE) + { + vkDestroyPipelineLayout(device, m_pipelineLayout, nullptr); + } + m_pipelineLayout = VK_NULL_HANDLE; + + shadowMapId = NULL_IMAGE_ID; +} + +glm::mat4 ShadowMapPipeline::calculateLightViewProj(const glm::vec3& lightDirection, const glm::vec3& center) const +{ + const glm::vec3 direction = glm::normalize(lightDirection); + const glm::vec3 up = std::abs(direction.y) > 0.99f ? glm::vec3{0.0f, 0.0f, 1.0f} : glm::vec3{0.0f, 1.0f, 0.0f}; + + const glm::mat4 lightView = glm::lookAt(glm::vec3{0.0f}, direction, up); + + const float texelSize = (2.0f * shadowDistance) / static_cast(SHADOW_MAP_SIZE); + glm::vec3 lightSpaceCenter = glm::vec3(lightView * glm::vec4(center, 1.0f)); + lightSpaceCenter.x = std::floor(lightSpaceCenter.x / texelSize) * texelSize; + lightSpaceCenter.y = std::floor(lightSpaceCenter.y / texelSize) * texelSize; + + const glm::mat4 lightProj = glm::orthoRH_ZO( + lightSpaceCenter.x - shadowDistance, + lightSpaceCenter.x + shadowDistance, + lightSpaceCenter.y - shadowDistance, + lightSpaceCenter.y + shadowDistance, + -lightSpaceCenter.z - shadowDepthRange, + -lightSpaceCenter.z + shadowDepthRange); + + return lightProj * lightView; +} + +float ShadowMapPipeline::getNormalOffset() const +{ + const float texelSize = (2.0f * shadowDistance) / static_cast(SHADOW_MAP_SIZE); + return texelSize * 1.5f; +} + +void ShadowMapPipeline::draw( + VkCommandBuffer cmd, + const RenderResources& resources, + const GPUBuffer& sceneDataBuffer, + const std::vector& drawCommands, + const std::vector& sortedDrawCommands) +{ + if (!m_pipeline || shadowMapId == NULL_IMAGE_ID) + { + return; + } + + const auto& shadowMap = resources.getImage(shadowMapId); + + vkutil::transitionImage( + cmd, + shadowMap.image, + shadowMap.layout, + VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL); + shadowMap.setLayout(VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL); + + auto renderInfo = vkutil::createRenderingInfo({ + .renderExtent = shadowMap.getExtent2D(), + .depthImageView = shadowMap.imageView, + .depthImageClearValue = 1.f, + }); + renderInfo.renderingInfo.pDepthAttachment = &renderInfo.depthAttachment; + + vkCmdBeginRendering(cmd, &renderInfo.renderingInfo); + + m_pipeline->bind(cmd); + resources.bindBindlessDescSet(cmd, m_pipelineLayout); + + const auto viewport = VkViewport{ + .x = 0.f, + .y = 0.f, + .width = static_cast(SHADOW_MAP_SIZE), + .height = static_cast(SHADOW_MAP_SIZE), + .minDepth = 0.f, + .maxDepth = 1.f, + }; + vkCmdSetViewport(cmd, 0, 1, &viewport); + + const auto scissor = VkRect2D{ + .offset = {}, + .extent = shadowMap.getExtent2D(), + }; + vkCmdSetScissor(cmd, 0, 1, &scissor); + + vkCmdSetPolygonModeEXT(cmd, VK_POLYGON_MODE_FILL); + + const MeshCache& meshCache = resources.meshes(); + MeshID prevMeshId = NULL_MESH_ID; + + const auto drawOne = [&](const MeshDrawCommand& dc) + { + if (!dc.castShadow) + { + return; + } + + const auto& mesh = meshCache.getMesh(dc.meshId); + + const std::string& meshKey = meshCache.getMeshKey(dc.meshId); + vkutil::cmdBeginLabelFormatted( + cmd, + vkutil::labelColors::draw, + "%s [mesh %zu, material %u%s]", + meshKey.empty() ? "Mesh" : meshKey.c_str(), + dc.meshId, + dc.materialId, + dc.skinnedMesh != nullptr ? ", skinned" : ""); + + if (dc.meshId != prevMeshId) + { + prevMeshId = dc.meshId; + vkCmdBindIndexBuffer(cmd, mesh.indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT32); + } + + const auto pushConstants = PushConstants{ + .transform = dc.transformMatrix, + .sceneDataBuffer = sceneDataBuffer.address, + .vertexBuffer = + dc.skinnedMesh != nullptr + ? dc.skinnedMesh->skinnedVertexBuffer.address + : mesh.vertexBuffer.address, + .materialId = dc.materialId, + .padding = 0, + }; + + vkCmdPushConstants( + cmd, + m_pipelineLayout, + VK_SHADER_STAGE_VERTEX_BIT, + 0, + sizeof(PushConstants), + &pushConstants); + + vkCmdDrawIndexed(cmd, mesh.numIndices, 1, 0, 0, 0); + + vkutil::cmdEndLabel(cmd); + }; + + if (!sortedDrawCommands.empty()) + { + for (std::size_t drawIndex : sortedDrawCommands) + { + assert(drawIndex < drawCommands.size()); + drawOne(drawCommands[drawIndex]); + } + } + else + { + for (const MeshDrawCommand& dc : drawCommands) + { + drawOne(dc); + } + } + + vkCmdEndRendering(cmd); + + vkutil::transitionImage( + cmd, + shadowMap.image, + shadowMap.layout, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + shadowMap.setLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); +} diff --git a/destrum/src/Graphics/Pipelines/SkinningPipeline.cpp b/destrum/src/Graphics/Pipelines/SkinningPipeline.cpp index 69c1df1..40ecfad 100644 --- a/destrum/src/Graphics/Pipelines/SkinningPipeline.cpp +++ b/destrum/src/Graphics/Pipelines/SkinningPipeline.cpp @@ -53,6 +53,9 @@ void SkinningPipeline::init(GfxDevice& gfxDevice) { MAX_JOINT_MATRICES * sizeof(glm::mat4), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); + + const std::string bufferName = "joint matrices (frame " + std::to_string(i) + ")"; + vkutil::addDebugLabel(device, jointMatricesBuffer.buffer.buffer, bufferName.c_str()); } } catch (...) { cleanup(gfxDevice); @@ -84,6 +87,9 @@ void SkinningPipeline::doSkinning(VkCommandBuffer cmd, assert(mesh.hasSkeleton); assert(dc.skinnedMesh); + const std::string& meshKey = meshCache.getMeshKey(dc.meshId); + vkutil::ScopedCmdLabel label(cmd, meshKey.empty() ? "Skin Mesh" : meshKey.c_str(), vkutil::labelColors::compute); + const auto cs = PushConstants{ .jointMatricesBuffer = getCurrentFrameData(frameIndex).jointMatricesBuffer.buffer.address, .jointMatricesStartIndex = static_cast(dc.jointMatricesStartIndex), diff --git a/destrum/src/Graphics/RenderResources.cpp b/destrum/src/Graphics/RenderResources.cpp index d5106b0..b876807 100644 --- a/destrum/src/Graphics/RenderResources.cpp +++ b/destrum/src/Graphics/RenderResources.cpp @@ -130,6 +130,8 @@ ImageID RenderResources::createImage( if (!debugName.empty()) { vkutil::addDebugLabel(gfxDevice.getVkDevice(), image.image, debugName.c_str()); + const std::string viewName = debugName + " (view)"; + vkutil::addDebugLabel(gfxDevice.getVkDevice(), image.imageView, viewName.c_str()); image.debugName = debugName; } diff --git a/destrum/src/Graphics/Renderer.cpp b/destrum/src/Graphics/Renderer.cpp index 899245b..6a18f17 100644 --- a/destrum/src/Graphics/Renderer.cpp +++ b/destrum/src/Graphics/Renderer.cpp @@ -44,6 +44,9 @@ void GameRenderer::init(GfxDevice& gfxDevice, RenderResources& _resources, glm:: lightCullingPipeline = std::make_unique(); lightCullingPipeline->init(gfxDevice, _resources, drawImageSize); + shadowMapPipeline = std::make_unique(); + shadowMapPipeline->init(gfxDevice, _resources); + lineRenderingPass = std::make_unique(); lineRenderingPass->init(gfxDevice, drawImageFormat); @@ -66,6 +69,7 @@ void GameRenderer::beginDrawing(GfxDevice& gfxDevice) meshDrawCommands.clear(); skinningPipeline->beginDrawing(gfxDevice.getCurrentFrameIndex()); lightCullingPipeline->beginDrawing(gfxDevice.getCurrentFrameIndex()); + shadowLightDirection.reset(); QuadRenderingManager::GetInstance().ClearQuads(); } @@ -97,9 +101,11 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& { ZoneScopedN("GameRenderer::draw"); TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "GameRenderer::draw"); + vkutil::ScopedCmdLabel frameLabel(cmd, "GameRenderer::draw", vkutil::labelColors::frame); { TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Skinning"); + vkutil::ScopedCmdLabel label(cmd, "Skinning", vkutil::labelColors::compute); skinningPipeline->flushCurrentFrame( cmd, @@ -118,10 +124,17 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& { TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Lights Buffer Flush"); + vkutil::ScopedCmdLabel label(cmd, "Lights Buffer Flush", vkutil::labelColors::transfer); lightCullingPipeline->flushCurrentFrame(cmd, gfxDevice); } + const glm::mat4 lightViewProj = shadowLightDirection + ? shadowMapPipeline->calculateLightViewProj( + *shadowLightDirection, + sceneData.camera.GetPosition()) + : glm::mat4{1.0f}; + // TODO: there has to be a better way to do this const auto gpuSceneData = GPUSceneData{ .view = sceneData.camera.GetViewMatrix(), @@ -139,11 +152,15 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& .numLights = lightCullingPipeline->getNumLights(), .tileCountX = lightCullingPipeline->getTileCount().x, .debugLightHeatmap = renderLightHeatmap ? 1u : 0u, + .shadowMapId = static_cast(shadowMapPipeline->getShadowMapId()), + .shadowNormalOffset = shadowMapPipeline->getNormalOffset(), .padding = 0, + .lightViewProj = lightViewProj, }; { TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Material Buffer Barrier"); + vkutil::ScopedCmdLabel label(cmd, "Material Buffer Barrier", vkutil::labelColors::transfer); vkutil::bufferHostWriteToShaderReadBarrier( cmd, @@ -155,6 +172,7 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& { TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Upload Scene Data"); + vkutil::ScopedCmdLabel label(cmd, "Upload Scene Data", vkutil::labelColors::transfer); sceneDataBuffer.uploadNewData( cmd, @@ -189,8 +207,22 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& depthImage.setLayout(VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL); } + if (shadowLightDirection) + { + TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Shadow Map"); + vkutil::ScopedCmdLabel label(cmd, "Shadow Map", vkutil::labelColors::shadow); + + shadowMapPipeline->draw( + cmd, + *resources, + sceneDataBuffer.getBuffer(), + meshDrawCommands, + sortedMeshDrawCommands); + } + { TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Depth Prepass"); + vkutil::ScopedCmdLabel label(cmd, "Depth Prepass", vkutil::labelColors::depth); auto depthPrepassInfo = vkutil::createRenderingInfo({ .renderExtent = drawImage.getExtent2D(), @@ -215,6 +247,7 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& { TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Light Culling"); + vkutil::ScopedCmdLabel label(cmd, "Light Culling", vkutil::labelColors::compute); vkutil::transitionImage( cmd, @@ -247,6 +280,8 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& renderInfo.renderingInfo.pColorAttachments = &renderInfo.colorAttachment; renderInfo.renderingInfo.pDepthAttachment = &renderInfo.depthAttachment; + vkutil::cmdBeginLabel(cmd, "Forward Pass", vkutil::labelColors::opaque); + { TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "vkCmdBeginRendering"); @@ -255,6 +290,7 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& { TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "MeshPipeline::draw"); + vkutil::ScopedCmdLabel label(cmd, "MeshPipeline::draw", vkutil::labelColors::opaque); meshPipeline->draw( cmd, @@ -268,6 +304,7 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& { TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "SkyboxPipeline::draw"); + vkutil::ScopedCmdLabel label(cmd, "SkyboxPipeline::draw", vkutil::labelColors::sky); skyboxPipeline->draw(cmd, *resources, camera); } @@ -278,8 +315,11 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& vkCmdEndRendering(cmd); } + vkutil::cmdEndLabel(cmd); + { TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "LineRenderingPass::draw"); + vkutil::ScopedCmdLabel label(cmd, "LineRenderingPass::draw", vkutil::labelColors::debug); lineRenderingPass->draw( cmd, @@ -291,6 +331,7 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& { TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "QuadRendererPass::draw"); + vkutil::ScopedCmdLabel label(cmd, "QuadRendererPass::draw", vkutil::labelColors::ui); quadRendererPass->draw( cmd, @@ -302,7 +343,6 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& static_cast(drawImage.extent.height), 0.0f, -1.0f, 1.0f)); } - // vkutil::cmdEndLabel(cmd); } void GameRenderer::drawQuads( @@ -335,6 +375,9 @@ void GameRenderer::cleanup(GfxDevice& gfxDevice) if (lightCullingPipeline) lightCullingPipeline->cleanup(gfxDevice); + if (shadowMapPipeline) + shadowMapPipeline->cleanup(device); + if (skyboxPipeline) skyboxPipeline->cleanup(device); @@ -358,6 +401,8 @@ void GameRenderer::cleanup(GfxDevice& gfxDevice) lineRenderingPass.reset(); quadRendererPass.reset(); lightCullingPipeline.reset(); + shadowMapPipeline.reset(); + shadowLightDirection.reset(); pendingMaterialUploads.clear(); meshDrawCommands.clear(); sortedMeshDrawCommands.clear(); @@ -402,7 +447,18 @@ void GameRenderer::drawSkinnedMesh(MeshID id, void GameRenderer::submitLight(const Light& light) { - lightCullingPipeline->appendLight(light); + Light submittedLight = light; + + // there's a single shadow map, the first shadow casting directional light gets it + submittedLight.castShadows = + light.castShadows && light.type == LightType::Directional && !shadowLightDirection; + + if (submittedLight.castShadows) + { + shadowLightDirection = light.direction; + } + + lightCullingPipeline->appendLight(submittedLight); } const GPUImage& GameRenderer::getDrawImage() const diff --git a/destrum/src/Graphics/Resources/Cubemap.cpp b/destrum/src/Graphics/Resources/Cubemap.cpp index 444d1e1..6c4ab05 100644 --- a/destrum/src/Graphics/Resources/Cubemap.cpp +++ b/destrum/src/Graphics/Resources/Cubemap.cpp @@ -71,6 +71,8 @@ void CubeMap::RenderToCubemap( } gfxDevice.getImmediateExecuter().immediateSubmit([&](VkCommandBuffer cmd) { + vkutil::ScopedCmdLabel label(cmd, "HDRI To Cubemap", vkutil::labelColors::sky); + VkImageMemoryBarrier barrier{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER}; barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; @@ -108,6 +110,8 @@ void CubeMap::RenderToCubemap( scissor.extent = {size, size}; for (std::uint32_t face = 0; face < 6; ++face) { + vkutil::cmdBeginLabelFormatted(cmd, vkutil::labelColors::draw, "Cubemap Face %u", face); + PC pc{}; pc.viewMtx = viewMatrices[face]; pc.projMtx = m_projection; @@ -153,6 +157,8 @@ void CubeMap::RenderToCubemap( vkCmdDraw(cmd, 36, 1, 0, 0); vkCmdEndRendering(cmd); + + vkutil::cmdEndLabel(cmd); } barrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; @@ -199,6 +205,7 @@ void CubeMap::CreateCubeMap( .mipMap = false, .isCubemap = true }); + vkutil::addDebugLabel(gfxDevice.getDevice(), cubeMapImage.image, "skybox cubemap"); std::array faceViews{}; @@ -220,6 +227,9 @@ void CubeMap::CreateCubeMap( &viewInfo, nullptr, &faceViews[face])); + + const std::string faceViewName = "skybox cubemap face " + std::to_string(face) + " (view)"; + vkutil::addDebugLabel(gfxDevice.getDevice(), faceViews[face], faceViewName.c_str()); } spdlog::info("HDRI image id = {}", m_hdrImage); @@ -305,6 +315,7 @@ void CubeMap::InitCubemapPipeline( PipelineConfigInfo pipelineConfig{}; Pipeline::DefaultPipelineConfigInfo(pipelineConfig); + pipelineConfig.name = "HDRI To Cubemap Pipeline"; pipelineConfig.vertexAttributeDescriptions = {}; pipelineConfig.vertexBindingDescriptions = {}; diff --git a/destrum/src/Graphics/Swapchain.cpp b/destrum/src/Graphics/Swapchain.cpp index 4f3ea5f..66a1d57 100644 --- a/destrum/src/Graphics/Swapchain.cpp +++ b/destrum/src/Graphics/Swapchain.cpp @@ -21,6 +21,22 @@ void Swapchain::initSync(VkDevice device) { for (std::uint32_t i = 0; i < FRAMES_IN_FLIGHT; ++i) { VK_CHECK(vkCreateFence(device, &fenceCreateInfo, nullptr, &frames[i].renderFence)); VK_CHECK(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &frames[i].swapchainSemaphore)); + + const auto fenceName = std::format("frame {} render fence", i); + const auto semaphoreName = std::format("frame {} swapchain semaphore", i); + vkutil::addDebugLabel(device, frames[i].renderFence, fenceName.c_str()); + vkutil::addDebugLabel(device, frames[i].swapchainSemaphore, semaphoreName.c_str()); + } +} + +void Swapchain::nameSwapchainObjects(VkDevice device) const { + for (std::size_t i = 0; i < images.size(); ++i) { + const auto imageName = std::format("swapchain image {}", i); + const auto viewName = std::format("swapchain image {} (view)", i); + const auto semaphoreName = std::format("swapchain image {} render semaphore", i); + vkutil::addDebugLabel(device, images[i], imageName.c_str()); + vkutil::addDebugLabel(device, imageViews[i], viewName.c_str()); + vkutil::addDebugLabel(device, imageRenderSemaphores[i], semaphoreName.c_str()); } } @@ -85,6 +101,8 @@ void Swapchain::createSwapchain( VK_CHECK(vkCreateSemaphore(vkbDevice, &sci, nullptr, &sem)); } + nameSwapchainObjects(vkbDevice); + extent = m_swapchain.extent; dirty = false; } @@ -187,6 +205,8 @@ void Swapchain::recreateSwapchain( VK_CHECK(vkCreateSemaphore(vkbDevice, &sci, nullptr, &sem)); } + nameSwapchainObjects(vkbDevice); + extent = m_swapchain.extent; dirty = false; } diff --git a/destrum/src/Graphics/Util.cpp b/destrum/src/Graphics/Util.cpp index d461f96..0b77afd 100644 --- a/destrum/src/Graphics/Util.cpp +++ b/destrum/src/Graphics/Util.cpp @@ -188,54 +188,124 @@ void vkutil::bufferHostWriteToTransferReadBarrier( } -void vkutil::addDebugLabel(VkDevice device, VkImage image, const char* label) { +void vkutil::addDebugLabel(VkDevice device, VkObjectType objectType, std::uint64_t objectHandle, const char* label) { + if (!vkSetDebugUtilsObjectNameEXT || device == VK_NULL_HANDLE || objectHandle == 0 || label == nullptr) { + return; + } + const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{ .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, - .objectType = VK_OBJECT_TYPE_IMAGE, - .objectHandle = (std::uint64_t)image, + .objectType = objectType, + .objectHandle = objectHandle, .pObjectName = label, }; - if (vkSetDebugUtilsObjectNameEXT) vkSetDebugUtilsObjectNameEXT(device, &nameInfo); + vkSetDebugUtilsObjectNameEXT(device, &nameInfo); +} + +void vkutil::addDebugLabel(VkDevice device, VkImage image, const char* label) { + addDebugLabel(device, VK_OBJECT_TYPE_IMAGE, reinterpret_cast(image), label); +} + +void vkutil::addDebugLabel(VkDevice device, VkImageView imageView, const char* label) { + addDebugLabel(device, VK_OBJECT_TYPE_IMAGE_VIEW, reinterpret_cast(imageView), label); } void vkutil::addDebugLabel(VkDevice device, VkShaderModule shaderModule, const char* label) { - const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{ - .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, - .objectType = VK_OBJECT_TYPE_SHADER_MODULE, - .objectHandle = (std::uint64_t)shaderModule, - .pObjectName = label, - }; - if (vkSetDebugUtilsObjectNameEXT) vkSetDebugUtilsObjectNameEXT(device, &nameInfo); + addDebugLabel(device, VK_OBJECT_TYPE_SHADER_MODULE, reinterpret_cast(shaderModule), label); } void vkutil::addDebugLabel(VkDevice device, VkPipeline pipeline, const char* label) { - const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{ - .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, - .objectType = VK_OBJECT_TYPE_PIPELINE, - .objectHandle = (std::uint64_t)pipeline, - .pObjectName = label, - }; - if (vkSetDebugUtilsObjectNameEXT) vkSetDebugUtilsObjectNameEXT(device, &nameInfo); + addDebugLabel(device, VK_OBJECT_TYPE_PIPELINE, reinterpret_cast(pipeline), label); +} + +void vkutil::addDebugLabel(VkDevice device, VkPipelineLayout layout, const char* label) { + addDebugLabel(device, VK_OBJECT_TYPE_PIPELINE_LAYOUT, reinterpret_cast(layout), label); } void vkutil::addDebugLabel(VkDevice device, VkBuffer buffer, const char* label) { - const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{ - .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, - .objectType = VK_OBJECT_TYPE_BUFFER, - .objectHandle = (std::uint64_t)buffer, - .pObjectName = label, - }; - if (vkSetDebugUtilsObjectNameEXT) vkSetDebugUtilsObjectNameEXT(device, &nameInfo); + addDebugLabel(device, VK_OBJECT_TYPE_BUFFER, reinterpret_cast(buffer), label); } void vkutil::addDebugLabel(VkDevice device, VkSampler sampler, const char* label) { - const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{ - .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, - .objectType = VK_OBJECT_TYPE_SAMPLER, - .objectHandle = (std::uint64_t)sampler, - .pObjectName = label, + addDebugLabel(device, VK_OBJECT_TYPE_SAMPLER, reinterpret_cast(sampler), label); +} + +void vkutil::addDebugLabel(VkDevice device, VkDescriptorPool descriptorPool, const char* label) { + addDebugLabel(device, VK_OBJECT_TYPE_DESCRIPTOR_POOL, reinterpret_cast(descriptorPool), label); +} + +void vkutil::addDebugLabel(VkDevice device, VkDescriptorSetLayout descriptorSetLayout, const char* label) { + addDebugLabel(device, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, reinterpret_cast(descriptorSetLayout), label); +} + +void vkutil::addDebugLabel(VkDevice device, VkDescriptorSet descriptorSet, const char* label) { + addDebugLabel(device, VK_OBJECT_TYPE_DESCRIPTOR_SET, reinterpret_cast(descriptorSet), label); +} + +void vkutil::addDebugLabel(VkDevice device, VkCommandPool commandPool, const char* label) { + addDebugLabel(device, VK_OBJECT_TYPE_COMMAND_POOL, reinterpret_cast(commandPool), label); +} + +void vkutil::addDebugLabel(VkDevice device, VkCommandBuffer commandBuffer, const char* label) { + addDebugLabel(device, VK_OBJECT_TYPE_COMMAND_BUFFER, reinterpret_cast(commandBuffer), label); +} + +void vkutil::addDebugLabel(VkDevice device, VkFence fence, const char* label) { + addDebugLabel(device, VK_OBJECT_TYPE_FENCE, reinterpret_cast(fence), label); +} + +void vkutil::addDebugLabel(VkDevice device, VkSemaphore semaphore, const char* label) { + addDebugLabel(device, VK_OBJECT_TYPE_SEMAPHORE, reinterpret_cast(semaphore), label); +} + +void vkutil::addDebugLabel(VkDevice device, VkQueue queue, const char* label) { + addDebugLabel(device, VK_OBJECT_TYPE_QUEUE, reinterpret_cast(queue), label); +} + +bool vkutil::debugLabelsEnabled() { + return vkCmdBeginDebugUtilsLabelEXT != nullptr && vkCmdEndDebugUtilsLabelEXT != nullptr; +} + +void vkutil::cmdBeginLabel(VkCommandBuffer cmd, const char* label, const glm::vec4& color) { + if (!debugLabelsEnabled() || label == nullptr) { + return; + } + + const auto labelInfo = VkDebugUtilsLabelEXT{ + .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, + .pLabelName = label, + .color = {color.x, color.y, color.z, color.w}, }; - if (vkSetDebugUtilsObjectNameEXT) vkSetDebugUtilsObjectNameEXT(device, &nameInfo); + vkCmdBeginDebugUtilsLabelEXT(cmd, &labelInfo); +} + +void vkutil::cmdEndLabel(VkCommandBuffer cmd) { + if (!debugLabelsEnabled()) { + return; + } + vkCmdEndDebugUtilsLabelEXT(cmd); +} + +void vkutil::cmdInsertLabel(VkCommandBuffer cmd, const char* label, const glm::vec4& color) { + if (!vkCmdInsertDebugUtilsLabelEXT || label == nullptr) { + return; + } + + const auto labelInfo = VkDebugUtilsLabelEXT{ + .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, + .pLabelName = label, + .color = {color.x, color.y, color.z, color.w}, + }; + vkCmdInsertDebugUtilsLabelEXT(cmd, &labelInfo); +} + +vkutil::ScopedCmdLabel::ScopedCmdLabel(VkCommandBuffer cmd, const char* label, const glm::vec4& color) + : cmd(cmd) { + cmdBeginLabel(cmd, label, color); +} + +vkutil::ScopedCmdLabel::~ScopedCmdLabel() { + cmdEndLabel(cmd); } vkutil::RenderInfo vkutil::createRenderingInfo(const RenderingInfoParams& params) { @@ -319,76 +389,6 @@ VkShaderModule vkutil::loadShaderModule(const std::filesystem::path& path, VkDev return shaderModule; } -void addDebugLabel(VkDevice device, VkImage image, const char* label) { - const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{ - .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, - .objectType = VK_OBJECT_TYPE_IMAGE, - .objectHandle = (std::uint64_t)image, - .pObjectName = label, - }; - if (vkSetDebugUtilsObjectNameEXT) vkSetDebugUtilsObjectNameEXT(device, &nameInfo); -} - -void addDebugLabel(VkDevice device, VkImageView imageView, const char* label) { - const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{ - .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, - .objectType = VK_OBJECT_TYPE_IMAGE_VIEW, - .objectHandle = (std::uint64_t)imageView, - .pObjectName = label, - }; - if (vkSetDebugUtilsObjectNameEXT) vkSetDebugUtilsObjectNameEXT(device, &nameInfo); -} - -void addDebugLabel(VkDevice device, VkShaderModule shaderModule, const char* label) { - const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{ - .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, - .objectType = VK_OBJECT_TYPE_SHADER_MODULE, - .objectHandle = (std::uint64_t)shaderModule, - .pObjectName = label, - }; - if (vkSetDebugUtilsObjectNameEXT) vkSetDebugUtilsObjectNameEXT(device, &nameInfo); -} - -void addDebugLabel(VkDevice device, VkPipeline pipeline, const char* label) { - const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{ - .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, - .objectType = VK_OBJECT_TYPE_PIPELINE, - .objectHandle = (std::uint64_t)pipeline, - .pObjectName = label, - }; - if (vkSetDebugUtilsObjectNameEXT) vkSetDebugUtilsObjectNameEXT(device, &nameInfo); -} - -void addDebugLabel(VkDevice device, VkPipelineLayout layout, const char* label) { - const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{ - .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, - .objectType = VK_OBJECT_TYPE_PIPELINE_LAYOUT, - .objectHandle = (std::uint64_t)layout, - .pObjectName = label, - }; - if (vkSetDebugUtilsObjectNameEXT) vkSetDebugUtilsObjectNameEXT(device, &nameInfo); -} - -void addDebugLabel(VkDevice device, VkBuffer buffer, const char* label) { - const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{ - .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, - .objectType = VK_OBJECT_TYPE_BUFFER, - .objectHandle = (std::uint64_t)buffer, - .pObjectName = label, - }; - if (vkSetDebugUtilsObjectNameEXT) vkSetDebugUtilsObjectNameEXT(device, &nameInfo); -} - -void addDebugLabel(VkDevice device, VkSampler sampler, const char* label) { - const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{ - .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, - .objectType = VK_OBJECT_TYPE_SAMPLER, - .objectHandle = (std::uint64_t)sampler, - .pObjectName = label, - }; - if (vkSetDebugUtilsObjectNameEXT) vkSetDebugUtilsObjectNameEXT(device, &nameInfo); -} - vkutil::RenderInfo createRenderingInfo(const vkutil::RenderingInfoParams& params) { return vkutil::createRenderingInfo(params); } diff --git a/lightkeeper/src/Lightkeeper.cpp b/lightkeeper/src/Lightkeeper.cpp index 2fef89b..44e9740 100644 --- a/lightkeeper/src/Lightkeeper.cpp +++ b/lightkeeper/src/Lightkeeper.cpp @@ -153,65 +153,65 @@ void LightKeeper::customInit() characterModelOptions.loadSkeleton = true; characterModelOptions.loadAnimations = true; - // auto charModel = ModelDoc::LoadModel( - // AssetFS::GetInstance().GetFullPath("engine://cotw-capybara-male/source/capybara.fbx").generic_string(), - // characterModelOptions - // ); - // ModelDocUtils::LogModelDocSummary(charModel, "capybara.fbx"); - // - // const auto& charPrimitive = ModelDocUtils::GetFirstSkinnedPrimitiveOrFirstOrThrow( - // charModel, - // "engine://cotw-capybara-male/source/capybara.fbx" - // ); - // - // const auto charMeshID = resources.meshes().addMesh(gfxDevice, charPrimitive.mesh); - // - // const auto charTexturePath = ModelDocUtils::PickTexturePath( - // charModel, - // charPrimitive, - // AssetFS::GetInstance().GetFullPath( - // "engine://cotw-capybara-male/textures/capybara_male_light_brown_dif.ddsc.DECA.RE.pngballs") - // ); - // - // const auto charTextureID = resources.loadImageFromFile(gfxDevice, charTexturePath); - // // const auto charTextureID = gfxDevice.loadImageFromFile(AssetFS::GetInstance().GetFullPath("engine://char.jpg")); - // const auto charMaterialID = resources.materials().addMaterial({ - // .baseColor = ModelDocUtils::GetImportedBaseColor( - // charModel, charPrimitive), - // .diffuseTexture = charTextureID, - // .name = ModelDocUtils::GetImportedMaterialName( - // charModel, charPrimitive, "CharacterMaterial"), - // }); - // - // const auto charMeshComp = CharObj->AddComponent(); - // charMeshComp->SetMeshID(charMeshID); - // charMeshComp->SetMaterialID(charMaterialID); - // - // const auto animator = CharObj->AddComponent(); - // animator->setSkeleton(std::move(charModel.skeleton)); - // - // std::string firstAnimationName; - // - // for (auto& clip : charModel.animations) - // { - // spdlog::info("Loaded animation: '{}' ({:.2f}s)", clip.name, clip.duration); - // - // if (firstAnimationName.empty()) - // firstAnimationName = clip.name; - // - // animator->addClip(std::make_shared(std::move(clip))); - // } - // - // if (!firstAnimationName.empty()) - // { - // spdlog::info("Playing animation: '{}'", firstAnimationName); - // animator->play(firstAnimationName); - // } - // - // animator->play("capybara_canter_fwd_01|capybara_canter_fwd_01|run"); - // - // CharObj->GetTransform().SetWorldPosition(glm::vec3(0.f, 0.f, 0.f)); - // CharObj->GetTransform().SetWorldScale(0.01f, 0.01f, 0.01f); + auto charModel = ModelDoc::LoadModel( + AssetFS::GetInstance().GetFullPath("engine://cotw-capybara-male/source/capybara.fbx").generic_string(), + characterModelOptions + ); + ModelDocUtils::LogModelDocSummary(charModel, "capybara.fbx"); + + const auto& charPrimitive = ModelDocUtils::GetFirstSkinnedPrimitiveOrFirstOrThrow( + charModel, + "engine://cotw-capybara-male/source/capybara.fbx" + ); + + const auto charMeshID = resources.meshes().addMesh(gfxDevice, charPrimitive.mesh); + + const auto charTexturePath = ModelDocUtils::PickTexturePath( + charModel, + charPrimitive, + AssetFS::GetInstance().GetFullPath( + "engine://cotw-capybara-male/textures/capybara_male_light_brown_dif.ddsc.DECA.RE.pngballs") + ); + + const auto charTextureID = resources.loadImageFromFile(gfxDevice, charTexturePath); + // const auto charTextureID = gfxDevice.loadImageFromFile(AssetFS::GetInstance().GetFullPath("engine://char.jpg")); + const auto charMaterialID = resources.materials().addMaterial({ + .baseColor = ModelDocUtils::GetImportedBaseColor( + charModel, charPrimitive), + .diffuseTexture = charTextureID, + .name = ModelDocUtils::GetImportedMaterialName( + charModel, charPrimitive, "CharacterMaterial"), + }); + + const auto charMeshComp = CharObj->AddComponent(); + charMeshComp->SetMeshID(charMeshID); + charMeshComp->SetMaterialID(charMaterialID); + + const auto animator = CharObj->AddComponent(); + animator->setSkeleton(std::move(charModel.skeleton)); + + std::string firstAnimationName; + + for (auto& clip : charModel.animations) + { + spdlog::info("Loaded animation: '{}' ({:.2f}s)", clip.name, clip.duration); + + if (firstAnimationName.empty()) + firstAnimationName = clip.name; + + animator->addClip(std::make_shared(std::move(clip))); + } + + if (!firstAnimationName.empty()) + { + spdlog::info("Playing animation: '{}'", firstAnimationName); + animator->play(firstAnimationName); + } + + animator->play("capybara_canter_fwd_01|capybara_canter_fwd_01|run"); + + CharObj->GetTransform().SetWorldPosition(glm::vec3(0.f, 0.f, 0.f)); + CharObj->GetTransform().SetWorldScale(0.01f, 0.01f, 0.01f); // ModelDoc::LoadOptions options{}; // options.meshImportMode = ModelDoc::MeshImportMode::MergedPerNode; @@ -450,46 +450,58 @@ void LightKeeper::customInit() triggerObj->GetTransform().SetWorldPosition({0.0f, 0.0f, 0.0f}); // Forward+ test lights: a dim sun and a ring of colored point lights orbiting the origin - auto sunObj = scene.CreateGameObject("Sun"); - auto* sun = sunObj->AddComponent(LightType::Directional); - sun->SetColor(glm::vec3{1.0f, 0.95f, 0.85f}); - sun->SetIntensity(1.0f); - sunObj->GetTransform().SetWorldRotation(glm::vec3{-50.0f, 30.0f, 0.0f}); + // auto sunObj = scene.CreateGameObject("Sun"); + // auto* sun = sunObj->AddComponent(LightType::Directional); + // sun->SetColor(glm::vec3{1.0f, 0.95f, 0.85f}); + // sun->SetIntensity(1.0f); + // sun->SetCastShadows(true); + // sunObj->GetTransform().SetWorldRotation(glm::vec3{-50.0f, 30.0f, 0.0f}); + // + // // static shadow casters, so the sun's shadows are visible without spawning balls + // for (int i = 0; i < 3; i++) + // { + // auto casterObj = scene.CreateGameObject("ShadowCaster " + std::to_string(i)); + // auto* casterMesh = casterObj->AddComponent(); + // casterMesh->SetMeshID(sphereMesh); + // casterMesh->SetMaterialID(sphereMaterial); + // casterObj->GetTransform().SetWorldPosition(glm::vec3{-4.0f + 4.0f * static_cast(i), 0.5f, 3.0f}); + // casterObj->GetTransform().SetWorldScale(glm::vec3{0.015f}); + // } + // + // constexpr int pointLightCount = 32; + // constexpr float pointLightRingRadius = 6.0f; + // for (int i = 0; i < pointLightCount; i++) + // { + // const float t = static_cast(i) / static_cast(pointLightCount); + // const float angle = t * glm::two_pi(); + // + // auto lightObj = scene.CreateGameObject("PointLight " + std::to_string(i)); + // lightObj->GetTransform().SetWorldPosition(glm::vec3{ + // std::cos(angle) * pointLightRingRadius, + // -0.25f, + // std::sin(angle) * pointLightRingRadius + // }); + // + // auto* light = lightObj->AddComponent(LightType::Point); + // light->SetColor(glm::vec3{ + // 0.5f + 0.5f * std::cos(glm::two_pi() * t), + // 0.5f + 0.5f * std::cos(glm::two_pi() * (t + 0.33f)), + // 0.5f + 0.5f * std::cos(glm::two_pi() * (t + 0.67f)) + // }); + // light->SetIntensity(1.0f); + // light->SetRange(3.0f); + // + // auto* orbit = lightObj->AddComponent(pointLightRingRadius, 0.5f); + // orbit->SetAxis(glm::vec3{0.0f, 1.0f, 0.0f}); + // orbit->SetPivotPosition(glm::vec3{0.0f, -0.25f, 0.0f}); + // } - constexpr int pointLightCount = 32; - constexpr float pointLightRingRadius = 6.0f; - for (int i = 0; i < pointLightCount; i++) - { - const float t = static_cast(i) / static_cast(pointLightCount); - const float angle = t * glm::two_pi(); - - auto lightObj = scene.CreateGameObject("PointLight " + std::to_string(i)); - lightObj->GetTransform().SetWorldPosition(glm::vec3{ - std::cos(angle) * pointLightRingRadius, - -0.25f, - std::sin(angle) * pointLightRingRadius - }); - - auto* light = lightObj->AddComponent(LightType::Point); - light->SetColor(glm::vec3{ - 0.5f + 0.5f * std::cos(glm::two_pi() * t), - 0.5f + 0.5f * std::cos(glm::two_pi() * (t + 0.33f)), - 0.5f + 0.5f * std::cos(glm::two_pi() * (t + 0.67f)) - }); - light->SetIntensity(1.0f); - light->SetRange(3.0f); - - auto* orbit = lightObj->AddComponent(pointLightRingRadius, 0.5f); - orbit->SetAxis(glm::vec3{0.0f, 1.0f, 0.0f}); - orbit->SetPivotPosition(glm::vec3{0.0f, -0.25f, 0.0f}); - } - - auto playerObj = scene.CreateGameObject("Player"); - playerObj->GetTransform().SetWorldPosition(glm::vec3(0.0f, 2.0f, -5.0f)); - auto* fps = playerObj->AddComponent(); - m_Player = playerObj; - m_FPSController = fps; - scene.GetPhysics().RegisterGameObject(*playerObj); + // auto playerObj = scene.CreateGameObject("Player"); + // playerObj->GetTransform().SetWorldPosition(glm::vec3(0.0f, 2.0f, -5.0f)); + // auto* fps = playerObj->AddComponent(); + // m_Player = playerObj; + // m_FPSController = fps; + // scene.GetPhysics().RegisterGameObject(*playerObj); const auto texPath = AssetFS::GetInstance().GetFullPath("engine://textures/kobe.png"); texID = resources.loadImageFromFile(gfxDevice, texPath); diff --git a/tests/destrum_tests.cpp b/tests/destrum_tests.cpp index 6aac3bc..1941cef 100644 --- a/tests/destrum_tests.cpp +++ b/tests/destrum_tests.cpp @@ -457,6 +457,7 @@ namespace { light->SetIntensity(4.0f); light->SetRange(7.5f); light->SetConeAngles(0.2f, 0.4f); + light->SetCastShadows(true); const auto data = light->Serialize(); @@ -472,6 +473,8 @@ namespace { "LightComponent must round-trip intensity and range"); check(loaded->GetInnerConeAngle() == 0.2f && loaded->GetOuterConeAngle() == 0.4f, "LightComponent must round-trip its cone angles"); + check(loaded->GetCastShadows(), + "LightComponent must round-trip its cast shadows flag"); loaded->SetConeAngles(0.5f, 0.1f); check(loaded->GetOuterConeAngle() >= loaded->GetInnerConeAngle(),