Compare commits

...
2 Commits
Author SHA1 Message Date
brammie15 170af2a4e9 feat: add imgui texture inspector (Thanks claude) 2026-10-11 01:34:49 +02:00
brammie15 95d1497169 feat: basic shadow map + renderdoc labels 2026-10-11 01:02:26 +02:00
51 changed files with 1571 additions and 245 deletions
+2
View File
@@ -33,6 +33,7 @@ set(SRC_FILES
"src/Graphics/Swapchain.cpp" "src/Graphics/Swapchain.cpp"
"src/Graphics/Util.cpp" "src/Graphics/Util.cpp"
"src/Graphics/RenderResources.cpp" "src/Graphics/RenderResources.cpp"
"src/Graphics/ResourcesInspector.cpp"
"src/Graphics/Managers/VulkanInstanceManager.cpp" "src/Graphics/Managers/VulkanInstanceManager.cpp"
"src/Graphics/Managers/MemoryManager.cpp" "src/Graphics/Managers/MemoryManager.cpp"
@@ -50,6 +51,7 @@ set(SRC_FILES
"src/Graphics/Pipelines/QuadRendererPass.cpp" "src/Graphics/Pipelines/QuadRendererPass.cpp"
"src/Graphics/Pipelines/SkyboxPipeline.cpp" "src/Graphics/Pipelines/SkyboxPipeline.cpp"
"src/Graphics/Pipelines/LightCullingPipeline.cpp" "src/Graphics/Pipelines/LightCullingPipeline.cpp"
"src/Graphics/Pipelines/ShadowMapPipeline.cpp"
"src/Graphics/Pipelines/SkinningPipeline.cpp" "src/Graphics/Pipelines/SkinningPipeline.cpp"
"src/Graphics/Pipelines/ImguiPass.cpp" "src/Graphics/Pipelines/ImguiPass.cpp"
+4
View File
@@ -35,6 +35,10 @@ vec4 sampleTextureCubeLinear(uint texID, vec3 p) {
return texture(nonuniformEXT(samplerCube(textureCubes[texID], samplers[LINEAR_SAMPLER_ID])), 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) { float sampleTextureArrayShadow(uint texID, vec4 p) {
return texture(nonuniformEXT(sampler2DArrayShadow(textureArrays[texID], samplers[SHADOW_SAMPLER_ID])), p); return texture(nonuniformEXT(sampler2DArrayShadow(textureArrays[texID], samplers[SHADOW_SAMPLER_ID])), p);
} }
+2 -2
View File
@@ -44,7 +44,7 @@ float calculateSpotAttenuation(Light light, vec3 l)
} }
vec3 calculateLight(Light light, vec3 fragPos, vec3 n, vec3 v, 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; vec3 l;
float attenuation = 1.0; 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 kd = (vec3(1.0) - F) * (1.0 - metallic);
vec3 diffuse = kd * diffuseColor / PI; 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 // blue -> green -> red, used to visualise how many lights a tile has
+2 -1
View File
@@ -27,7 +27,8 @@ struct Light {
float spotInnerCos; float spotInnerCos;
float spotOuterCos; float spotOuterCos;
vec2 padding; uint castShadows;
float padding;
}; };
layout (buffer_reference, std430) readonly buffer LightsBuffer { layout (buffer_reference, std430) readonly buffer LightsBuffer {
+11 -1
View File
@@ -6,6 +6,7 @@
#include "scene_data.glsl" #include "scene_data.glsl"
#include "mesh_pcs.glsl" #include "mesh_pcs.glsl"
#include "lighting.glsl" #include "lighting.glsl"
#include "shadows.glsl"
layout (location = 0) in vec3 inPos; layout (location = 0) in vec3 inPos;
layout (location = 1) in vec2 inUV; layout (location = 1) in vec2 inUV;
@@ -69,8 +70,17 @@ void main()
uint lightIndex = pcs.sceneData.tileLightIndices.data[tileOffset + 1 + i]; uint lightIndex = pcs.sceneData.tileLightIndices.data[tileOffset + 1 + i];
Light light = pcs.sceneData.lights.data[lightIndex]; 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, fragColor += calculateLight(light, fragPos, n, v,
diffuseColor, roughness, metallic, f0); diffuseColor, roughness, metallic, f0, occlusion);
} }
// emissive // emissive
@@ -33,7 +33,12 @@ layout (buffer_reference, scalar) readonly buffer SceneDataBuffer {
uint numLights; uint numLights;
uint tileCountX; uint tileCountX;
uint debugLightHeatmap; uint debugLightHeatmap;
// directional shadows
uint shadowMapId;
float shadowNormalOffset;
uint padding; uint padding;
mat4 lightViewProj;
} sceneDataBuffer; } sceneDataBuffer;
#endif // SCENE_DATA_GLSL #endif // SCENE_DATA_GLSL
+12
View File
@@ -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);
}
+34
View File
@@ -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
+15
View File
@@ -11,6 +11,7 @@
#include <destrum/Graphics/GfxDevice.h> #include <destrum/Graphics/GfxDevice.h>
#include <destrum/Graphics/Renderer.h> #include <destrum/Graphics/Renderer.h>
#include <destrum/Graphics/Pipelines/ImguiPass.h> #include <destrum/Graphics/Pipelines/ImguiPass.h>
#include <destrum/Graphics/ResourcesInspector.h>
#include <destrum/Input/InputManager.h> #include <destrum/Input/InputManager.h>
@@ -41,6 +42,9 @@ public:
virtual void onWindowResize(int, int) {}; virtual void onWindowResize(int, int) {};
// Called inside the "Windows" menu of the main menu bar, add toggles for the game's own windows here
virtual void customWindowsMenu() {}
protected: protected:
SDL_Window* window{nullptr}; SDL_Window* window{nullptr};
AppParams m_params{}; AppParams m_params{};
@@ -72,6 +76,17 @@ protected:
float m_PhysicsTimeScale = 1.0f; float m_PhysicsTimeScale = 1.0f;
int m_PhysicsStepsLastFrame = 0; int m_PhysicsStepsLastFrame = 0;
struct DebugWindows {
bool stats{true};
bool physics{true};
bool inspector{true};
bool imageCache{false};
};
DebugWindows m_DebugWindows{};
ResourcesInspector m_ResourcesInspector;
void drawWindowsMenu();
void drawPhysicsPanel(float dt, float fixedDt, float accumulator); void drawPhysicsPanel(float dt, float fixedDt, float accumulator);
}; };
@@ -39,6 +39,9 @@ public:
[[nodiscard]] float GetInnerConeAngle() const { return m_InnerConeAngle; } [[nodiscard]] float GetInnerConeAngle() const { return m_InnerConeAngle; }
[[nodiscard]] float GetOuterConeAngle() const { return m_OuterConeAngle; } [[nodiscard]] float GetOuterConeAngle() const { return m_OuterConeAngle; }
void SetCastShadows(bool castShadows) { m_CastShadows = castShadows; }
[[nodiscard]] bool GetCastShadows() const { return m_CastShadows; }
private: private:
LightType m_Type{LightType::Point}; LightType m_Type{LightType::Point};
glm::vec3 m_Color{1.0f}; glm::vec3 m_Color{1.0f};
@@ -46,6 +49,7 @@ private:
float m_Range{10.0f}; float m_Range{10.0f};
float m_InnerConeAngle{0.35f}; float m_InnerConeAngle{0.35f};
float m_OuterConeAngle{0.5f}; float m_OuterConeAngle{0.5f};
bool m_CastShadows{false};
}; };
#endif // LIGHTCOMPONENT_H #endif // LIGHTCOMPONENT_H
+5 -1
View File
@@ -25,6 +25,9 @@ struct Light {
float innerConeAngle{0.35f}; float innerConeAngle{0.35f};
float outerConeAngle{0.5f}; float outerConeAngle{0.5f};
// directional lights only, the first one submitted each frame gets the shadow map
bool castShadows{false};
}; };
struct GPULight { struct GPULight {
@@ -39,7 +42,8 @@ struct GPULight {
float spotInnerCos; float spotInnerCos;
float spotOuterCos; float spotOuterCos;
glm::vec2 padding; std::uint32_t castShadows;
float padding;
}; };
static_assert(sizeof(GPULight) == 64, "GPULight must match the struct in lights.glsl"); static_assert(sizeof(GPULight) == 64, "GPULight must match the struct in lights.glsl");
@@ -1,8 +1,15 @@
#ifndef DESTRUM_IMGUI_PASS_H #ifndef DESTRUM_IMGUI_PASS_H
#define DESTRUM_IMGUI_PASS_H #define DESTRUM_IMGUI_PASS_H
#include <array>
#include <utility>
#include <vector>
#include <vulkan/vulkan.h> #include <vulkan/vulkan.h>
#include <SDL.h> #include <SDL.h>
#include <imgui.h>
#include <destrum/Graphics/Swapchain.h>
class GfxDevice; class GfxDevice;
@@ -33,9 +40,19 @@ public:
[[nodiscard]] bool wantsMouse() const; [[nodiscard]] bool wantsMouse() const;
[[nodiscard]] bool wantsKeyboard() const; [[nodiscard]] bool wantsKeyboard() const;
// Wraps an image view for ImGui::Image in the frame that's being built. Call it every frame
// you draw the image, the descriptor set is freed again once the GPU is done with that frame.
// The image has to be in imageLayout by the time the UI gets rendered at the end of the frame.
[[nodiscard]] ImTextureID getFrameTexture(
VkImageView imageView,
VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
private: private:
void initVulkanBackend(); void initVulkanBackend();
void releaseTextures(std::vector<VkDescriptorSet>& textures);
void releaseAllTextures();
void transitionToColorAttachment( void transitionToColorAttachment(
VkCommandBuffer cmd, VkCommandBuffer cmd,
VkImage image, VkImage image,
@@ -50,6 +67,17 @@ private:
VkDescriptorPool descriptorPool = VK_NULL_HANDLE; VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
VkFormat colorFormat = VK_FORMAT_UNDEFINED; VkFormat colorFormat = VK_FORMAT_UNDEFINED;
struct FrameTexture {
VkImageView imageView{VK_NULL_HANDLE};
VkImageLayout imageLayout{VK_IMAGE_LAYOUT_UNDEFINED};
VkDescriptorSet descriptorSet{VK_NULL_HANDLE};
};
// created while building the current UI, not submitted yet
std::vector<FrameTexture> pendingTextures;
// submitted with a frame, freed once that frame's fence has been waited on
std::array<std::vector<VkDescriptorSet>, FRAMES_IN_FLIGHT> inFlightTextures;
bool initialized = false; bool initialized = false;
bool frameBegun = false; bool frameBegun = false;
bool contextCreated = false; bool contextCreated = false;
@@ -0,0 +1,65 @@
#ifndef SHADOWMAPPIPELINE_H
#define SHADOWMAPPIPELINE_H
#include <cstdint>
#include <memory>
#include <vector>
#include <glm/glm.hpp>
#include <vulkan/vulkan.h>
#include <destrum/Graphics/Pipeline.h>
#include <destrum/Graphics/ids.h>
#include <destrum/Graphics/MeshDrawCommand.h>
#include <destrum/Graphics/Resources/Buffer.h>
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<MeshDrawCommand>& drawCommands,
const std::vector<std::size_t>& 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<Pipeline> 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
@@ -53,6 +53,7 @@ public:
[[nodiscard]] ImageID getWhiteTextureID() const { return whiteImageId; } [[nodiscard]] ImageID getWhiteTextureID() const { return whiteImageId; }
[[nodiscard]] ImageID getErrorTextureID() const { return errorImageId; } [[nodiscard]] ImageID getErrorTextureID() const { return errorImageId; }
[[nodiscard]] ImageID getDefaultNormalTextureID() const { return defaultNormalImageId; }
BindlessSetManager& getBindlessSetManager(); BindlessSetManager& getBindlessSetManager();
VkDescriptorSetLayout getBindlessDescSetLayout() const; VkDescriptorSetLayout getBindlessDescSetLayout() const;
@@ -62,6 +63,10 @@ public:
VkPipelineLayout layout, VkPipelineLayout layout,
VkPipelineBindPoint bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS) const; VkPipelineBindPoint bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS) const;
[[nodiscard]] bool isInitialized() const { return imageCache != nullptr && materialCache != nullptr; }
const ImageCache& images() const { return *imageCache; }
MeshCache& meshes() { return *meshCache; } MeshCache& meshes() { return *meshCache; }
const MeshCache& meshes() const { return *meshCache; } const MeshCache& meshes() const { return *meshCache; }
@@ -10,6 +10,7 @@
#include <destrum/Graphics/Pipelines/LineRenderingPass.h> #include <destrum/Graphics/Pipelines/LineRenderingPass.h>
#include <destrum/Graphics/Pipelines/MeshPipeline.h> #include <destrum/Graphics/Pipelines/MeshPipeline.h>
#include <destrum/Graphics/Pipelines/QuadRendererPass.h> #include <destrum/Graphics/Pipelines/QuadRendererPass.h>
#include <destrum/Graphics/Pipelines/ShadowMapPipeline.h>
#include <destrum/Graphics/ids.h> #include <destrum/Graphics/ids.h>
#include <destrum/Graphics/MeshDrawCommand.h> #include <destrum/Graphics/MeshDrawCommand.h>
#include <destrum/Graphics/Resources/NBuffer.h> #include <destrum/Graphics/Resources/NBuffer.h>
@@ -17,6 +18,7 @@
#include <memory> #include <memory>
#include <optional>
#include <destrum/Graphics/Pipelines/SkinningPipeline.h> #include <destrum/Graphics/Pipelines/SkinningPipeline.h>
@@ -87,6 +89,16 @@ public:
return renderLightHeatmap; return renderLightHeatmap;
} }
void setShadowDistance(float distance)
{
shadowMapPipeline->setShadowDistance(distance);
}
float getShadowDistance() const
{
return shadowMapPipeline->getShadowDistance();
}
RenderResources* getResources() RenderResources* getResources()
{ {
return resources; return resources;
@@ -138,7 +150,11 @@ private:
std::uint32_t numLights; std::uint32_t numLights;
std::uint32_t tileCountX; std::uint32_t tileCountX;
std::uint32_t debugLightHeatmap; std::uint32_t debugLightHeatmap;
std::uint32_t shadowMapId;
float shadowNormalOffset;
std::uint32_t padding; std::uint32_t padding;
glm::mat4 lightViewProj;
}; };
NBuffer sceneDataBuffer; NBuffer sceneDataBuffer;
@@ -150,6 +166,9 @@ private:
std::unique_ptr<SkinningPipeline> skinningPipeline; std::unique_ptr<SkinningPipeline> skinningPipeline;
std::unique_ptr<LightCullingPipeline> lightCullingPipeline; std::unique_ptr<LightCullingPipeline> lightCullingPipeline;
std::unique_ptr<ShadowMapPipeline> shadowMapPipeline;
std::optional<glm::vec3> shadowLightDirection;
bool renderLightHeatmap{false}; bool renderLightHeatmap{false};
bool initialized{false}; bool initialized{false};
@@ -0,0 +1,35 @@
#ifndef RESOURCESINSPECTOR_H
#define RESOURCESINSPECTOR_H
#include <destrum/Graphics/ids.h>
struct GPUImage;
class ImguiPass;
class RenderResources;
// ImGui windows for looking into the render resource caches
class ResourcesInspector final
{
public:
void drawImageCacheWindow(bool* open, const RenderResources& resources, ImguiPass& imguiPass);
private:
struct PreviewInfo
{
bool previewable{false};
const char* reason{""};
};
[[nodiscard]] static PreviewInfo getPreviewInfo(const GPUImage& image);
void drawImagePreview(const GPUImage& image, float maxSize, ImguiPass& imguiPass) const;
void drawImageDetails(ImageID id, const RenderResources& resources) const;
[[nodiscard]] bool matchesFilter(ImageID id, const RenderResources& resources) const;
char filter[128]{};
float thumbnailSize{48.0f};
bool onlyPreviewable{false};
ImageID selectedImageId{NULL_IMAGE_ID};
};
#endif //RESOURCESINSPECTOR_H
@@ -77,6 +77,8 @@ public:
[[nodiscard]] VkFormat getFormat() const { return m_swapchain.image_format; } [[nodiscard]] VkFormat getFormat() const { return m_swapchain.image_format; }
private: private:
void nameSwapchainObjects(VkDevice device) const;
struct FrameData { struct FrameData {
VkSemaphore swapchainSemaphore{VK_NULL_HANDLE}; VkSemaphore swapchainSemaphore{VK_NULL_HANDLE};
VkFence renderFence{VK_NULL_HANDLE}; VkFence renderFence{VK_NULL_HANDLE};
+61
View File
@@ -1,6 +1,8 @@
#ifndef UTIL_H #ifndef UTIL_H
#define UTIL_H #define UTIL_H
#include <cstdint>
#include <cstdio>
#include <filesystem> #include <filesystem>
#include <optional> #include <optional>
#include <stdexcept> #include <stdexcept>
@@ -87,6 +89,9 @@ namespace vkutil {
VkDeviceSize offset = 0, VkDeviceSize offset = 0,
VkDeviceSize size = VK_WHOLE_SIZE); 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, VkImage image, const char* label);
void addDebugLabel(VkDevice device, VkImageView imageView, const char* label); void addDebugLabel(VkDevice device, VkImageView imageView, const char* label);
void addDebugLabel(VkDevice device, VkShaderModule shaderModule, 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, VkPipelineLayout layout, const char* label);
void addDebugLabel(VkDevice device, VkBuffer buffer, 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, 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 <typename... Args>
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 { struct RenderingInfoParams {
VkExtent2D renderExtent; VkExtent2D renderExtent;
+1 -1
View File
@@ -33,7 +33,7 @@ public:
void FixedUpdate(float dt); void FixedUpdate(float dt);
void LateUpdate(float dt); void LateUpdate(float dt);
void Render(const RenderContext& ctx); void Render(const RenderContext& ctx);
void RenderImgui(); void RenderImgui(bool* open = nullptr);
void CleanupDestroyedGameObjects(); void CleanupDestroyedGameObjects();
void Unload(); void Unload();
+1 -1
View File
@@ -23,7 +23,7 @@ public:
void LateUpdate(float dt); void LateUpdate(float dt);
void Render(const RenderContext& ctx); void Render(const RenderContext& ctx);
void RenderImgui(); void RenderImgui(bool* open = nullptr);
void HandleGameObjectDestroy(); void HandleGameObjectDestroy();
void DestroyGameObjects(); void DestroyGameObjects();
+2 -1
View File
@@ -21,7 +21,8 @@ namespace ImGuiUtils {
std::string_view sceneName, std::string_view sceneName,
const std::vector<std::shared_ptr<GameObject>>& objects, const std::vector<std::shared_ptr<GameObject>>& objects,
const std::vector<std::shared_ptr<GameObject>>& pendingAdditions, const std::vector<std::shared_ptr<GameObject>>& pendingAdditions,
ObjectId& selectedObjectId); ObjectId& selectedObjectId,
bool* open = nullptr);
} }
#endif // DESTRUM_IMGUIUTILS_H #endif // DESTRUM_IMGUIUTILS_H
+51 -6
View File
@@ -195,13 +195,31 @@ if (!isRunning) break;
if (m_params.showDebugUi) if (m_params.showDebugUi)
{ {
ZoneScopedN("Debug ImGui"); ZoneScopedN("Debug ImGui");
SceneManager::GetInstance().RenderImgui(); drawWindowsMenu();
ImGui::Begin("Debug"); if (m_DebugWindows.inspector)
ImGui::Text("FPS: %.2f", avgFPS); {
ImGui::End(); SceneManager::GetInstance().RenderImgui(&m_DebugWindows.inspector);
}
drawPhysicsPanel(dt, fixedDt, accumulator); if (m_DebugWindows.stats)
{
if (ImGui::Begin("Debug", &m_DebugWindows.stats))
{
ImGui::Text("FPS: %.2f", avgFPS);
}
ImGui::End();
}
if (m_DebugWindows.physics)
{
drawPhysicsPanel(dt, fixedDt, accumulator);
}
if (m_DebugWindows.imageCache)
{
m_ResourcesInspector.drawImageCacheWindow(&m_DebugWindows.imageCache, resources, imguiPass);
}
TracyPlot("FPS", avgFPS); TracyPlot("FPS", avgFPS);
TracyPlot("Frame Time ms", dt * 1000.0f); TracyPlot("Frame Time ms", dt * 1000.0f);
@@ -359,9 +377,36 @@ void App::cleanup()
} }
} }
void App::drawWindowsMenu()
{
// ImGui appends to the main menu bar, so this ends up next to whatever menus the game added
if (!ImGui::BeginMainMenuBar())
{
return;
}
if (ImGui::BeginMenu("Windows"))
{
ImGui::MenuItem("Debug", nullptr, &m_DebugWindows.stats);
ImGui::MenuItem("Physics", nullptr, &m_DebugWindows.physics);
ImGui::MenuItem("Inspector", nullptr, &m_DebugWindows.inspector);
ImGui::MenuItem("Image Cache", nullptr, &m_DebugWindows.imageCache);
customWindowsMenu();
ImGui::EndMenu();
}
ImGui::EndMainMenuBar();
}
void App::drawPhysicsPanel(float dt, float fixedDt, float accumulator) void App::drawPhysicsPanel(float dt, float fixedDt, float accumulator)
{ {
ImGui::Begin("Physics"); if (!ImGui::Begin("Physics", &m_DebugWindows.physics))
{
ImGui::End();
return;
}
ImGui::Text("Frame dt: %.3f ms", dt * 1000.0f); ImGui::Text("Frame dt: %.3f ms", dt * 1000.0f);
ImGui::Text("Fixed dt: %.3f ms", fixedDt * 1000.0f); ImGui::Text("Fixed dt: %.3f ms", fixedDt * 1000.0f);
+10 -1
View File
@@ -47,6 +47,7 @@ void LightComponent::Render(const RenderContext& ctx)
.range = m_Range, .range = m_Range,
.innerConeAngle = m_InnerConeAngle, .innerConeAngle = m_InnerConeAngle,
.outerConeAngle = m_OuterConeAngle, .outerConeAngle = m_OuterConeAngle,
.castShadows = m_CastShadows,
}); });
} }
@@ -57,7 +58,8 @@ nlohmann::json LightComponent::Serialize() const {
{"intensity", m_Intensity}, {"intensity", m_Intensity},
{"range", m_Range}, {"range", m_Range},
{"innerConeAngle", m_InnerConeAngle}, {"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<float>(); outerConeAngle = data.at("outerConeAngle").get<float>();
} }
SetConeAngles(innerConeAngle, outerConeAngle); SetConeAngles(innerConeAngle, outerConeAngle);
if (data.contains("castShadows")) {
m_CastShadows = data.at("castShadows").get<bool>();
}
} }
void LightComponent::ImGuiInspector() { void LightComponent::ImGuiInspector() {
@@ -106,6 +112,9 @@ void LightComponent::ImGuiInspector() {
} }
if (m_Type == LightType::Directional) { if (m_Type == LightType::Directional) {
ImGui::Checkbox("Cast Shadows", &m_CastShadows);
ImGui::SameLine();
ImGui::TextDisabled("(first directional light only)");
return; return;
} }
@@ -33,6 +33,9 @@ void MeshRendererComponent::Start() {
VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT |
VK_BUFFER_USAGE_VERTEX_BUFFER_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());
} }
} }
@@ -74,6 +74,7 @@ void BindlessSetManager::init(
}; };
VK_CHECK(vkCreateDescriptorPool(device, &poolInfo, nullptr, &descPool)); VK_CHECK(vkCreateDescriptorPool(device, &poolInfo, nullptr, &descPool));
vkutil::addDebugLabel(device, descPool, "bindless descriptor pool");
} }
{ // build desc set layout { // build desc set layout
@@ -111,6 +112,7 @@ void BindlessSetManager::init(
}; };
VK_CHECK(vkCreateDescriptorSetLayout(device, &info, nullptr, &descSetLayout)); VK_CHECK(vkCreateDescriptorSetLayout(device, &info, nullptr, &descSetLayout));
vkutil::addDebugLabel(device, descSetLayout, "bindless descriptor set layout");
} }
{ // alloc desc set { // alloc desc set
@@ -122,6 +124,7 @@ void BindlessSetManager::init(
}; };
VK_CHECK(vkAllocateDescriptorSets(device, &allocInfo, &descSet)); VK_CHECK(vkAllocateDescriptorSets(device, &allocInfo, &descSet));
vkutil::addDebugLabel(device, descSet, "bindless descriptor set");
} }
initDefaultSamplers(device, maxAnisotropy); initDefaultSamplers(device, maxAnisotropy);
+6 -1
View File
@@ -99,8 +99,13 @@ void ComputePipeline::CreateComputePipeline(const std::string& compPath,
throw std::runtime_error("Can't make compute pipeline!"); throw std::runtime_error("Can't make compute pipeline!");
} }
vkutil::addDebugLabel(m_device.getDevice(), m_compShaderModule, compFileName.c_str());
if (!configInfo.name.empty()) { 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());
} }
} }
+4
View File
@@ -156,6 +156,7 @@ void GfxDevice::endFrame(VkCommandBuffer cmd, const GPUImage& drawImage, const E
auto swapchainLayout = VK_IMAGE_LAYOUT_UNDEFINED; { auto swapchainLayout = VK_IMAGE_LAYOUT_UNDEFINED; {
ZoneScopedN("Clear Swapchain Image"); ZoneScopedN("Clear Swapchain Image");
vkutil::ScopedCmdLabel label(cmd, "Clear Swapchain Image", vkutil::labelColors::transfer);
const VkImageSubresourceRange clearRange = vkinit::imageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT); const VkImageSubresourceRange clearRange = vkinit::imageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT);
vkutil::transitionImage(cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_GENERAL); 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) { if (true) {
ZoneScopedN("Copy DrawImage To Swapchain"); ZoneScopedN("Copy DrawImage To Swapchain");
vkutil::ScopedCmdLabel label(cmd, "Copy DrawImage To Swapchain", vkutil::labelColors::transfer);
vkutil::transitionImage( vkutil::transitionImage(
cmd, cmd,
@@ -190,6 +192,7 @@ void GfxDevice::endFrame(VkCommandBuffer cmd, const GPUImage& drawImage, const E
if (props.imguiPass) { if (props.imguiPass) {
ZoneScopedN("ImGui Render"); ZoneScopedN("ImGui Render");
vkutil::ScopedCmdLabel label(cmd, "ImGui Render", vkutil::labelColors::ui);
props.imguiPass->render( props.imguiPass->render(
cmd, cmd,
@@ -202,6 +205,7 @@ void GfxDevice::endFrame(VkCommandBuffer cmd, const GPUImage& drawImage, const E
{ {
ZoneScopedN("Transition Swapchain To Present"); 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); vkutil::transitionImage(cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
swapchainLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; swapchainLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
@@ -40,6 +40,10 @@ void VulkanImmediateExecutor::init(
}; };
VK_CHECK(vkCreateFence(deviceHandle, &fenceCreateInfo, nullptr, &immFence)); 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; initialized = true;
} catch (...) { } catch (...) {
cleanup(deviceHandle); cleanup(deviceHandle);
@@ -26,6 +26,11 @@ void FrameManager::init(VkDevice dev, std::uint32_t queueFamily) {
const auto cmdAllocInfo = vkinit::commandBufferAllocateInfo(commandPool, 1); const auto cmdAllocInfo = vkinit::commandBufferAllocateInfo(commandPool, 1);
auto& mainCommandBuffer = frames[i].commandBuffer; auto& mainCommandBuffer = frames[i].commandBuffer;
VK_CHECK(vkAllocateCommandBuffers(device, &cmdAllocInfo, &mainCommandBuffer)); 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 (...) { } catch (...) {
cleanup(dev); cleanup(dev);
@@ -178,7 +178,9 @@ std::optional<GPUImage> ImageManager::loadImageFromFile(
} }
image.debugName = path.string(); 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; return image;
} }
+3
View File
@@ -156,6 +156,9 @@ void MeshCache::uploadMesh(GfxDevice& gfxDevice, const CPUMesh& cpuMesh, GPUMesh
VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE); 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 = auto skinningStaging =
gfxDevice.createBuffer( gfxDevice.createBuffer(
skinningDataSize, skinningDataSize,
+17 -18
View File
@@ -146,11 +146,11 @@ void Pipeline::CreateGraphicsPipeline(const std::string& vertPath, const std::st
CreateShaderModule(vertCode, &m_vertShaderModule); CreateShaderModule(vertCode, &m_vertShaderModule);
// vkutil::addDebugLabel( vkutil::addDebugLabel(
// m_device.getDevice(), m_device.getDevice(),
// m_fragShaderModule, m_vertShaderModule,
// VertFileName.c_str() VertFileName.c_str()
// ); );
VkPipelineShaderStageCreateInfo vertShaderStageInfo{}; VkPipelineShaderStageCreateInfo vertShaderStageInfo{};
vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; 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); CreateShaderModule(fragCode, &m_fragShaderModule);
// vkutil::addDebugLabel( vkutil::addDebugLabel(
// m_device.getDevice(), m_device.getDevice(),
// m_fragShaderModule, m_fragShaderModule,
// FragFileName.c_str() FragFileName.c_str()
// ); );
VkPipelineShaderStageCreateInfo fragShaderStageInfo{}; VkPipelineShaderStageCreateInfo fragShaderStageInfo{};
@@ -231,18 +231,17 @@ void Pipeline::CreateGraphicsPipeline(const std::string& vertPath, const std::st
pipelineInfo.basePipelineIndex = -1; pipelineInfo.basePipelineIndex = -1;
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
if (!configInfo.name.empty()) {
// DebugLabel::SetObjectName(
// reinterpret_cast<uint64_t>(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) { if (vkCreateGraphicsPipelines(m_device.getDevice(), VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &m_graphicsPipeline) != VK_SUCCESS) {
throw std::runtime_error("Can't make pipeline!"); 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<char>& code, VkShaderModule* shaderModule) const { void Pipeline::CreateShaderModule(const std::vector<char>& code, VkShaderModule* shaderModule) const {
@@ -122,6 +122,15 @@ void ImguiPass::beginFrame() {
return; return;
} }
// the previous UI was built but never rendered (e.g. the frame got skipped while resizing),
// so the GPU never saw these
std::vector<VkDescriptorSet> unusedTextures;
for (const FrameTexture& texture : pendingTextures) {
unusedTextures.push_back(texture.descriptorSet);
}
pendingTextures.clear();
releaseTextures(unusedTextures);
ImGui_ImplVulkan_NewFrame(); ImGui_ImplVulkan_NewFrame();
ImGui_ImplSDL2_NewFrame(); ImGui_ImplSDL2_NewFrame();
@@ -155,6 +164,18 @@ void ImguiPass::render(
return; return;
} }
{
// beginFrame on the gfx device already waited for this frame index' fence,
// so the textures it used last time around can go
auto& inFlight = inFlightTextures[gfx->getCurrentFrameIndex() % FRAMES_IN_FLIGHT];
releaseTextures(inFlight);
for (const FrameTexture& texture : pendingTextures) {
inFlight.push_back(texture.descriptorSet);
}
pendingTextures.clear();
}
ImDrawData* drawData = ImGui::GetDrawData(); ImDrawData* drawData = ImGui::GetDrawData();
if (drawData == nullptr || drawData->CmdListsCount == 0) { if (drawData == nullptr || drawData->CmdListsCount == 0) {
return; return;
@@ -200,6 +221,8 @@ void ImguiPass::onSwapchainRecreated() {
const auto newFormat = gfx->getSwapchainFormat(); const auto newFormat = gfx->getSwapchainFormat();
if (newFormat != colorFormat) { if (newFormat != colorFormat) {
if (vulkanInitialized) { if (vulkanInitialized) {
// the swapchain recreate waited for the device, nothing is in flight anymore
releaseAllTextures();
ImGui_ImplVulkan_Shutdown(); ImGui_ImplVulkan_Shutdown();
vulkanInitialized = false; vulkanInitialized = false;
} }
@@ -224,6 +247,7 @@ void ImguiPass::cleanup() {
} }
if (vulkanInitialized) { if (vulkanInitialized) {
releaseAllTextures();
ImGui_ImplVulkan_Shutdown(); ImGui_ImplVulkan_Shutdown();
vulkanInitialized = false; vulkanInitialized = false;
} }
@@ -293,3 +317,49 @@ void ImguiPass::transitionToColorAttachment(
vkCmdPipelineBarrier2(cmd, &dependencyInfo); vkCmdPipelineBarrier2(cmd, &dependencyInfo);
} }
ImTextureID ImguiPass::getFrameTexture(VkImageView imageView, VkImageLayout imageLayout) {
if (!vulkanInitialized || imageView == VK_NULL_HANDLE) {
return ImTextureID{};
}
for (const FrameTexture& texture : pendingTextures) {
if (texture.imageView == imageView && texture.imageLayout == imageLayout) {
return reinterpret_cast<ImTextureID>(texture.descriptorSet);
}
}
const VkDescriptorSet descriptorSet = ImGui_ImplVulkan_AddTexture(imageView, imageLayout);
if (descriptorSet == VK_NULL_HANDLE) {
return ImTextureID{};
}
pendingTextures.push_back(FrameTexture{
.imageView = imageView,
.imageLayout = imageLayout,
.descriptorSet = descriptorSet,
});
return reinterpret_cast<ImTextureID>(descriptorSet);
}
void ImguiPass::releaseTextures(std::vector<VkDescriptorSet>& textures) {
if (vulkanInitialized) {
for (VkDescriptorSet descriptorSet : textures) {
ImGui_ImplVulkan_RemoveTexture(descriptorSet);
}
}
textures.clear();
}
void ImguiPass::releaseAllTextures() {
std::vector<VkDescriptorSet> textures;
for (const FrameTexture& texture : pendingTextures) {
textures.push_back(texture.descriptorSet);
}
pendingTextures.clear();
releaseTextures(textures);
for (auto& inFlight : inFlightTextures) {
releaseTextures(inFlight);
}
}
@@ -158,7 +158,8 @@ void LightCullingPipeline::appendLight(const Light& light)
.type = static_cast<std::uint32_t>(light.type), .type = static_cast<std::uint32_t>(light.type),
.spotInnerCos = std::cos(light.innerConeAngle), .spotInnerCos = std::cos(light.innerConeAngle),
.spotOuterCos = std::cos(light.outerConeAngle), .spotOuterCos = std::cos(light.outerConeAngle),
.padding = glm::vec2{0.0f}, .castShadows = light.castShadows ? 1u : 0u,
.padding = 0.0f,
}; };
lightsBuffer.append(gpuLight); lightsBuffer.append(gpuLight);
} }
@@ -9,6 +9,7 @@
#include <destrum/Graphics/RenderResources.h> #include <destrum/Graphics/RenderResources.h>
#include <destrum/Graphics/Caches/MeshCache.h> #include <destrum/Graphics/Caches/MeshCache.h>
#include <destrum/Graphics/Frustum.h> #include <destrum/Graphics/Frustum.h>
#include <destrum/Graphics/Util.h>
#include "volk.h" #include "volk.h"
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"
@@ -219,6 +220,16 @@ void MeshPipeline::drawMeshes(
const auto& mesh = meshCache.getMesh(dc.meshId); 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) if (dc.meshId != prevMeshId)
{ {
prevMeshId = dc.meshId; prevMeshId = dc.meshId;
@@ -254,6 +265,8 @@ void MeshPipeline::drawMeshes(
); );
vkCmdDrawIndexed(cmd, mesh.numIndices, 1, 0, 0, 0); vkCmdDrawIndexed(cmd, mesh.numIndices, 1, 0, 0, 0);
vkutil::cmdEndLabel(cmd);
}; };
if (!sortedDrawCommands.empty()) if (!sortedDrawCommands.empty())
@@ -42,12 +42,14 @@ void QuadRendererPass::init(GfxDevice& gfxDevice, RenderResources& resources, Vk
vertexBytes, vertexBytes,
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
VMA_MEMORY_USAGE_AUTO_PREFER_HOST); 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); const std::size_t indexBytes = InitialQuadCapacity * IndicesPerQuad * sizeof(std::uint32_t);
indexBuffer = gfxDevice.createBuffer( indexBuffer = gfxDevice.createBuffer(
indexBytes, indexBytes,
VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
VMA_MEMORY_USAGE_AUTO_PREFER_HOST); VMA_MEMORY_USAGE_AUTO_PREFER_HOST);
vkutil::addDebugLabel(gfxDevice.getDevice(), indexBuffer.buffer, "quad indices");
quadCapacity = InitialQuadCapacity; quadCapacity = InitialQuadCapacity;
@@ -321,6 +323,7 @@ void QuadRendererPass::ensureCapacity(
newCapacity * VerticesPerQuad * sizeof(QuadVertex), newCapacity * VerticesPerQuad * sizeof(QuadVertex),
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
VMA_MEMORY_USAGE_AUTO_PREFER_HOST); VMA_MEMORY_USAGE_AUTO_PREFER_HOST);
vkutil::addDebugLabel(gfxDevice.getDevice(), vertexBuffer.buffer, "quad vertices");
if (indexBuffer.buffer != VK_NULL_HANDLE) { if (indexBuffer.buffer != VK_NULL_HANDLE) {
gfxDevice.destroyBuffer(indexBuffer); gfxDevice.destroyBuffer(indexBuffer);
@@ -329,6 +332,7 @@ void QuadRendererPass::ensureCapacity(
newCapacity * IndicesPerQuad * sizeof(std::uint32_t), newCapacity * IndicesPerQuad * sizeof(std::uint32_t),
VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
VMA_MEMORY_USAGE_AUTO_PREFER_HOST); VMA_MEMORY_USAGE_AUTO_PREFER_HOST);
vkutil::addDebugLabel(gfxDevice.getDevice(), indexBuffer.buffer, "quad indices");
quadCapacity = newCapacity; quadCapacity = newCapacity;
} }
@@ -0,0 +1,265 @@
#include <destrum/Graphics/Pipelines/ShadowMapPipeline.h>
#include <array>
#include <cassert>
#include <cmath>
#include <stdexcept>
#include <glm/gtc/matrix_transform.hpp>
#include <destrum/FS/AssetFS.h>
#include <destrum/Graphics/GfxDevice.h>
#include <destrum/Graphics/RenderResources.h>
#include <destrum/Graphics/Caches/MeshCache.h>
#include <destrum/Graphics/Util.h>
#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<std::uint32_t>(layouts.size());
pipelineLayoutInfo.pSetLayouts = layouts.data();
pipelineLayoutInfo.pushConstantRangeCount = static_cast<std::uint32_t>(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<Pipeline>(
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<float>(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<float>(SHADOW_MAP_SIZE);
return texelSize * 1.5f;
}
void ShadowMapPipeline::draw(
VkCommandBuffer cmd,
const RenderResources& resources,
const GPUBuffer& sceneDataBuffer,
const std::vector<MeshDrawCommand>& drawCommands,
const std::vector<std::size_t>& 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<float>(SHADOW_MAP_SIZE),
.height = static_cast<float>(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);
}
@@ -53,6 +53,9 @@ void SkinningPipeline::init(GfxDevice& gfxDevice) {
MAX_JOINT_MATRICES * sizeof(glm::mat4), MAX_JOINT_MATRICES * sizeof(glm::mat4),
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
VMA_MEMORY_USAGE_CPU_TO_GPU); 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 (...) { } catch (...) {
cleanup(gfxDevice); cleanup(gfxDevice);
@@ -84,6 +87,9 @@ void SkinningPipeline::doSkinning(VkCommandBuffer cmd,
assert(mesh.hasSkeleton); assert(mesh.hasSkeleton);
assert(dc.skinnedMesh); 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{ const auto cs = PushConstants{
.jointMatricesBuffer = getCurrentFrameData(frameIndex).jointMatricesBuffer.buffer.address, .jointMatricesBuffer = getCurrentFrameData(frameIndex).jointMatricesBuffer.buffer.address,
.jointMatricesStartIndex = static_cast<std::uint32_t>(dc.jointMatricesStartIndex), .jointMatricesStartIndex = static_cast<std::uint32_t>(dc.jointMatricesStartIndex),
+2
View File
@@ -130,6 +130,8 @@ ImageID RenderResources::createImage(
if (!debugName.empty()) { if (!debugName.empty()) {
vkutil::addDebugLabel(gfxDevice.getVkDevice(), image.image, debugName.c_str()); 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; image.debugName = debugName;
} }
+58 -2
View File
@@ -44,6 +44,9 @@ void GameRenderer::init(GfxDevice& gfxDevice, RenderResources& _resources, glm::
lightCullingPipeline = std::make_unique<LightCullingPipeline>(); lightCullingPipeline = std::make_unique<LightCullingPipeline>();
lightCullingPipeline->init(gfxDevice, _resources, drawImageSize); lightCullingPipeline->init(gfxDevice, _resources, drawImageSize);
shadowMapPipeline = std::make_unique<ShadowMapPipeline>();
shadowMapPipeline->init(gfxDevice, _resources);
lineRenderingPass = std::make_unique<LineRenderingPass>(); lineRenderingPass = std::make_unique<LineRenderingPass>();
lineRenderingPass->init(gfxDevice, drawImageFormat); lineRenderingPass->init(gfxDevice, drawImageFormat);
@@ -66,6 +69,7 @@ void GameRenderer::beginDrawing(GfxDevice& gfxDevice)
meshDrawCommands.clear(); meshDrawCommands.clear();
skinningPipeline->beginDrawing(gfxDevice.getCurrentFrameIndex()); skinningPipeline->beginDrawing(gfxDevice.getCurrentFrameIndex());
lightCullingPipeline->beginDrawing(gfxDevice.getCurrentFrameIndex()); lightCullingPipeline->beginDrawing(gfxDevice.getCurrentFrameIndex());
shadowLightDirection.reset();
QuadRenderingManager::GetInstance().ClearQuads(); QuadRenderingManager::GetInstance().ClearQuads();
} }
@@ -97,9 +101,11 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
{ {
ZoneScopedN("GameRenderer::draw"); ZoneScopedN("GameRenderer::draw");
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "GameRenderer::draw"); TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "GameRenderer::draw");
vkutil::ScopedCmdLabel frameLabel(cmd, "GameRenderer::draw", vkutil::labelColors::frame);
{ {
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Skinning"); TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Skinning");
vkutil::ScopedCmdLabel label(cmd, "Skinning", vkutil::labelColors::compute);
skinningPipeline->flushCurrentFrame( skinningPipeline->flushCurrentFrame(
cmd, cmd,
@@ -118,10 +124,17 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
{ {
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Lights Buffer Flush"); TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Lights Buffer Flush");
vkutil::ScopedCmdLabel label(cmd, "Lights Buffer Flush", vkutil::labelColors::transfer);
lightCullingPipeline->flushCurrentFrame(cmd, gfxDevice); 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 // TODO: there has to be a better way to do this
const auto gpuSceneData = GPUSceneData{ const auto gpuSceneData = GPUSceneData{
.view = sceneData.camera.GetViewMatrix(), .view = sceneData.camera.GetViewMatrix(),
@@ -139,11 +152,15 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
.numLights = lightCullingPipeline->getNumLights(), .numLights = lightCullingPipeline->getNumLights(),
.tileCountX = lightCullingPipeline->getTileCount().x, .tileCountX = lightCullingPipeline->getTileCount().x,
.debugLightHeatmap = renderLightHeatmap ? 1u : 0u, .debugLightHeatmap = renderLightHeatmap ? 1u : 0u,
.shadowMapId = static_cast<std::uint32_t>(shadowMapPipeline->getShadowMapId()),
.shadowNormalOffset = shadowMapPipeline->getNormalOffset(),
.padding = 0, .padding = 0,
.lightViewProj = lightViewProj,
}; };
{ {
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Material Buffer Barrier"); TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Material Buffer Barrier");
vkutil::ScopedCmdLabel label(cmd, "Material Buffer Barrier", vkutil::labelColors::transfer);
vkutil::bufferHostWriteToShaderReadBarrier( vkutil::bufferHostWriteToShaderReadBarrier(
cmd, cmd,
@@ -155,6 +172,7 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
{ {
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Upload Scene Data"); TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Upload Scene Data");
vkutil::ScopedCmdLabel label(cmd, "Upload Scene Data", vkutil::labelColors::transfer);
sceneDataBuffer.uploadNewData( sceneDataBuffer.uploadNewData(
cmd, cmd,
@@ -189,8 +207,22 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
depthImage.setLayout(VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL); 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"); TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Depth Prepass");
vkutil::ScopedCmdLabel label(cmd, "Depth Prepass", vkutil::labelColors::depth);
auto depthPrepassInfo = vkutil::createRenderingInfo({ auto depthPrepassInfo = vkutil::createRenderingInfo({
.renderExtent = drawImage.getExtent2D(), .renderExtent = drawImage.getExtent2D(),
@@ -215,6 +247,7 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
{ {
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Light Culling"); TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Light Culling");
vkutil::ScopedCmdLabel label(cmd, "Light Culling", vkutil::labelColors::compute);
vkutil::transitionImage( vkutil::transitionImage(
cmd, cmd,
@@ -247,6 +280,8 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
renderInfo.renderingInfo.pColorAttachments = &renderInfo.colorAttachment; renderInfo.renderingInfo.pColorAttachments = &renderInfo.colorAttachment;
renderInfo.renderingInfo.pDepthAttachment = &renderInfo.depthAttachment; renderInfo.renderingInfo.pDepthAttachment = &renderInfo.depthAttachment;
vkutil::cmdBeginLabel(cmd, "Forward Pass", vkutil::labelColors::opaque);
{ {
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "vkCmdBeginRendering"); TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "vkCmdBeginRendering");
@@ -255,6 +290,7 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
{ {
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "MeshPipeline::draw"); TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "MeshPipeline::draw");
vkutil::ScopedCmdLabel label(cmd, "MeshPipeline::draw", vkutil::labelColors::opaque);
meshPipeline->draw( meshPipeline->draw(
cmd, cmd,
@@ -268,6 +304,7 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
{ {
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "SkyboxPipeline::draw"); TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "SkyboxPipeline::draw");
vkutil::ScopedCmdLabel label(cmd, "SkyboxPipeline::draw", vkutil::labelColors::sky);
skyboxPipeline->draw(cmd, *resources, camera); skyboxPipeline->draw(cmd, *resources, camera);
} }
@@ -278,8 +315,11 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
vkCmdEndRendering(cmd); vkCmdEndRendering(cmd);
} }
vkutil::cmdEndLabel(cmd);
{ {
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "LineRenderingPass::draw"); TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "LineRenderingPass::draw");
vkutil::ScopedCmdLabel label(cmd, "LineRenderingPass::draw", vkutil::labelColors::debug);
lineRenderingPass->draw( lineRenderingPass->draw(
cmd, cmd,
@@ -291,6 +331,7 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
{ {
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "QuadRendererPass::draw"); TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "QuadRendererPass::draw");
vkutil::ScopedCmdLabel label(cmd, "QuadRendererPass::draw", vkutil::labelColors::ui);
quadRendererPass->draw( quadRendererPass->draw(
cmd, cmd,
@@ -302,7 +343,6 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
static_cast<float>(drawImage.extent.height), static_cast<float>(drawImage.extent.height),
0.0f, -1.0f, 1.0f)); 0.0f, -1.0f, 1.0f));
} }
// vkutil::cmdEndLabel(cmd);
} }
void GameRenderer::drawQuads( void GameRenderer::drawQuads(
@@ -335,6 +375,9 @@ void GameRenderer::cleanup(GfxDevice& gfxDevice)
if (lightCullingPipeline) if (lightCullingPipeline)
lightCullingPipeline->cleanup(gfxDevice); lightCullingPipeline->cleanup(gfxDevice);
if (shadowMapPipeline)
shadowMapPipeline->cleanup(device);
if (skyboxPipeline) if (skyboxPipeline)
skyboxPipeline->cleanup(device); skyboxPipeline->cleanup(device);
@@ -358,6 +401,8 @@ void GameRenderer::cleanup(GfxDevice& gfxDevice)
lineRenderingPass.reset(); lineRenderingPass.reset();
quadRendererPass.reset(); quadRendererPass.reset();
lightCullingPipeline.reset(); lightCullingPipeline.reset();
shadowMapPipeline.reset();
shadowLightDirection.reset();
pendingMaterialUploads.clear(); pendingMaterialUploads.clear();
meshDrawCommands.clear(); meshDrawCommands.clear();
sortedMeshDrawCommands.clear(); sortedMeshDrawCommands.clear();
@@ -402,7 +447,18 @@ void GameRenderer::drawSkinnedMesh(MeshID id,
void GameRenderer::submitLight(const Light& light) 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 const GPUImage& GameRenderer::getDrawImage() const
@@ -71,6 +71,8 @@ void CubeMap::RenderToCubemap(
} }
gfxDevice.getImmediateExecuter().immediateSubmit([&](VkCommandBuffer cmd) { gfxDevice.getImmediateExecuter().immediateSubmit([&](VkCommandBuffer cmd) {
vkutil::ScopedCmdLabel label(cmd, "HDRI To Cubemap", vkutil::labelColors::sky);
VkImageMemoryBarrier barrier{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER}; VkImageMemoryBarrier barrier{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};
barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
@@ -108,6 +110,8 @@ void CubeMap::RenderToCubemap(
scissor.extent = {size, size}; scissor.extent = {size, size};
for (std::uint32_t face = 0; face < 6; ++face) { for (std::uint32_t face = 0; face < 6; ++face) {
vkutil::cmdBeginLabelFormatted(cmd, vkutil::labelColors::draw, "Cubemap Face %u", face);
PC pc{}; PC pc{};
pc.viewMtx = viewMatrices[face]; pc.viewMtx = viewMatrices[face];
pc.projMtx = m_projection; pc.projMtx = m_projection;
@@ -153,6 +157,8 @@ void CubeMap::RenderToCubemap(
vkCmdDraw(cmd, 36, 1, 0, 0); vkCmdDraw(cmd, 36, 1, 0, 0);
vkCmdEndRendering(cmd); vkCmdEndRendering(cmd);
vkutil::cmdEndLabel(cmd);
} }
barrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; barrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
@@ -199,6 +205,8 @@ void CubeMap::CreateCubeMap(
.mipMap = false, .mipMap = false,
.isCubemap = true .isCubemap = true
}); });
cubeMapImage.debugName = "skybox cubemap";
vkutil::addDebugLabel(gfxDevice.getDevice(), cubeMapImage.image, cubeMapImage.debugName.c_str());
std::array<VkImageView, 6> faceViews{}; std::array<VkImageView, 6> faceViews{};
@@ -220,6 +228,9 @@ void CubeMap::CreateCubeMap(
&viewInfo, &viewInfo,
nullptr, nullptr,
&faceViews[face])); &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); spdlog::info("HDRI image id = {}", m_hdrImage);
@@ -305,6 +316,7 @@ void CubeMap::InitCubemapPipeline(
PipelineConfigInfo pipelineConfig{}; PipelineConfigInfo pipelineConfig{};
Pipeline::DefaultPipelineConfigInfo(pipelineConfig); Pipeline::DefaultPipelineConfigInfo(pipelineConfig);
pipelineConfig.name = "HDRI To Cubemap Pipeline";
pipelineConfig.vertexAttributeDescriptions = {}; pipelineConfig.vertexAttributeDescriptions = {};
pipelineConfig.vertexBindingDescriptions = {}; pipelineConfig.vertexBindingDescriptions = {};
+439
View File
@@ -0,0 +1,439 @@
#include <destrum/Graphics/ResourcesInspector.h>
#include <algorithm>
#include <cctype>
#include <charconv>
#include <system_error>
#include <cstring>
#include <string>
#include <string_view>
#include <vector>
#include <imgui.h>
#include <vulkan/vk_enum_string_helper.h>
#include <destrum/Graphics/Caches/ImageCache.h>
#include <destrum/Graphics/Caches/MaterialCache.h>
#include <destrum/Graphics/Pipelines/ImguiPass.h>
#include <destrum/Graphics/RenderResources.h>
namespace
{
[[nodiscard]] const char* StripPrefix(const char* name, const char* prefix)
{
const std::size_t length = std::strlen(prefix);
return std::strncmp(name, prefix, length) == 0 ? name + length : name;
}
[[nodiscard]] const char* FormatName(VkFormat format)
{
return StripPrefix(string_VkFormat(format), "VK_FORMAT_");
}
[[nodiscard]] const char* LayoutName(VkImageLayout layout)
{
return StripPrefix(string_VkImageLayout(layout), "VK_IMAGE_LAYOUT_");
}
[[nodiscard]] std::string UsageNames(VkImageUsageFlags usage)
{
struct UsageName
{
VkImageUsageFlagBits bit;
const char* name;
};
constexpr UsageName usageNames[] = {
{VK_IMAGE_USAGE_SAMPLED_BIT, "sampled"},
{VK_IMAGE_USAGE_STORAGE_BIT, "storage"},
{VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, "color attachment"},
{VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, "depth attachment"},
{VK_IMAGE_USAGE_TRANSFER_SRC_BIT, "transfer src"},
{VK_IMAGE_USAGE_TRANSFER_DST_BIT, "transfer dst"},
};
std::string result;
for (const UsageName& usageName : usageNames)
{
if ((usage & usageName.bit) == 0)
{
continue;
}
if (!result.empty())
{
result += ", ";
}
result += usageName.name;
}
return result.empty() ? "none" : result;
}
[[nodiscard]] bool IsDepthFormat(VkFormat format)
{
switch (format)
{
case VK_FORMAT_D16_UNORM:
case VK_FORMAT_X8_D24_UNORM_PACK32:
case VK_FORMAT_D32_SFLOAT:
case VK_FORMAT_D16_UNORM_S8_UINT:
case VK_FORMAT_D24_UNORM_S8_UINT:
case VK_FORMAT_D32_SFLOAT_S8_UINT:
return true;
default:
return false;
}
}
[[nodiscard]] bool ContainsCaseInsensitive(std::string_view haystack, std::string_view needle)
{
const auto it = std::search(
haystack.begin(), haystack.end(),
needle.begin(), needle.end(),
[](char a, char b)
{
return std::tolower(static_cast<unsigned char>(a)) == std::tolower(static_cast<unsigned char>(b));
});
return it != haystack.end();
}
[[nodiscard]] const char* TextureIntentName(TextureIntent intent)
{
switch (intent)
{
case TextureIntent::ColorSrgb: return "color (sRGB)";
case TextureIntent::DataLinear: return "data (linear)";
}
return "unknown";
}
}
ResourcesInspector::PreviewInfo ResourcesInspector::getPreviewInfo(const GPUImage& image)
{
if (image.image == VK_NULL_HANDLE || image.imageView == VK_NULL_HANDLE)
{
return {false, "no image view"};
}
if ((image.usage & VK_IMAGE_USAGE_SAMPLED_BIT) == 0)
{
return {false, "not sampleable"};
}
if (image.isCubemap)
{
return {false, "cubemap"};
}
if (image.numLayers > 1)
{
return {false, "image array"};
}
if (image.getLayout() == VK_IMAGE_LAYOUT_UNDEFINED)
{
return {false, "never written"};
}
// ImGui reads the image when the UI gets rendered at the very end of the frame. Render targets
// like the draw / depth image are in an attachment or transfer layout by then.
if (image.getLayout() != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
{
return {false, "render target"};
}
return {true, ""};
}
void ResourcesInspector::drawImagePreview(const GPUImage& image, float maxSize, ImguiPass& imguiPass) const
{
const float width = static_cast<float>(image.extent.width);
const float height = static_cast<float>(image.extent.height);
const float scale = maxSize / std::max({width, height, 1.0f});
const ImVec2 size{std::max(width * scale, 1.0f), std::max(height * scale, 1.0f)};
const PreviewInfo preview = getPreviewInfo(image);
if (preview.previewable)
{
const ImTextureID textureId = imguiPass.getFrameTexture(image.imageView);
if (textureId != ImTextureID{})
{
ImGui::ImageWithBg(textureId, size, ImVec2{0.0f, 0.0f}, ImVec2{1.0f, 1.0f}, ImVec4{0.1f, 0.1f, 0.1f, 1.0f});
return;
}
}
// placeholder box with the reason it can't be shown
const ImVec2 boxSize{maxSize, std::min(maxSize, std::max(size.y, ImGui::GetTextLineHeight() * 2.0f))};
const ImVec2 min = ImGui::GetCursorScreenPos();
const ImVec2 max{min.x + boxSize.x, min.y + boxSize.y};
ImDrawList* drawList = ImGui::GetWindowDrawList();
drawList->AddRectFilled(min, max, ImGui::GetColorU32(ImGuiCol_FrameBg));
drawList->AddRect(min, max, ImGui::GetColorU32(ImGuiCol_Border));
// small thumbnails just get a dash, the reason is in the tooltip
const char* text = ImGui::CalcTextSize(preview.reason).x <= boxSize.x - 8.0f ? preview.reason : "-";
const ImVec2 textSize = ImGui::CalcTextSize(text);
drawList->AddText(
ImVec2{min.x + (boxSize.x - textSize.x) * 0.5f, min.y + (boxSize.y - textSize.y) * 0.5f},
ImGui::GetColorU32(ImGuiCol_TextDisabled),
text);
ImGui::Dummy(boxSize);
}
void ResourcesInspector::drawImageDetails(ImageID id, const RenderResources& resources) const
{
const ImageCache& cache = resources.images();
const GPUImage& image = cache.images[id];
ImGui::Text("Image ID: %u", static_cast<unsigned>(id));
ImGui::TextWrapped("Name: %s", image.debugName.empty() ? "<unnamed>" : image.debugName.c_str());
if (id == resources.getWhiteTextureID())
{
ImGui::TextColored(ImVec4{0.6f, 0.8f, 1.0f, 1.0f}, "Default white (also metallic/roughness + emissive default)");
}
if (id == resources.getErrorTextureID())
{
ImGui::TextColored(ImVec4{1.0f, 0.4f, 0.8f, 1.0f}, "Error texture (used when a load fails)");
}
if (id == resources.getDefaultNormalTextureID())
{
ImGui::TextColored(ImVec4{0.6f, 0.8f, 1.0f, 1.0f}, "Default normal map");
}
ImGui::Separator();
ImGui::Text("Size: %u x %u", image.extent.width, image.extent.height);
ImGui::Text("Format: %s%s", FormatName(image.format), IsDepthFormat(image.format) ? " (shown as red)" : "");
ImGui::Text("Mips: %u Layers: %u%s", image.mipLevels, image.numLayers, image.isCubemap ? " cubemap" : "");
ImGui::Text("Layout: %s", LayoutName(image.getLayout()));
if (const PreviewInfo preview = getPreviewInfo(image); !preview.previewable)
{
ImGui::TextDisabled("No preview: %s", preview.reason);
}
ImGui::TextWrapped("Usage: %s", UsageNames(image.usage).c_str());
if (const auto it = cache.loadedImagesInfo.find(id); it != cache.loadedImagesInfo.end())
{
ImGui::Separator();
ImGui::TextWrapped("File: %s", it->second.path.generic_string().c_str());
ImGui::Text("Intent: %s%s", TextureIntentName(it->second.intent), it->second.mipMap ? ", mipmapped" : "");
}
const MaterialCache& materials = resources.materials();
std::vector<std::size_t> users;
for (std::size_t materialId = 0; materialId < materials.materials.size(); ++materialId)
{
if (materials.materials[materialId].diffuseTexture == id)
{
users.push_back(materialId);
}
}
if (!users.empty())
{
ImGui::Separator();
ImGui::Text("Diffuse texture of %zu material(s):", users.size());
constexpr std::size_t maxListed = 8;
for (std::size_t i = 0; i < std::min(users.size(), maxListed); ++i)
{
const Material& material = materials.materials[users[i]];
ImGui::BulletText(
"#%zu %s",
users[i],
material.name.empty() ? "<unnamed>" : material.name.c_str());
}
if (users.size() > maxListed)
{
ImGui::TextDisabled("... and %zu more", users.size() - maxListed);
}
}
}
bool ResourcesInspector::matchesFilter(ImageID id, const RenderResources& resources) const
{
const std::string_view filterText{filter};
if (filterText.empty())
{
return true;
}
// a plain number matches the image id exactly
unsigned filterId = 0;
const auto [end, error] = std::from_chars(filterText.data(), filterText.data() + filterText.size(), filterId);
if (error == std::errc{} && end == filterText.data() + filterText.size())
{
return filterId == id;
}
const ImageCache& cache = resources.images();
if (ContainsCaseInsensitive(cache.images[id].debugName, filterText))
{
return true;
}
if (const auto it = cache.loadedImagesInfo.find(id); it != cache.loadedImagesInfo.end())
{
return ContainsCaseInsensitive(it->second.path.generic_string(), filterText);
}
return false;
}
void ResourcesInspector::drawImageCacheWindow(bool* open, const RenderResources& resources, ImguiPass& imguiPass)
{
if (!ImGui::Begin("Image Cache", open))
{
ImGui::End();
return;
}
if (!resources.isInitialized())
{
ImGui::TextDisabled("Render resources aren't initialized yet");
ImGui::End();
return;
}
const ImageCache& cache = resources.images();
std::vector<ImageID> visibleIds;
std::size_t imageCount = 0;
for (std::size_t i = 0; i < cache.images.size(); ++i)
{
const auto id = static_cast<ImageID>(i);
const GPUImage& image = cache.images[i];
if (image.image == VK_NULL_HANDLE)
{
continue;
}
++imageCount;
if (!matchesFilter(id, resources))
{
continue;
}
if (onlyPreviewable && !getPreviewInfo(image).previewable)
{
continue;
}
visibleIds.push_back(id);
}
ImGui::Text("%zu images (bindless capacity %u)", imageCount, cache.getMaxImageCount());
ImGui::SetNextItemWidth(220.0f);
ImGui::InputTextWithHint("##filter", "Filter by name, path or ID", filter, sizeof(filter));
ImGui::SameLine();
ImGui::SetNextItemWidth(140.0f);
ImGui::SliderFloat("Thumbnail", &thumbnailSize, 24.0f, 256.0f, "%.0f px");
ImGui::SameLine();
ImGui::Checkbox("Previewable only", &onlyPreviewable);
if (selectedImageId != NULL_IMAGE_ID &&
(selectedImageId >= cache.images.size() || cache.images[selectedImageId].image == VK_NULL_HANDLE))
{
selectedImageId = NULL_IMAGE_ID;
}
if (selectedImageId != NULL_IMAGE_ID)
{
if (ImGui::CollapsingHeader("Selected Image", ImGuiTreeNodeFlags_DefaultOpen))
{
drawImagePreview(cache.images[selectedImageId], 256.0f, imguiPass);
ImGui::SameLine();
ImGui::BeginGroup();
drawImageDetails(selectedImageId, resources);
if (ImGui::SmallButton("Clear selection"))
{
selectedImageId = NULL_IMAGE_ID;
}
ImGui::EndGroup();
}
}
constexpr ImGuiTableFlags tableFlags =
ImGuiTableFlags_Borders |
ImGuiTableFlags_RowBg |
ImGuiTableFlags_ScrollY |
ImGuiTableFlags_Resizable |
ImGuiTableFlags_SizingStretchProp;
if (ImGui::BeginTable("##images", 6, tableFlags))
{
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed, 40.0f);
ImGui::TableSetupColumn("Preview", ImGuiTableColumnFlags_WidthFixed, thumbnailSize + 4.0f);
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch, 3.0f);
ImGui::TableSetupColumn("Size", ImGuiTableColumnFlags_WidthFixed, 90.0f);
ImGui::TableSetupColumn("Format", ImGuiTableColumnFlags_WidthStretch, 1.5f);
ImGui::TableSetupColumn("Layout", ImGuiTableColumnFlags_WidthStretch, 1.5f);
ImGui::TableHeadersRow();
// only the visible rows get a texture descriptor
const float rowHeight = thumbnailSize + ImGui::GetStyle().CellPadding.y * 2.0f;
ImGuiListClipper clipper;
clipper.Begin(static_cast<int>(visibleIds.size()), rowHeight);
while (clipper.Step())
{
for (int row = clipper.DisplayStart; row < clipper.DisplayEnd; ++row)
{
const ImageID id = visibleIds[static_cast<std::size_t>(row)];
const GPUImage& image = cache.images[id];
ImGui::PushID(static_cast<int>(id));
ImGui::TableNextRow(ImGuiTableRowFlags_None, thumbnailSize);
ImGui::TableNextColumn();
const std::string idLabel = std::to_string(id);
if (ImGui::Selectable(
idLabel.c_str(),
selectedImageId == id,
ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap,
ImVec2{0.0f, thumbnailSize}))
{
selectedImageId = selectedImageId == id ? NULL_IMAGE_ID : id;
}
if (ImGui::BeginItemTooltip())
{
drawImagePreview(image, 256.0f, imguiPass);
drawImageDetails(id, resources);
ImGui::EndTooltip();
}
ImGui::TableNextColumn();
drawImagePreview(image, thumbnailSize, imguiPass);
ImGui::TableNextColumn();
if (const auto it = cache.loadedImagesInfo.find(id); it != cache.loadedImagesInfo.end())
{
// loaded images are named after their full path, the file name is enough here
ImGui::TextWrapped("%s", it->second.path.filename().generic_string().c_str());
}
else
{
ImGui::TextWrapped("%s", image.debugName.empty() ? "<unnamed>" : image.debugName.c_str());
}
ImGui::TableNextColumn();
ImGui::Text("%u x %u", image.extent.width, image.extent.height);
if (image.mipLevels > 1)
{
ImGui::TextDisabled("%u mips", image.mipLevels);
}
ImGui::TableNextColumn();
ImGui::TextUnformatted(FormatName(image.format));
ImGui::TableNextColumn();
ImGui::TextUnformatted(LayoutName(image.getLayout()));
ImGui::PopID();
}
}
ImGui::EndTable();
}
ImGui::End();
}
+20
View File
@@ -21,6 +21,22 @@ void Swapchain::initSync(VkDevice device) {
for (std::uint32_t i = 0; i < FRAMES_IN_FLIGHT; ++i) { for (std::uint32_t i = 0; i < FRAMES_IN_FLIGHT; ++i) {
VK_CHECK(vkCreateFence(device, &fenceCreateInfo, nullptr, &frames[i].renderFence)); VK_CHECK(vkCreateFence(device, &fenceCreateInfo, nullptr, &frames[i].renderFence));
VK_CHECK(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &frames[i].swapchainSemaphore)); 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)); VK_CHECK(vkCreateSemaphore(vkbDevice, &sci, nullptr, &sem));
} }
nameSwapchainObjects(vkbDevice);
extent = m_swapchain.extent; extent = m_swapchain.extent;
dirty = false; dirty = false;
} }
@@ -187,6 +205,8 @@ void Swapchain::recreateSwapchain(
VK_CHECK(vkCreateSemaphore(vkbDevice, &sci, nullptr, &sem)); VK_CHECK(vkCreateSemaphore(vkbDevice, &sci, nullptr, &sem));
} }
nameSwapchainObjects(vkbDevice);
extent = m_swapchain.extent; extent = m_swapchain.extent;
dirty = false; dirty = false;
} }
+101 -101
View File
@@ -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{ const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
.objectType = VK_OBJECT_TYPE_IMAGE, .objectType = objectType,
.objectHandle = (std::uint64_t)image, .objectHandle = objectHandle,
.pObjectName = label, .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<std::uint64_t>(image), label);
}
void vkutil::addDebugLabel(VkDevice device, VkImageView imageView, const char* label) {
addDebugLabel(device, VK_OBJECT_TYPE_IMAGE_VIEW, reinterpret_cast<std::uint64_t>(imageView), label);
} }
void vkutil::addDebugLabel(VkDevice device, VkShaderModule shaderModule, const char* label) { void vkutil::addDebugLabel(VkDevice device, VkShaderModule shaderModule, const char* label) {
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{ addDebugLabel(device, VK_OBJECT_TYPE_SHADER_MODULE, reinterpret_cast<std::uint64_t>(shaderModule), label);
.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 vkutil::addDebugLabel(VkDevice device, VkPipeline pipeline, const char* label) { void vkutil::addDebugLabel(VkDevice device, VkPipeline pipeline, const char* label) {
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{ addDebugLabel(device, VK_OBJECT_TYPE_PIPELINE, reinterpret_cast<std::uint64_t>(pipeline), label);
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, }
.objectType = VK_OBJECT_TYPE_PIPELINE,
.objectHandle = (std::uint64_t)pipeline, void vkutil::addDebugLabel(VkDevice device, VkPipelineLayout layout, const char* label) {
.pObjectName = label, addDebugLabel(device, VK_OBJECT_TYPE_PIPELINE_LAYOUT, reinterpret_cast<std::uint64_t>(layout), label);
};
if (vkSetDebugUtilsObjectNameEXT) vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
} }
void vkutil::addDebugLabel(VkDevice device, VkBuffer buffer, const char* label) { void vkutil::addDebugLabel(VkDevice device, VkBuffer buffer, const char* label) {
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{ addDebugLabel(device, VK_OBJECT_TYPE_BUFFER, reinterpret_cast<std::uint64_t>(buffer), label);
.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 vkutil::addDebugLabel(VkDevice device, VkSampler sampler, const char* label) { void vkutil::addDebugLabel(VkDevice device, VkSampler sampler, const char* label) {
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{ addDebugLabel(device, VK_OBJECT_TYPE_SAMPLER, reinterpret_cast<std::uint64_t>(sampler), label);
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, }
.objectType = VK_OBJECT_TYPE_SAMPLER,
.objectHandle = (std::uint64_t)sampler, void vkutil::addDebugLabel(VkDevice device, VkDescriptorPool descriptorPool, const char* label) {
.pObjectName = label, addDebugLabel(device, VK_OBJECT_TYPE_DESCRIPTOR_POOL, reinterpret_cast<std::uint64_t>(descriptorPool), label);
}
void vkutil::addDebugLabel(VkDevice device, VkDescriptorSetLayout descriptorSetLayout, const char* label) {
addDebugLabel(device, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, reinterpret_cast<std::uint64_t>(descriptorSetLayout), label);
}
void vkutil::addDebugLabel(VkDevice device, VkDescriptorSet descriptorSet, const char* label) {
addDebugLabel(device, VK_OBJECT_TYPE_DESCRIPTOR_SET, reinterpret_cast<std::uint64_t>(descriptorSet), label);
}
void vkutil::addDebugLabel(VkDevice device, VkCommandPool commandPool, const char* label) {
addDebugLabel(device, VK_OBJECT_TYPE_COMMAND_POOL, reinterpret_cast<std::uint64_t>(commandPool), label);
}
void vkutil::addDebugLabel(VkDevice device, VkCommandBuffer commandBuffer, const char* label) {
addDebugLabel(device, VK_OBJECT_TYPE_COMMAND_BUFFER, reinterpret_cast<std::uint64_t>(commandBuffer), label);
}
void vkutil::addDebugLabel(VkDevice device, VkFence fence, const char* label) {
addDebugLabel(device, VK_OBJECT_TYPE_FENCE, reinterpret_cast<std::uint64_t>(fence), label);
}
void vkutil::addDebugLabel(VkDevice device, VkSemaphore semaphore, const char* label) {
addDebugLabel(device, VK_OBJECT_TYPE_SEMAPHORE, reinterpret_cast<std::uint64_t>(semaphore), label);
}
void vkutil::addDebugLabel(VkDevice device, VkQueue queue, const char* label) {
addDebugLabel(device, VK_OBJECT_TYPE_QUEUE, reinterpret_cast<std::uint64_t>(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) { vkutil::RenderInfo vkutil::createRenderingInfo(const RenderingInfoParams& params) {
@@ -319,76 +389,6 @@ VkShaderModule vkutil::loadShaderModule(const std::filesystem::path& path, VkDev
return shaderModule; 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) { vkutil::RenderInfo createRenderingInfo(const vkutil::RenderingInfoParams& params) {
return vkutil::createRenderingInfo(params); return vkutil::createRenderingInfo(params);
} }
+3 -2
View File
@@ -164,12 +164,13 @@ void Scene::Render(const RenderContext& ctx) {
} }
void Scene::RenderImgui() { void Scene::RenderImgui(bool* open) {
ImGuiUtils::RenderSceneInspector( ImGuiUtils::RenderSceneInspector(
m_name, m_name,
m_objects, m_objects,
m_pendingAdditions, m_pendingAdditions,
m_SelectedObjectId); m_SelectedObjectId,
open);
} }
void Scene::CleanupDestroyedGameObjects() { void Scene::CleanupDestroyedGameObjects() {
+2 -2
View File
@@ -38,9 +38,9 @@ void SceneManager::Render(const RenderContext& ctx) {
} }
} }
void SceneManager::RenderImgui() { void SceneManager::RenderImgui(bool* open) {
for (const auto& scene : m_activeScenes) { for (const auto& scene : m_activeScenes) {
scene->RenderImgui(); scene->RenderImgui(open);
} }
} }
+3 -2
View File
@@ -123,7 +123,8 @@ namespace ImGuiUtils {
std::string_view sceneName, std::string_view sceneName,
const std::vector<std::shared_ptr<GameObject>>& objectsSource, const std::vector<std::shared_ptr<GameObject>>& objectsSource,
const std::vector<std::shared_ptr<GameObject>>& pendingAdditions, const std::vector<std::shared_ptr<GameObject>>& pendingAdditions,
ObjectId& selectedObjectId) { ObjectId& selectedObjectId,
bool* open) {
RegisterEngineComponents(); RegisterEngineComponents();
std::vector<GameObject*> objects; std::vector<GameObject*> objects;
@@ -154,7 +155,7 @@ namespace ImGuiUtils {
} }
ImGui::SetNextWindowSize(ImVec2(640.0f, 520.0f), ImGuiCond_FirstUseEver); ImGui::SetNextWindowSize(ImVec2(640.0f, 520.0f), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("Inspector")) { if (!ImGui::Begin("Inspector", open)) {
ImGui::End(); ImGui::End();
return; return;
} }
+24
View File
@@ -64,8 +64,32 @@ set(ASSIMP_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(ASSIMP_INJECT_DEBUG_POSTFIX OFF CACHE BOOL "" FORCE) set(ASSIMP_INJECT_DEBUG_POSTFIX OFF CACHE BOOL "" FORCE)
set(ASSIMP_INSTALL OFF CACHE BOOL "" FORCE) set(ASSIMP_INSTALL OFF CACHE BOOL "" FORCE)
set(ASSIMP_WARNINGS_AS_ERRORS OFF CACHE BOOL "" FORCE) set(ASSIMP_WARNINGS_AS_ERRORS OFF CACHE BOOL "" FORCE)
# assimp is unbearably slow unoptimized (a big FBX takes ~10s), so build it optimized even in Debug.
# This only affects targets created inside the assimp directory (including its bundled zlib).
set(DESTRUM_SAVED_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
set(DESTRUM_SAVED_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
if (MSVC)
foreach (flags_var CMAKE_C_FLAGS_DEBUG CMAKE_CXX_FLAGS_DEBUG)
string(REPLACE "/RTC1" "" ${flags_var} "${${flags_var}}")
string(REPLACE "/Od" "/O2" ${flags_var} "${${flags_var}}")
string(REPLACE "/Ob0" "/Ob2" ${flags_var} "${${flags_var}}")
endforeach ()
else ()
string(APPEND CMAKE_C_FLAGS_DEBUG " -O2")
string(APPEND CMAKE_CXX_FLAGS_DEBUG " -O2")
endif ()
add_subdirectory(assimp) add_subdirectory(assimp)
# assimp's own CMakeLists appends "/Od" to the Debug flags again, target options come later and win
if (MSVC)
target_compile_options(assimp PRIVATE $<$<CONFIG:Debug>:/O2>)
endif ()
set(CMAKE_C_FLAGS_DEBUG "${DESTRUM_SAVED_C_FLAGS_DEBUG}")
set(CMAKE_CXX_FLAGS_DEBUG "${DESTRUM_SAVED_CXX_FLAGS_DEBUG}")
set(IMGUI_DIR "${CMAKE_CURRENT_LIST_DIR}/imgui") set(IMGUI_DIR "${CMAKE_CURRENT_LIST_DIR}/imgui")
add_library(imgui STATIC add_library(imgui STATIC
+2
View File
@@ -22,6 +22,7 @@ public:
void customFixedUpdate(float dt) override; void customFixedUpdate(float dt) override;
void onWindowResize(int newWidth, int newHeight) override; void onWindowResize(int newWidth, int newHeight) override;
void customWindowsMenu() override;
private: private:
void drawDebugMenuBar(); void drawDebugMenuBar();
@@ -49,6 +50,7 @@ private:
bool renderBoxColliders{false}; bool renderBoxColliders{false};
bool renderQuads{true}; bool renderQuads{true};
bool renderDebugLines{true}; bool renderDebugLines{true};
bool showTestWindow{true};
}; };
#endif //LIGHTKEEPER_H #endif //LIGHTKEEPER_H
+118 -100
View File
@@ -153,65 +153,65 @@ void LightKeeper::customInit()
characterModelOptions.loadSkeleton = true; characterModelOptions.loadSkeleton = true;
characterModelOptions.loadAnimations = true; characterModelOptions.loadAnimations = true;
// auto charModel = ModelDoc::LoadModel( auto charModel = ModelDoc::LoadModel(
// AssetFS::GetInstance().GetFullPath("engine://cotw-capybara-male/source/capybara.fbx").generic_string(), AssetFS::GetInstance().GetFullPath("engine://cotw-capybara-male/source/capybara.fbx").generic_string(),
// characterModelOptions characterModelOptions
// ); );
// ModelDocUtils::LogModelDocSummary(charModel, "capybara.fbx"); ModelDocUtils::LogModelDocSummary(charModel, "capybara.fbx");
//
// const auto& charPrimitive = ModelDocUtils::GetFirstSkinnedPrimitiveOrFirstOrThrow( const auto& charPrimitive = ModelDocUtils::GetFirstSkinnedPrimitiveOrFirstOrThrow(
// charModel, charModel,
// "engine://cotw-capybara-male/source/capybara.fbx" "engine://cotw-capybara-male/source/capybara.fbx"
// ); );
//
// const auto charMeshID = resources.meshes().addMesh(gfxDevice, charPrimitive.mesh); const auto charMeshID = resources.meshes().addMesh(gfxDevice, charPrimitive.mesh);
//
// const auto charTexturePath = ModelDocUtils::PickTexturePath( const auto charTexturePath = ModelDocUtils::PickTexturePath(
// charModel, charModel,
// charPrimitive, charPrimitive,
// AssetFS::GetInstance().GetFullPath( AssetFS::GetInstance().GetFullPath(
// "engine://cotw-capybara-male/textures/capybara_male_light_brown_dif.ddsc.DECA.RE.pngballs") "engine://cotw-capybara-male/textures/capybara_male_light_brown_dif.ddsc.DECA.RE.pngballs")
// ); );
//
// const auto charTextureID = resources.loadImageFromFile(gfxDevice, charTexturePath); const auto charTextureID = resources.loadImageFromFile(gfxDevice, charTexturePath);
// // const auto charTextureID = gfxDevice.loadImageFromFile(AssetFS::GetInstance().GetFullPath("engine://char.jpg")); // const auto charTextureID = gfxDevice.loadImageFromFile(AssetFS::GetInstance().GetFullPath("engine://char.jpg"));
// const auto charMaterialID = resources.materials().addMaterial({ const auto charMaterialID = resources.materials().addMaterial({
// .baseColor = ModelDocUtils::GetImportedBaseColor( .baseColor = ModelDocUtils::GetImportedBaseColor(
// charModel, charPrimitive), charModel, charPrimitive),
// .diffuseTexture = charTextureID, .diffuseTexture = charTextureID,
// .name = ModelDocUtils::GetImportedMaterialName( .name = ModelDocUtils::GetImportedMaterialName(
// charModel, charPrimitive, "CharacterMaterial"), charModel, charPrimitive, "CharacterMaterial"),
// }); });
//
// const auto charMeshComp = CharObj->AddComponent<MeshRendererComponent>(); const auto charMeshComp = CharObj->AddComponent<MeshRendererComponent>();
// charMeshComp->SetMeshID(charMeshID); charMeshComp->SetMeshID(charMeshID);
// charMeshComp->SetMaterialID(charMaterialID); charMeshComp->SetMaterialID(charMaterialID);
//
// const auto animator = CharObj->AddComponent<Animator>(); const auto animator = CharObj->AddComponent<Animator>();
// animator->setSkeleton(std::move(charModel.skeleton)); animator->setSkeleton(std::move(charModel.skeleton));
//
// std::string firstAnimationName; std::string firstAnimationName;
//
// for (auto& clip : charModel.animations) for (auto& clip : charModel.animations)
// { {
// spdlog::info("Loaded animation: '{}' ({:.2f}s)", clip.name, clip.duration); spdlog::info("Loaded animation: '{}' ({:.2f}s)", clip.name, clip.duration);
//
// if (firstAnimationName.empty()) if (firstAnimationName.empty())
// firstAnimationName = clip.name; firstAnimationName = clip.name;
//
// animator->addClip(std::make_shared<SkeletalAnimation>(std::move(clip))); animator->addClip(std::make_shared<SkeletalAnimation>(std::move(clip)));
// } }
//
// if (!firstAnimationName.empty()) if (!firstAnimationName.empty())
// { {
// spdlog::info("Playing animation: '{}'", firstAnimationName); spdlog::info("Playing animation: '{}'", firstAnimationName);
// animator->play(firstAnimationName); animator->play(firstAnimationName);
// } }
//
// animator->play("capybara_canter_fwd_01|capybara_canter_fwd_01|run"); animator->play("capybara_canter_fwd_01|capybara_canter_fwd_01|run");
//
// CharObj->GetTransform().SetWorldPosition(glm::vec3(0.f, 0.f, 0.f)); CharObj->GetTransform().SetWorldPosition(glm::vec3(0.f, 0.f, 0.f));
// CharObj->GetTransform().SetWorldScale(0.01f, 0.01f, 0.01f); CharObj->GetTransform().SetWorldScale(0.01f, 0.01f, 0.01f);
// ModelDoc::LoadOptions options{}; // ModelDoc::LoadOptions options{};
// options.meshImportMode = ModelDoc::MeshImportMode::MergedPerNode; // options.meshImportMode = ModelDoc::MeshImportMode::MergedPerNode;
@@ -450,46 +450,58 @@ void LightKeeper::customInit()
triggerObj->GetTransform().SetWorldPosition({0.0f, 0.0f, 0.0f}); 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 // Forward+ test lights: a dim sun and a ring of colored point lights orbiting the origin
auto sunObj = scene.CreateGameObject("Sun"); // auto sunObj = scene.CreateGameObject("Sun");
auto* sun = sunObj->AddComponent<LightComponent>(LightType::Directional); // auto* sun = sunObj->AddComponent<LightComponent>(LightType::Directional);
sun->SetColor(glm::vec3{1.0f, 0.95f, 0.85f}); // sun->SetColor(glm::vec3{1.0f, 0.95f, 0.85f});
sun->SetIntensity(1.0f); // sun->SetIntensity(1.0f);
sunObj->GetTransform().SetWorldRotation(glm::vec3{-50.0f, 30.0f, 0.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<MeshRendererComponent>();
// casterMesh->SetMeshID(sphereMesh);
// casterMesh->SetMaterialID(sphereMaterial);
// casterObj->GetTransform().SetWorldPosition(glm::vec3{-4.0f + 4.0f * static_cast<float>(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<float>(i) / static_cast<float>(pointLightCount);
// const float angle = t * glm::two_pi<float>();
//
// 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<LightComponent>(LightType::Point);
// light->SetColor(glm::vec3{
// 0.5f + 0.5f * std::cos(glm::two_pi<float>() * t),
// 0.5f + 0.5f * std::cos(glm::two_pi<float>() * (t + 0.33f)),
// 0.5f + 0.5f * std::cos(glm::two_pi<float>() * (t + 0.67f))
// });
// light->SetIntensity(1.0f);
// light->SetRange(3.0f);
//
// auto* orbit = lightObj->AddComponent<Rotator>(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; // auto playerObj = scene.CreateGameObject("Player");
constexpr float pointLightRingRadius = 6.0f; // playerObj->GetTransform().SetWorldPosition(glm::vec3(0.0f, 2.0f, -5.0f));
for (int i = 0; i < pointLightCount; i++) // auto* fps = playerObj->AddComponent<FirstPersonController>();
{ // m_Player = playerObj;
const float t = static_cast<float>(i) / static_cast<float>(pointLightCount); // m_FPSController = fps;
const float angle = t * glm::two_pi<float>(); // scene.GetPhysics().RegisterGameObject(*playerObj);
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<LightComponent>(LightType::Point);
light->SetColor(glm::vec3{
0.5f + 0.5f * std::cos(glm::two_pi<float>() * t),
0.5f + 0.5f * std::cos(glm::two_pi<float>() * (t + 0.33f)),
0.5f + 0.5f * std::cos(glm::two_pi<float>() * (t + 0.67f))
});
light->SetIntensity(1.0f);
light->SetRange(3.0f);
auto* orbit = lightObj->AddComponent<Rotator>(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<FirstPersonController>();
m_Player = playerObj;
m_FPSController = fps;
scene.GetPhysics().RegisterGameObject(*playerObj);
const auto texPath = AssetFS::GetInstance().GetFullPath("engine://textures/kobe.png"); const auto texPath = AssetFS::GetInstance().GetFullPath("engine://textures/kobe.png");
texID = resources.loadImageFromFile(gfxDevice, texPath); texID = resources.loadImageFromFile(gfxDevice, texPath);
@@ -527,8 +539,8 @@ void LightKeeper::customUpdate(float dt)
renderer.setRenderWireframe(!renderer.getRenderWireframe()); renderer.setRenderWireframe(!renderer.getRenderWireframe());
} }
if (m_params.showDebugUi) { if (m_params.showDebugUi && showTestWindow) {
ImGui::Begin("Test"); ImGui::Begin("Test", &showTestWindow);
if (ImGui::Button("SPawn ball")) if (ImGui::Button("SPawn ball"))
{ {
auto sphere = SceneManager::GetInstance().GetCurrentScene().CreateGameObject("Sphere"); auto sphere = SceneManager::GetInstance().GetCurrentScene().CreateGameObject("Sphere");
@@ -551,6 +563,12 @@ void LightKeeper::customUpdate(float dt)
} }
void LightKeeper::customWindowsMenu()
{
ImGui::Separator();
ImGui::MenuItem("Test", nullptr, &showTestWindow);
}
void LightKeeper::drawDebugMenuBar() void LightKeeper::drawDebugMenuBar()
{ {
if (!ImGui::BeginMainMenuBar()) { if (!ImGui::BeginMainMenuBar()) {
+3
View File
@@ -457,6 +457,7 @@ namespace {
light->SetIntensity(4.0f); light->SetIntensity(4.0f);
light->SetRange(7.5f); light->SetRange(7.5f);
light->SetConeAngles(0.2f, 0.4f); light->SetConeAngles(0.2f, 0.4f);
light->SetCastShadows(true);
const auto data = light->Serialize(); const auto data = light->Serialize();
@@ -472,6 +473,8 @@ namespace {
"LightComponent must round-trip intensity and range"); "LightComponent must round-trip intensity and range");
check(loaded->GetInnerConeAngle() == 0.2f && loaded->GetOuterConeAngle() == 0.4f, check(loaded->GetInnerConeAngle() == 0.2f && loaded->GetOuterConeAngle() == 0.4f,
"LightComponent must round-trip its cone angles"); "LightComponent must round-trip its cone angles");
check(loaded->GetCastShadows(),
"LightComponent must round-trip its cast shadows flag");
loaded->SetConeAngles(0.5f, 0.1f); loaded->SetConeAngles(0.5f, 0.1f);
check(loaded->GetOuterConeAngle() >= loaded->GetInnerConeAngle(), check(loaded->GetOuterConeAngle() >= loaded->GetInnerConeAngle(),