feat: basic shadow map + renderdoc labels
This commit is contained in:
@@ -50,6 +50,7 @@ set(SRC_FILES
|
||||
"src/Graphics/Pipelines/QuadRendererPass.cpp"
|
||||
"src/Graphics/Pipelines/SkyboxPipeline.cpp"
|
||||
"src/Graphics/Pipelines/LightCullingPipeline.cpp"
|
||||
"src/Graphics/Pipelines/ShadowMapPipeline.cpp"
|
||||
"src/Graphics/Pipelines/SkinningPipeline.cpp"
|
||||
"src/Graphics/Pipelines/ImguiPass.cpp"
|
||||
|
||||
|
||||
@@ -35,6 +35,10 @@ vec4 sampleTextureCubeLinear(uint texID, vec3 p) {
|
||||
return texture(nonuniformEXT(samplerCube(textureCubes[texID], samplers[LINEAR_SAMPLER_ID])), p);
|
||||
}
|
||||
|
||||
float sampleTexture2DShadow(uint texID, vec3 p) {
|
||||
return texture(nonuniformEXT(sampler2DShadow(textures[texID], samplers[SHADOW_SAMPLER_ID])), p);
|
||||
}
|
||||
|
||||
float sampleTextureArrayShadow(uint texID, vec4 p) {
|
||||
return texture(nonuniformEXT(sampler2DArrayShadow(textureArrays[texID], samplers[SHADOW_SAMPLER_ID])), p);
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ float calculateSpotAttenuation(Light light, vec3 l)
|
||||
}
|
||||
|
||||
vec3 calculateLight(Light light, vec3 fragPos, vec3 n, vec3 v,
|
||||
vec3 diffuseColor, float roughness, float metallic, vec3 f0)
|
||||
vec3 diffuseColor, float roughness, float metallic, vec3 f0, float occlusion)
|
||||
{
|
||||
vec3 l;
|
||||
float attenuation = 1.0;
|
||||
@@ -80,7 +80,7 @@ vec3 calculateLight(Light light, vec3 fragPos, vec3 n, vec3 v,
|
||||
vec3 kd = (vec3(1.0) - F) * (1.0 - metallic);
|
||||
vec3 diffuse = kd * diffuseColor / PI;
|
||||
|
||||
return (diffuse + specular) * light.color * light.intensity * attenuation * NoL;
|
||||
return (diffuse + specular) * light.color * light.intensity * attenuation * NoL * occlusion;
|
||||
}
|
||||
|
||||
// blue -> green -> red, used to visualise how many lights a tile has
|
||||
|
||||
@@ -27,7 +27,8 @@ struct Light {
|
||||
|
||||
float spotInnerCos;
|
||||
float spotOuterCos;
|
||||
vec2 padding;
|
||||
uint castShadows;
|
||||
float padding;
|
||||
};
|
||||
|
||||
layout (buffer_reference, std430) readonly buffer LightsBuffer {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "scene_data.glsl"
|
||||
#include "mesh_pcs.glsl"
|
||||
#include "lighting.glsl"
|
||||
#include "shadows.glsl"
|
||||
|
||||
layout (location = 0) in vec3 inPos;
|
||||
layout (location = 1) in vec2 inUV;
|
||||
@@ -69,8 +70,17 @@ void main()
|
||||
uint lightIndex = pcs.sceneData.tileLightIndices.data[tileOffset + 1 + i];
|
||||
Light light = pcs.sceneData.lights.data[lightIndex];
|
||||
|
||||
float occlusion = 1.0;
|
||||
if (light.castShadows != 0) {
|
||||
occlusion = calculateDirectionalShadow(
|
||||
fragPos, n,
|
||||
pcs.sceneData.lightViewProj,
|
||||
pcs.sceneData.shadowMapId,
|
||||
pcs.sceneData.shadowNormalOffset);
|
||||
}
|
||||
|
||||
fragColor += calculateLight(light, fragPos, n, v,
|
||||
diffuseColor, roughness, metallic, f0);
|
||||
diffuseColor, roughness, metallic, f0, occlusion);
|
||||
}
|
||||
|
||||
// emissive
|
||||
|
||||
@@ -33,7 +33,12 @@ layout (buffer_reference, scalar) readonly buffer SceneDataBuffer {
|
||||
uint numLights;
|
||||
uint tileCountX;
|
||||
uint debugLightHeatmap;
|
||||
|
||||
// directional shadows
|
||||
uint shadowMapId;
|
||||
float shadowNormalOffset;
|
||||
uint padding;
|
||||
mat4 lightViewProj;
|
||||
} sceneDataBuffer;
|
||||
|
||||
#endif // SCENE_DATA_GLSL
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -39,6 +39,9 @@ public:
|
||||
[[nodiscard]] float GetInnerConeAngle() const { return m_InnerConeAngle; }
|
||||
[[nodiscard]] float GetOuterConeAngle() const { return m_OuterConeAngle; }
|
||||
|
||||
void SetCastShadows(bool castShadows) { m_CastShadows = castShadows; }
|
||||
[[nodiscard]] bool GetCastShadows() const { return m_CastShadows; }
|
||||
|
||||
private:
|
||||
LightType m_Type{LightType::Point};
|
||||
glm::vec3 m_Color{1.0f};
|
||||
@@ -46,6 +49,7 @@ private:
|
||||
float m_Range{10.0f};
|
||||
float m_InnerConeAngle{0.35f};
|
||||
float m_OuterConeAngle{0.5f};
|
||||
bool m_CastShadows{false};
|
||||
};
|
||||
|
||||
#endif // LIGHTCOMPONENT_H
|
||||
|
||||
@@ -25,6 +25,9 @@ struct Light {
|
||||
|
||||
float innerConeAngle{0.35f};
|
||||
float outerConeAngle{0.5f};
|
||||
|
||||
// directional lights only, the first one submitted each frame gets the shadow map
|
||||
bool castShadows{false};
|
||||
};
|
||||
|
||||
struct GPULight {
|
||||
@@ -39,7 +42,8 @@ struct GPULight {
|
||||
|
||||
float spotInnerCos;
|
||||
float spotOuterCos;
|
||||
glm::vec2 padding;
|
||||
std::uint32_t castShadows;
|
||||
float padding;
|
||||
};
|
||||
|
||||
static_assert(sizeof(GPULight) == 64, "GPULight must match the struct in lights.glsl");
|
||||
|
||||
@@ -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
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <destrum/Graphics/Pipelines/LineRenderingPass.h>
|
||||
#include <destrum/Graphics/Pipelines/MeshPipeline.h>
|
||||
#include <destrum/Graphics/Pipelines/QuadRendererPass.h>
|
||||
#include <destrum/Graphics/Pipelines/ShadowMapPipeline.h>
|
||||
#include <destrum/Graphics/ids.h>
|
||||
#include <destrum/Graphics/MeshDrawCommand.h>
|
||||
#include <destrum/Graphics/Resources/NBuffer.h>
|
||||
@@ -17,6 +18,7 @@
|
||||
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include <destrum/Graphics/Pipelines/SkinningPipeline.h>
|
||||
|
||||
@@ -87,6 +89,16 @@ public:
|
||||
return renderLightHeatmap;
|
||||
}
|
||||
|
||||
void setShadowDistance(float distance)
|
||||
{
|
||||
shadowMapPipeline->setShadowDistance(distance);
|
||||
}
|
||||
|
||||
float getShadowDistance() const
|
||||
{
|
||||
return shadowMapPipeline->getShadowDistance();
|
||||
}
|
||||
|
||||
RenderResources* getResources()
|
||||
{
|
||||
return resources;
|
||||
@@ -138,7 +150,11 @@ private:
|
||||
std::uint32_t numLights;
|
||||
std::uint32_t tileCountX;
|
||||
std::uint32_t debugLightHeatmap;
|
||||
|
||||
std::uint32_t shadowMapId;
|
||||
float shadowNormalOffset;
|
||||
std::uint32_t padding;
|
||||
glm::mat4 lightViewProj;
|
||||
};
|
||||
|
||||
NBuffer sceneDataBuffer;
|
||||
@@ -150,6 +166,9 @@ private:
|
||||
|
||||
std::unique_ptr<SkinningPipeline> skinningPipeline;
|
||||
std::unique_ptr<LightCullingPipeline> lightCullingPipeline;
|
||||
std::unique_ptr<ShadowMapPipeline> shadowMapPipeline;
|
||||
|
||||
std::optional<glm::vec3> shadowLightDirection;
|
||||
|
||||
bool renderLightHeatmap{false};
|
||||
bool initialized{false};
|
||||
|
||||
@@ -77,6 +77,8 @@ public:
|
||||
[[nodiscard]] VkFormat getFormat() const { return m_swapchain.image_format; }
|
||||
|
||||
private:
|
||||
void nameSwapchainObjects(VkDevice device) const;
|
||||
|
||||
struct FrameData {
|
||||
VkSemaphore swapchainSemaphore{VK_NULL_HANDLE};
|
||||
VkFence renderFence{VK_NULL_HANDLE};
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef UTIL_H
|
||||
#define UTIL_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
@@ -87,6 +89,9 @@ namespace vkutil {
|
||||
VkDeviceSize offset = 0,
|
||||
VkDeviceSize size = VK_WHOLE_SIZE);
|
||||
|
||||
// Object names (VK_EXT_debug_utils), shown in RenderDoc, Nsight and validation messages.
|
||||
// All of these are no-ops when the extension isn't loaded.
|
||||
void addDebugLabel(VkDevice device, VkObjectType objectType, std::uint64_t objectHandle, const char* label);
|
||||
void addDebugLabel(VkDevice device, VkImage image, const char* label);
|
||||
void addDebugLabel(VkDevice device, VkImageView imageView, const char* label);
|
||||
void addDebugLabel(VkDevice device, VkShaderModule shaderModule, const char* label);
|
||||
@@ -94,6 +99,62 @@ namespace vkutil {
|
||||
void addDebugLabel(VkDevice device, VkPipelineLayout layout, const char* label);
|
||||
void addDebugLabel(VkDevice device, VkBuffer buffer, const char* label);
|
||||
void addDebugLabel(VkDevice device, VkSampler sampler, const char* label);
|
||||
void addDebugLabel(VkDevice device, VkDescriptorPool descriptorPool, const char* label);
|
||||
void addDebugLabel(VkDevice device, VkDescriptorSetLayout descriptorSetLayout, const char* label);
|
||||
void addDebugLabel(VkDevice device, VkDescriptorSet descriptorSet, const char* label);
|
||||
void addDebugLabel(VkDevice device, VkCommandPool commandPool, const char* label);
|
||||
void addDebugLabel(VkDevice device, VkCommandBuffer commandBuffer, const char* label);
|
||||
void addDebugLabel(VkDevice device, VkFence fence, const char* label);
|
||||
void addDebugLabel(VkDevice device, VkSemaphore semaphore, const char* label);
|
||||
void addDebugLabel(VkDevice device, VkQueue queue, const char* label);
|
||||
|
||||
// Command buffer regions, they nest like a stack: every begin needs a matching end
|
||||
// in the same command buffer.
|
||||
[[nodiscard]] bool debugLabelsEnabled();
|
||||
void cmdBeginLabel(VkCommandBuffer cmd, const char* label, const glm::vec4& color = glm::vec4{1.0f});
|
||||
void cmdEndLabel(VkCommandBuffer cmd);
|
||||
void cmdInsertLabel(VkCommandBuffer cmd, const char* label, const glm::vec4& color = glm::vec4{1.0f});
|
||||
|
||||
// printf style, only formats when labels are actually recorded (cheap enough for per draw labels)
|
||||
template <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 {
|
||||
VkExtent2D renderExtent;
|
||||
|
||||
@@ -47,6 +47,7 @@ void LightComponent::Render(const RenderContext& ctx)
|
||||
.range = m_Range,
|
||||
.innerConeAngle = m_InnerConeAngle,
|
||||
.outerConeAngle = m_OuterConeAngle,
|
||||
.castShadows = m_CastShadows,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -57,7 +58,8 @@ nlohmann::json LightComponent::Serialize() const {
|
||||
{"intensity", m_Intensity},
|
||||
{"range", m_Range},
|
||||
{"innerConeAngle", m_InnerConeAngle},
|
||||
{"outerConeAngle", m_OuterConeAngle}
|
||||
{"outerConeAngle", m_OuterConeAngle},
|
||||
{"castShadows", m_CastShadows}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -88,6 +90,10 @@ void LightComponent::Deserialize(const nlohmann::json& data) {
|
||||
outerConeAngle = data.at("outerConeAngle").get<float>();
|
||||
}
|
||||
SetConeAngles(innerConeAngle, outerConeAngle);
|
||||
|
||||
if (data.contains("castShadows")) {
|
||||
m_CastShadows = data.at("castShadows").get<bool>();
|
||||
}
|
||||
}
|
||||
|
||||
void LightComponent::ImGuiInspector() {
|
||||
@@ -106,6 +112,9 @@ void LightComponent::ImGuiInspector() {
|
||||
}
|
||||
|
||||
if (m_Type == LightType::Directional) {
|
||||
ImGui::Checkbox("Cast Shadows", &m_CastShadows);
|
||||
ImGui::SameLine();
|
||||
ImGui::TextDisabled("(first directional light only)");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,9 @@ void MeshRendererComponent::Start() {
|
||||
VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT |
|
||||
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT
|
||||
);
|
||||
|
||||
const std::string bufferName = GetGameObject()->GetName() + " (skinned vertices)";
|
||||
vkutil::addDebugLabel(gfxDevice.getDevice(), m_skinnedMesh->skinnedVertexBuffer.buffer, bufferName.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -74,6 +74,7 @@ void BindlessSetManager::init(
|
||||
};
|
||||
|
||||
VK_CHECK(vkCreateDescriptorPool(device, &poolInfo, nullptr, &descPool));
|
||||
vkutil::addDebugLabel(device, descPool, "bindless descriptor pool");
|
||||
}
|
||||
|
||||
{ // build desc set layout
|
||||
@@ -111,6 +112,7 @@ void BindlessSetManager::init(
|
||||
};
|
||||
|
||||
VK_CHECK(vkCreateDescriptorSetLayout(device, &info, nullptr, &descSetLayout));
|
||||
vkutil::addDebugLabel(device, descSetLayout, "bindless descriptor set layout");
|
||||
}
|
||||
|
||||
{ // alloc desc set
|
||||
@@ -122,6 +124,7 @@ void BindlessSetManager::init(
|
||||
};
|
||||
|
||||
VK_CHECK(vkAllocateDescriptorSets(device, &allocInfo, &descSet));
|
||||
vkutil::addDebugLabel(device, descSet, "bindless descriptor set");
|
||||
}
|
||||
|
||||
initDefaultSamplers(device, maxAnisotropy);
|
||||
|
||||
@@ -99,8 +99,13 @@ void ComputePipeline::CreateComputePipeline(const std::string& compPath,
|
||||
throw std::runtime_error("Can't make compute pipeline!");
|
||||
}
|
||||
|
||||
vkutil::addDebugLabel(m_device.getDevice(), m_compShaderModule, compFileName.c_str());
|
||||
|
||||
if (!configInfo.name.empty()) {
|
||||
vkutil::addDebugLabel(GameState::GetInstance().Gfx().getDevice(), m_computePipeline, configInfo.name.c_str());
|
||||
vkutil::addDebugLabel(m_device.getDevice(), m_computePipeline, configInfo.name.c_str());
|
||||
|
||||
const std::string layoutName = configInfo.name + " Layout";
|
||||
vkutil::addDebugLabel(m_device.getDevice(), configInfo.pipelineLayout, layoutName.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -156,6 +156,7 @@ void GfxDevice::endFrame(VkCommandBuffer cmd, const GPUImage& drawImage, const E
|
||||
|
||||
auto swapchainLayout = VK_IMAGE_LAYOUT_UNDEFINED; {
|
||||
ZoneScopedN("Clear Swapchain Image");
|
||||
vkutil::ScopedCmdLabel label(cmd, "Clear Swapchain Image", vkutil::labelColors::transfer);
|
||||
|
||||
const VkImageSubresourceRange clearRange = vkinit::imageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
vkutil::transitionImage(cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_GENERAL);
|
||||
@@ -167,6 +168,7 @@ void GfxDevice::endFrame(VkCommandBuffer cmd, const GPUImage& drawImage, const E
|
||||
|
||||
if (true) {
|
||||
ZoneScopedN("Copy DrawImage To Swapchain");
|
||||
vkutil::ScopedCmdLabel label(cmd, "Copy DrawImage To Swapchain", vkutil::labelColors::transfer);
|
||||
|
||||
vkutil::transitionImage(
|
||||
cmd,
|
||||
@@ -190,6 +192,7 @@ void GfxDevice::endFrame(VkCommandBuffer cmd, const GPUImage& drawImage, const E
|
||||
|
||||
if (props.imguiPass) {
|
||||
ZoneScopedN("ImGui Render");
|
||||
vkutil::ScopedCmdLabel label(cmd, "ImGui Render", vkutil::labelColors::ui);
|
||||
|
||||
props.imguiPass->render(
|
||||
cmd,
|
||||
@@ -202,6 +205,7 @@ void GfxDevice::endFrame(VkCommandBuffer cmd, const GPUImage& drawImage, const E
|
||||
|
||||
{
|
||||
ZoneScopedN("Transition Swapchain To Present");
|
||||
vkutil::ScopedCmdLabel label(cmd, "Transition Swapchain To Present", vkutil::labelColors::transfer);
|
||||
|
||||
vkutil::transitionImage(cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
|
||||
swapchainLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
|
||||
@@ -40,6 +40,10 @@ void VulkanImmediateExecutor::init(
|
||||
};
|
||||
VK_CHECK(vkCreateFence(deviceHandle, &fenceCreateInfo, nullptr, &immFence));
|
||||
|
||||
vkutil::addDebugLabel(deviceHandle, immCommandPool, "immediate command pool");
|
||||
vkutil::addDebugLabel(deviceHandle, immCommandBuffer, "immediate command buffer");
|
||||
vkutil::addDebugLabel(deviceHandle, immFence, "immediate fence");
|
||||
|
||||
initialized = true;
|
||||
} catch (...) {
|
||||
cleanup(deviceHandle);
|
||||
|
||||
@@ -26,6 +26,11 @@ void FrameManager::init(VkDevice dev, std::uint32_t queueFamily) {
|
||||
const auto cmdAllocInfo = vkinit::commandBufferAllocateInfo(commandPool, 1);
|
||||
auto& mainCommandBuffer = frames[i].commandBuffer;
|
||||
VK_CHECK(vkAllocateCommandBuffers(device, &cmdAllocInfo, &mainCommandBuffer));
|
||||
|
||||
const std::string poolName = "frame " + std::to_string(i) + " command pool";
|
||||
const std::string cmdName = "frame " + std::to_string(i) + " command buffer";
|
||||
vkutil::addDebugLabel(device, commandPool, poolName.c_str());
|
||||
vkutil::addDebugLabel(device, mainCommandBuffer, cmdName.c_str());
|
||||
}
|
||||
} catch (...) {
|
||||
cleanup(dev);
|
||||
|
||||
@@ -178,7 +178,9 @@ std::optional<GPUImage> ImageManager::loadImageFromFile(
|
||||
}
|
||||
|
||||
image.debugName = path.string();
|
||||
vkutil::addDebugLabel(device, image.image, path.string().c_str());
|
||||
vkutil::addDebugLabel(device, image.image, image.debugName.c_str());
|
||||
const std::string viewName = image.debugName + " (view)";
|
||||
vkutil::addDebugLabel(device, image.imageView, viewName.c_str());
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
@@ -156,6 +156,9 @@ void MeshCache::uploadMesh(GfxDevice& gfxDevice, const CPUMesh& cpuMesh, GPUMesh
|
||||
VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
|
||||
VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE);
|
||||
|
||||
const auto skinningBufferName = cpuMesh.name + " (skinning data)";
|
||||
vkutil::addDebugLabel(gfxDevice.getDevice(), gpuMesh.skinningDataBuffer.buffer, skinningBufferName.c_str());
|
||||
|
||||
auto skinningStaging =
|
||||
gfxDevice.createBuffer(
|
||||
skinningDataSize,
|
||||
|
||||
@@ -146,11 +146,11 @@ void Pipeline::CreateGraphicsPipeline(const std::string& vertPath, const std::st
|
||||
|
||||
CreateShaderModule(vertCode, &m_vertShaderModule);
|
||||
|
||||
// vkutil::addDebugLabel(
|
||||
// m_device.getDevice(),
|
||||
// m_fragShaderModule,
|
||||
// VertFileName.c_str()
|
||||
// );
|
||||
vkutil::addDebugLabel(
|
||||
m_device.getDevice(),
|
||||
m_vertShaderModule,
|
||||
VertFileName.c_str()
|
||||
);
|
||||
|
||||
VkPipelineShaderStageCreateInfo vertShaderStageInfo{};
|
||||
vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
@@ -173,11 +173,11 @@ void Pipeline::CreateGraphicsPipeline(const std::string& vertPath, const std::st
|
||||
|
||||
CreateShaderModule(fragCode, &m_fragShaderModule);
|
||||
|
||||
// vkutil::addDebugLabel(
|
||||
// m_device.getDevice(),
|
||||
// m_fragShaderModule,
|
||||
// FragFileName.c_str()
|
||||
// );
|
||||
vkutil::addDebugLabel(
|
||||
m_device.getDevice(),
|
||||
m_fragShaderModule,
|
||||
FragFileName.c_str()
|
||||
);
|
||||
|
||||
|
||||
VkPipelineShaderStageCreateInfo fragShaderStageInfo{};
|
||||
@@ -231,18 +231,17 @@ void Pipeline::CreateGraphicsPipeline(const std::string& vertPath, const std::st
|
||||
pipelineInfo.basePipelineIndex = -1;
|
||||
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
|
||||
|
||||
if (!configInfo.name.empty()) {
|
||||
// DebugLabel::SetObjectName(
|
||||
// reinterpret_cast<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) {
|
||||
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 {
|
||||
|
||||
@@ -158,7 +158,8 @@ void LightCullingPipeline::appendLight(const Light& light)
|
||||
.type = static_cast<std::uint32_t>(light.type),
|
||||
.spotInnerCos = std::cos(light.innerConeAngle),
|
||||
.spotOuterCos = std::cos(light.outerConeAngle),
|
||||
.padding = glm::vec2{0.0f},
|
||||
.castShadows = light.castShadows ? 1u : 0u,
|
||||
.padding = 0.0f,
|
||||
};
|
||||
lightsBuffer.append(gpuLight);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <destrum/Graphics/RenderResources.h>
|
||||
#include <destrum/Graphics/Caches/MeshCache.h>
|
||||
#include <destrum/Graphics/Frustum.h>
|
||||
#include <destrum/Graphics/Util.h>
|
||||
|
||||
#include "volk.h"
|
||||
#include "spdlog/spdlog.h"
|
||||
@@ -219,6 +220,16 @@ void MeshPipeline::drawMeshes(
|
||||
|
||||
const auto& mesh = meshCache.getMesh(dc.meshId);
|
||||
|
||||
const std::string& meshKey = meshCache.getMeshKey(dc.meshId);
|
||||
vkutil::cmdBeginLabelFormatted(
|
||||
cmd,
|
||||
vkutil::labelColors::draw,
|
||||
"%s [mesh %zu, material %u%s]",
|
||||
meshKey.empty() ? "Mesh" : meshKey.c_str(),
|
||||
dc.meshId,
|
||||
dc.materialId,
|
||||
dc.skinnedMesh != nullptr ? ", skinned" : "");
|
||||
|
||||
if (dc.meshId != prevMeshId)
|
||||
{
|
||||
prevMeshId = dc.meshId;
|
||||
@@ -254,6 +265,8 @@ void MeshPipeline::drawMeshes(
|
||||
);
|
||||
|
||||
vkCmdDrawIndexed(cmd, mesh.numIndices, 1, 0, 0, 0);
|
||||
|
||||
vkutil::cmdEndLabel(cmd);
|
||||
};
|
||||
|
||||
if (!sortedDrawCommands.empty())
|
||||
|
||||
@@ -42,12 +42,14 @@ void QuadRendererPass::init(GfxDevice& gfxDevice, RenderResources& resources, Vk
|
||||
vertexBytes,
|
||||
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
|
||||
VMA_MEMORY_USAGE_AUTO_PREFER_HOST);
|
||||
vkutil::addDebugLabel(gfxDevice.getDevice(), vertexBuffer.buffer, "quad vertices");
|
||||
|
||||
const std::size_t indexBytes = InitialQuadCapacity * IndicesPerQuad * sizeof(std::uint32_t);
|
||||
indexBuffer = gfxDevice.createBuffer(
|
||||
indexBytes,
|
||||
VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
|
||||
VMA_MEMORY_USAGE_AUTO_PREFER_HOST);
|
||||
vkutil::addDebugLabel(gfxDevice.getDevice(), indexBuffer.buffer, "quad indices");
|
||||
|
||||
quadCapacity = InitialQuadCapacity;
|
||||
|
||||
@@ -321,6 +323,7 @@ void QuadRendererPass::ensureCapacity(
|
||||
newCapacity * VerticesPerQuad * sizeof(QuadVertex),
|
||||
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
|
||||
VMA_MEMORY_USAGE_AUTO_PREFER_HOST);
|
||||
vkutil::addDebugLabel(gfxDevice.getDevice(), vertexBuffer.buffer, "quad vertices");
|
||||
|
||||
if (indexBuffer.buffer != VK_NULL_HANDLE) {
|
||||
gfxDevice.destroyBuffer(indexBuffer);
|
||||
@@ -329,6 +332,7 @@ void QuadRendererPass::ensureCapacity(
|
||||
newCapacity * IndicesPerQuad * sizeof(std::uint32_t),
|
||||
VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
|
||||
VMA_MEMORY_USAGE_AUTO_PREFER_HOST);
|
||||
vkutil::addDebugLabel(gfxDevice.getDevice(), indexBuffer.buffer, "quad indices");
|
||||
|
||||
quadCapacity = newCapacity;
|
||||
}
|
||||
@@ -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),
|
||||
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
|
||||
VMA_MEMORY_USAGE_CPU_TO_GPU);
|
||||
|
||||
const std::string bufferName = "joint matrices (frame " + std::to_string(i) + ")";
|
||||
vkutil::addDebugLabel(device, jointMatricesBuffer.buffer.buffer, bufferName.c_str());
|
||||
}
|
||||
} catch (...) {
|
||||
cleanup(gfxDevice);
|
||||
@@ -84,6 +87,9 @@ void SkinningPipeline::doSkinning(VkCommandBuffer cmd,
|
||||
assert(mesh.hasSkeleton);
|
||||
assert(dc.skinnedMesh);
|
||||
|
||||
const std::string& meshKey = meshCache.getMeshKey(dc.meshId);
|
||||
vkutil::ScopedCmdLabel label(cmd, meshKey.empty() ? "Skin Mesh" : meshKey.c_str(), vkutil::labelColors::compute);
|
||||
|
||||
const auto cs = PushConstants{
|
||||
.jointMatricesBuffer = getCurrentFrameData(frameIndex).jointMatricesBuffer.buffer.address,
|
||||
.jointMatricesStartIndex = static_cast<std::uint32_t>(dc.jointMatricesStartIndex),
|
||||
|
||||
@@ -130,6 +130,8 @@ ImageID RenderResources::createImage(
|
||||
|
||||
if (!debugName.empty()) {
|
||||
vkutil::addDebugLabel(gfxDevice.getVkDevice(), image.image, debugName.c_str());
|
||||
const std::string viewName = debugName + " (view)";
|
||||
vkutil::addDebugLabel(gfxDevice.getVkDevice(), image.imageView, viewName.c_str());
|
||||
image.debugName = debugName;
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,9 @@ void GameRenderer::init(GfxDevice& gfxDevice, RenderResources& _resources, glm::
|
||||
lightCullingPipeline = std::make_unique<LightCullingPipeline>();
|
||||
lightCullingPipeline->init(gfxDevice, _resources, drawImageSize);
|
||||
|
||||
shadowMapPipeline = std::make_unique<ShadowMapPipeline>();
|
||||
shadowMapPipeline->init(gfxDevice, _resources);
|
||||
|
||||
lineRenderingPass = std::make_unique<LineRenderingPass>();
|
||||
lineRenderingPass->init(gfxDevice, drawImageFormat);
|
||||
|
||||
@@ -66,6 +69,7 @@ void GameRenderer::beginDrawing(GfxDevice& gfxDevice)
|
||||
meshDrawCommands.clear();
|
||||
skinningPipeline->beginDrawing(gfxDevice.getCurrentFrameIndex());
|
||||
lightCullingPipeline->beginDrawing(gfxDevice.getCurrentFrameIndex());
|
||||
shadowLightDirection.reset();
|
||||
QuadRenderingManager::GetInstance().ClearQuads();
|
||||
}
|
||||
|
||||
@@ -97,9 +101,11 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
|
||||
{
|
||||
ZoneScopedN("GameRenderer::draw");
|
||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "GameRenderer::draw");
|
||||
vkutil::ScopedCmdLabel frameLabel(cmd, "GameRenderer::draw", vkutil::labelColors::frame);
|
||||
|
||||
{
|
||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Skinning");
|
||||
vkutil::ScopedCmdLabel label(cmd, "Skinning", vkutil::labelColors::compute);
|
||||
|
||||
skinningPipeline->flushCurrentFrame(
|
||||
cmd,
|
||||
@@ -118,10 +124,17 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
|
||||
|
||||
{
|
||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Lights Buffer Flush");
|
||||
vkutil::ScopedCmdLabel label(cmd, "Lights Buffer Flush", vkutil::labelColors::transfer);
|
||||
|
||||
lightCullingPipeline->flushCurrentFrame(cmd, gfxDevice);
|
||||
}
|
||||
|
||||
const glm::mat4 lightViewProj = shadowLightDirection
|
||||
? shadowMapPipeline->calculateLightViewProj(
|
||||
*shadowLightDirection,
|
||||
sceneData.camera.GetPosition())
|
||||
: glm::mat4{1.0f};
|
||||
|
||||
// TODO: there has to be a better way to do this
|
||||
const auto gpuSceneData = GPUSceneData{
|
||||
.view = sceneData.camera.GetViewMatrix(),
|
||||
@@ -139,11 +152,15 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
|
||||
.numLights = lightCullingPipeline->getNumLights(),
|
||||
.tileCountX = lightCullingPipeline->getTileCount().x,
|
||||
.debugLightHeatmap = renderLightHeatmap ? 1u : 0u,
|
||||
.shadowMapId = static_cast<std::uint32_t>(shadowMapPipeline->getShadowMapId()),
|
||||
.shadowNormalOffset = shadowMapPipeline->getNormalOffset(),
|
||||
.padding = 0,
|
||||
.lightViewProj = lightViewProj,
|
||||
};
|
||||
|
||||
{
|
||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Material Buffer Barrier");
|
||||
vkutil::ScopedCmdLabel label(cmd, "Material Buffer Barrier", vkutil::labelColors::transfer);
|
||||
|
||||
vkutil::bufferHostWriteToShaderReadBarrier(
|
||||
cmd,
|
||||
@@ -155,6 +172,7 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
|
||||
|
||||
{
|
||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Upload Scene Data");
|
||||
vkutil::ScopedCmdLabel label(cmd, "Upload Scene Data", vkutil::labelColors::transfer);
|
||||
|
||||
sceneDataBuffer.uploadNewData(
|
||||
cmd,
|
||||
@@ -189,8 +207,22 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
|
||||
depthImage.setLayout(VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL);
|
||||
}
|
||||
|
||||
if (shadowLightDirection)
|
||||
{
|
||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Shadow Map");
|
||||
vkutil::ScopedCmdLabel label(cmd, "Shadow Map", vkutil::labelColors::shadow);
|
||||
|
||||
shadowMapPipeline->draw(
|
||||
cmd,
|
||||
*resources,
|
||||
sceneDataBuffer.getBuffer(),
|
||||
meshDrawCommands,
|
||||
sortedMeshDrawCommands);
|
||||
}
|
||||
|
||||
{
|
||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Depth Prepass");
|
||||
vkutil::ScopedCmdLabel label(cmd, "Depth Prepass", vkutil::labelColors::depth);
|
||||
|
||||
auto depthPrepassInfo = vkutil::createRenderingInfo({
|
||||
.renderExtent = drawImage.getExtent2D(),
|
||||
@@ -215,6 +247,7 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
|
||||
|
||||
{
|
||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Light Culling");
|
||||
vkutil::ScopedCmdLabel label(cmd, "Light Culling", vkutil::labelColors::compute);
|
||||
|
||||
vkutil::transitionImage(
|
||||
cmd,
|
||||
@@ -247,6 +280,8 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
|
||||
renderInfo.renderingInfo.pColorAttachments = &renderInfo.colorAttachment;
|
||||
renderInfo.renderingInfo.pDepthAttachment = &renderInfo.depthAttachment;
|
||||
|
||||
vkutil::cmdBeginLabel(cmd, "Forward Pass", vkutil::labelColors::opaque);
|
||||
|
||||
{
|
||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "vkCmdBeginRendering");
|
||||
|
||||
@@ -255,6 +290,7 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
|
||||
|
||||
{
|
||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "MeshPipeline::draw");
|
||||
vkutil::ScopedCmdLabel label(cmd, "MeshPipeline::draw", vkutil::labelColors::opaque);
|
||||
|
||||
meshPipeline->draw(
|
||||
cmd,
|
||||
@@ -268,6 +304,7 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
|
||||
|
||||
{
|
||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "SkyboxPipeline::draw");
|
||||
vkutil::ScopedCmdLabel label(cmd, "SkyboxPipeline::draw", vkutil::labelColors::sky);
|
||||
|
||||
skyboxPipeline->draw(cmd, *resources, camera);
|
||||
}
|
||||
@@ -278,8 +315,11 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
|
||||
vkCmdEndRendering(cmd);
|
||||
}
|
||||
|
||||
vkutil::cmdEndLabel(cmd);
|
||||
|
||||
{
|
||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "LineRenderingPass::draw");
|
||||
vkutil::ScopedCmdLabel label(cmd, "LineRenderingPass::draw", vkutil::labelColors::debug);
|
||||
|
||||
lineRenderingPass->draw(
|
||||
cmd,
|
||||
@@ -291,6 +331,7 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
|
||||
|
||||
{
|
||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "QuadRendererPass::draw");
|
||||
vkutil::ScopedCmdLabel label(cmd, "QuadRendererPass::draw", vkutil::labelColors::ui);
|
||||
|
||||
quadRendererPass->draw(
|
||||
cmd,
|
||||
@@ -302,7 +343,6 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
|
||||
static_cast<float>(drawImage.extent.height),
|
||||
0.0f, -1.0f, 1.0f));
|
||||
}
|
||||
// vkutil::cmdEndLabel(cmd);
|
||||
}
|
||||
|
||||
void GameRenderer::drawQuads(
|
||||
@@ -335,6 +375,9 @@ void GameRenderer::cleanup(GfxDevice& gfxDevice)
|
||||
if (lightCullingPipeline)
|
||||
lightCullingPipeline->cleanup(gfxDevice);
|
||||
|
||||
if (shadowMapPipeline)
|
||||
shadowMapPipeline->cleanup(device);
|
||||
|
||||
if (skyboxPipeline)
|
||||
skyboxPipeline->cleanup(device);
|
||||
|
||||
@@ -358,6 +401,8 @@ void GameRenderer::cleanup(GfxDevice& gfxDevice)
|
||||
lineRenderingPass.reset();
|
||||
quadRendererPass.reset();
|
||||
lightCullingPipeline.reset();
|
||||
shadowMapPipeline.reset();
|
||||
shadowLightDirection.reset();
|
||||
pendingMaterialUploads.clear();
|
||||
meshDrawCommands.clear();
|
||||
sortedMeshDrawCommands.clear();
|
||||
@@ -402,7 +447,18 @@ void GameRenderer::drawSkinnedMesh(MeshID id,
|
||||
|
||||
void GameRenderer::submitLight(const Light& light)
|
||||
{
|
||||
lightCullingPipeline->appendLight(light);
|
||||
Light submittedLight = light;
|
||||
|
||||
// there's a single shadow map, the first shadow casting directional light gets it
|
||||
submittedLight.castShadows =
|
||||
light.castShadows && light.type == LightType::Directional && !shadowLightDirection;
|
||||
|
||||
if (submittedLight.castShadows)
|
||||
{
|
||||
shadowLightDirection = light.direction;
|
||||
}
|
||||
|
||||
lightCullingPipeline->appendLight(submittedLight);
|
||||
}
|
||||
|
||||
const GPUImage& GameRenderer::getDrawImage() const
|
||||
|
||||
@@ -71,6 +71,8 @@ void CubeMap::RenderToCubemap(
|
||||
}
|
||||
|
||||
gfxDevice.getImmediateExecuter().immediateSubmit([&](VkCommandBuffer cmd) {
|
||||
vkutil::ScopedCmdLabel label(cmd, "HDRI To Cubemap", vkutil::labelColors::sky);
|
||||
|
||||
VkImageMemoryBarrier barrier{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};
|
||||
barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
@@ -108,6 +110,8 @@ void CubeMap::RenderToCubemap(
|
||||
scissor.extent = {size, size};
|
||||
|
||||
for (std::uint32_t face = 0; face < 6; ++face) {
|
||||
vkutil::cmdBeginLabelFormatted(cmd, vkutil::labelColors::draw, "Cubemap Face %u", face);
|
||||
|
||||
PC pc{};
|
||||
pc.viewMtx = viewMatrices[face];
|
||||
pc.projMtx = m_projection;
|
||||
@@ -153,6 +157,8 @@ void CubeMap::RenderToCubemap(
|
||||
vkCmdDraw(cmd, 36, 1, 0, 0);
|
||||
|
||||
vkCmdEndRendering(cmd);
|
||||
|
||||
vkutil::cmdEndLabel(cmd);
|
||||
}
|
||||
|
||||
barrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
@@ -199,6 +205,7 @@ void CubeMap::CreateCubeMap(
|
||||
.mipMap = false,
|
||||
.isCubemap = true
|
||||
});
|
||||
vkutil::addDebugLabel(gfxDevice.getDevice(), cubeMapImage.image, "skybox cubemap");
|
||||
|
||||
std::array<VkImageView, 6> faceViews{};
|
||||
|
||||
@@ -220,6 +227,9 @@ void CubeMap::CreateCubeMap(
|
||||
&viewInfo,
|
||||
nullptr,
|
||||
&faceViews[face]));
|
||||
|
||||
const std::string faceViewName = "skybox cubemap face " + std::to_string(face) + " (view)";
|
||||
vkutil::addDebugLabel(gfxDevice.getDevice(), faceViews[face], faceViewName.c_str());
|
||||
}
|
||||
|
||||
spdlog::info("HDRI image id = {}", m_hdrImage);
|
||||
@@ -305,6 +315,7 @@ void CubeMap::InitCubemapPipeline(
|
||||
|
||||
PipelineConfigInfo pipelineConfig{};
|
||||
Pipeline::DefaultPipelineConfigInfo(pipelineConfig);
|
||||
pipelineConfig.name = "HDRI To Cubemap Pipeline";
|
||||
|
||||
pipelineConfig.vertexAttributeDescriptions = {};
|
||||
pipelineConfig.vertexBindingDescriptions = {};
|
||||
|
||||
@@ -21,6 +21,22 @@ void Swapchain::initSync(VkDevice device) {
|
||||
for (std::uint32_t i = 0; i < FRAMES_IN_FLIGHT; ++i) {
|
||||
VK_CHECK(vkCreateFence(device, &fenceCreateInfo, nullptr, &frames[i].renderFence));
|
||||
VK_CHECK(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &frames[i].swapchainSemaphore));
|
||||
|
||||
const auto fenceName = std::format("frame {} render fence", i);
|
||||
const auto semaphoreName = std::format("frame {} swapchain semaphore", i);
|
||||
vkutil::addDebugLabel(device, frames[i].renderFence, fenceName.c_str());
|
||||
vkutil::addDebugLabel(device, frames[i].swapchainSemaphore, semaphoreName.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void Swapchain::nameSwapchainObjects(VkDevice device) const {
|
||||
for (std::size_t i = 0; i < images.size(); ++i) {
|
||||
const auto imageName = std::format("swapchain image {}", i);
|
||||
const auto viewName = std::format("swapchain image {} (view)", i);
|
||||
const auto semaphoreName = std::format("swapchain image {} render semaphore", i);
|
||||
vkutil::addDebugLabel(device, images[i], imageName.c_str());
|
||||
vkutil::addDebugLabel(device, imageViews[i], viewName.c_str());
|
||||
vkutil::addDebugLabel(device, imageRenderSemaphores[i], semaphoreName.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +101,8 @@ void Swapchain::createSwapchain(
|
||||
VK_CHECK(vkCreateSemaphore(vkbDevice, &sci, nullptr, &sem));
|
||||
}
|
||||
|
||||
nameSwapchainObjects(vkbDevice);
|
||||
|
||||
extent = m_swapchain.extent;
|
||||
dirty = false;
|
||||
}
|
||||
@@ -187,6 +205,8 @@ void Swapchain::recreateSwapchain(
|
||||
VK_CHECK(vkCreateSemaphore(vkbDevice, &sci, nullptr, &sem));
|
||||
}
|
||||
|
||||
nameSwapchainObjects(vkbDevice);
|
||||
|
||||
extent = m_swapchain.extent;
|
||||
dirty = false;
|
||||
}
|
||||
|
||||
+101
-101
@@ -188,54 +188,124 @@ void vkutil::bufferHostWriteToTransferReadBarrier(
|
||||
}
|
||||
|
||||
|
||||
void vkutil::addDebugLabel(VkDevice device, VkImage image, const char* label) {
|
||||
void vkutil::addDebugLabel(VkDevice device, VkObjectType objectType, std::uint64_t objectHandle, const char* label) {
|
||||
if (!vkSetDebugUtilsObjectNameEXT || device == VK_NULL_HANDLE || objectHandle == 0 || label == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
|
||||
.objectType = VK_OBJECT_TYPE_IMAGE,
|
||||
.objectHandle = (std::uint64_t)image,
|
||||
.objectType = objectType,
|
||||
.objectHandle = objectHandle,
|
||||
.pObjectName = label,
|
||||
};
|
||||
if (vkSetDebugUtilsObjectNameEXT) vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
|
||||
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
|
||||
}
|
||||
|
||||
void vkutil::addDebugLabel(VkDevice device, VkImage image, const char* label) {
|
||||
addDebugLabel(device, VK_OBJECT_TYPE_IMAGE, reinterpret_cast<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) {
|
||||
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
|
||||
.objectType = VK_OBJECT_TYPE_SHADER_MODULE,
|
||||
.objectHandle = (std::uint64_t)shaderModule,
|
||||
.pObjectName = label,
|
||||
};
|
||||
if (vkSetDebugUtilsObjectNameEXT) vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
|
||||
addDebugLabel(device, VK_OBJECT_TYPE_SHADER_MODULE, reinterpret_cast<std::uint64_t>(shaderModule), label);
|
||||
}
|
||||
|
||||
void vkutil::addDebugLabel(VkDevice device, VkPipeline pipeline, const char* label) {
|
||||
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
|
||||
.objectType = VK_OBJECT_TYPE_PIPELINE,
|
||||
.objectHandle = (std::uint64_t)pipeline,
|
||||
.pObjectName = label,
|
||||
};
|
||||
if (vkSetDebugUtilsObjectNameEXT) vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
|
||||
addDebugLabel(device, VK_OBJECT_TYPE_PIPELINE, reinterpret_cast<std::uint64_t>(pipeline), label);
|
||||
}
|
||||
|
||||
void vkutil::addDebugLabel(VkDevice device, VkPipelineLayout layout, const char* label) {
|
||||
addDebugLabel(device, VK_OBJECT_TYPE_PIPELINE_LAYOUT, reinterpret_cast<std::uint64_t>(layout), label);
|
||||
}
|
||||
|
||||
void vkutil::addDebugLabel(VkDevice device, VkBuffer buffer, const char* label) {
|
||||
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
|
||||
.objectType = VK_OBJECT_TYPE_BUFFER,
|
||||
.objectHandle = (std::uint64_t)buffer,
|
||||
.pObjectName = label,
|
||||
};
|
||||
if (vkSetDebugUtilsObjectNameEXT) vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
|
||||
addDebugLabel(device, VK_OBJECT_TYPE_BUFFER, reinterpret_cast<std::uint64_t>(buffer), label);
|
||||
}
|
||||
|
||||
void vkutil::addDebugLabel(VkDevice device, VkSampler sampler, const char* label) {
|
||||
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
|
||||
.objectType = VK_OBJECT_TYPE_SAMPLER,
|
||||
.objectHandle = (std::uint64_t)sampler,
|
||||
.pObjectName = label,
|
||||
addDebugLabel(device, VK_OBJECT_TYPE_SAMPLER, reinterpret_cast<std::uint64_t>(sampler), label);
|
||||
}
|
||||
|
||||
void vkutil::addDebugLabel(VkDevice device, VkDescriptorPool descriptorPool, const char* 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) {
|
||||
@@ -319,76 +389,6 @@ VkShaderModule vkutil::loadShaderModule(const std::filesystem::path& path, VkDev
|
||||
return shaderModule;
|
||||
}
|
||||
|
||||
void addDebugLabel(VkDevice device, VkImage image, const char* label) {
|
||||
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
|
||||
.objectType = VK_OBJECT_TYPE_IMAGE,
|
||||
.objectHandle = (std::uint64_t)image,
|
||||
.pObjectName = label,
|
||||
};
|
||||
if (vkSetDebugUtilsObjectNameEXT) vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
|
||||
}
|
||||
|
||||
void addDebugLabel(VkDevice device, VkImageView imageView, const char* label) {
|
||||
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
|
||||
.objectType = VK_OBJECT_TYPE_IMAGE_VIEW,
|
||||
.objectHandle = (std::uint64_t)imageView,
|
||||
.pObjectName = label,
|
||||
};
|
||||
if (vkSetDebugUtilsObjectNameEXT) vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
|
||||
}
|
||||
|
||||
void addDebugLabel(VkDevice device, VkShaderModule shaderModule, const char* label) {
|
||||
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
|
||||
.objectType = VK_OBJECT_TYPE_SHADER_MODULE,
|
||||
.objectHandle = (std::uint64_t)shaderModule,
|
||||
.pObjectName = label,
|
||||
};
|
||||
if (vkSetDebugUtilsObjectNameEXT) vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
|
||||
}
|
||||
|
||||
void addDebugLabel(VkDevice device, VkPipeline pipeline, const char* label) {
|
||||
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
|
||||
.objectType = VK_OBJECT_TYPE_PIPELINE,
|
||||
.objectHandle = (std::uint64_t)pipeline,
|
||||
.pObjectName = label,
|
||||
};
|
||||
if (vkSetDebugUtilsObjectNameEXT) vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
|
||||
}
|
||||
|
||||
void addDebugLabel(VkDevice device, VkPipelineLayout layout, const char* label) {
|
||||
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
|
||||
.objectType = VK_OBJECT_TYPE_PIPELINE_LAYOUT,
|
||||
.objectHandle = (std::uint64_t)layout,
|
||||
.pObjectName = label,
|
||||
};
|
||||
if (vkSetDebugUtilsObjectNameEXT) vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
|
||||
}
|
||||
|
||||
void addDebugLabel(VkDevice device, VkBuffer buffer, const char* label) {
|
||||
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
|
||||
.objectType = VK_OBJECT_TYPE_BUFFER,
|
||||
.objectHandle = (std::uint64_t)buffer,
|
||||
.pObjectName = label,
|
||||
};
|
||||
if (vkSetDebugUtilsObjectNameEXT) vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
|
||||
}
|
||||
|
||||
void addDebugLabel(VkDevice device, VkSampler sampler, const char* label) {
|
||||
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
|
||||
.objectType = VK_OBJECT_TYPE_SAMPLER,
|
||||
.objectHandle = (std::uint64_t)sampler,
|
||||
.pObjectName = label,
|
||||
};
|
||||
if (vkSetDebugUtilsObjectNameEXT) vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
|
||||
}
|
||||
|
||||
vkutil::RenderInfo createRenderingInfo(const vkutil::RenderingInfoParams& params) {
|
||||
return vkutil::createRenderingInfo(params);
|
||||
}
|
||||
|
||||
+110
-98
@@ -153,65 +153,65 @@ void LightKeeper::customInit()
|
||||
characterModelOptions.loadSkeleton = true;
|
||||
characterModelOptions.loadAnimations = true;
|
||||
|
||||
// auto charModel = ModelDoc::LoadModel(
|
||||
// AssetFS::GetInstance().GetFullPath("engine://cotw-capybara-male/source/capybara.fbx").generic_string(),
|
||||
// characterModelOptions
|
||||
// );
|
||||
// ModelDocUtils::LogModelDocSummary(charModel, "capybara.fbx");
|
||||
//
|
||||
// const auto& charPrimitive = ModelDocUtils::GetFirstSkinnedPrimitiveOrFirstOrThrow(
|
||||
// charModel,
|
||||
// "engine://cotw-capybara-male/source/capybara.fbx"
|
||||
// );
|
||||
//
|
||||
// const auto charMeshID = resources.meshes().addMesh(gfxDevice, charPrimitive.mesh);
|
||||
//
|
||||
// const auto charTexturePath = ModelDocUtils::PickTexturePath(
|
||||
// charModel,
|
||||
// charPrimitive,
|
||||
// AssetFS::GetInstance().GetFullPath(
|
||||
// "engine://cotw-capybara-male/textures/capybara_male_light_brown_dif.ddsc.DECA.RE.pngballs")
|
||||
// );
|
||||
//
|
||||
// const auto charTextureID = resources.loadImageFromFile(gfxDevice, charTexturePath);
|
||||
// // const auto charTextureID = gfxDevice.loadImageFromFile(AssetFS::GetInstance().GetFullPath("engine://char.jpg"));
|
||||
// const auto charMaterialID = resources.materials().addMaterial({
|
||||
// .baseColor = ModelDocUtils::GetImportedBaseColor(
|
||||
// charModel, charPrimitive),
|
||||
// .diffuseTexture = charTextureID,
|
||||
// .name = ModelDocUtils::GetImportedMaterialName(
|
||||
// charModel, charPrimitive, "CharacterMaterial"),
|
||||
// });
|
||||
//
|
||||
// const auto charMeshComp = CharObj->AddComponent<MeshRendererComponent>();
|
||||
// charMeshComp->SetMeshID(charMeshID);
|
||||
// charMeshComp->SetMaterialID(charMaterialID);
|
||||
//
|
||||
// const auto animator = CharObj->AddComponent<Animator>();
|
||||
// animator->setSkeleton(std::move(charModel.skeleton));
|
||||
//
|
||||
// std::string firstAnimationName;
|
||||
//
|
||||
// for (auto& clip : charModel.animations)
|
||||
// {
|
||||
// spdlog::info("Loaded animation: '{}' ({:.2f}s)", clip.name, clip.duration);
|
||||
//
|
||||
// if (firstAnimationName.empty())
|
||||
// firstAnimationName = clip.name;
|
||||
//
|
||||
// animator->addClip(std::make_shared<SkeletalAnimation>(std::move(clip)));
|
||||
// }
|
||||
//
|
||||
// if (!firstAnimationName.empty())
|
||||
// {
|
||||
// spdlog::info("Playing animation: '{}'", firstAnimationName);
|
||||
// animator->play(firstAnimationName);
|
||||
// }
|
||||
//
|
||||
// animator->play("capybara_canter_fwd_01|capybara_canter_fwd_01|run");
|
||||
//
|
||||
// CharObj->GetTransform().SetWorldPosition(glm::vec3(0.f, 0.f, 0.f));
|
||||
// CharObj->GetTransform().SetWorldScale(0.01f, 0.01f, 0.01f);
|
||||
auto charModel = ModelDoc::LoadModel(
|
||||
AssetFS::GetInstance().GetFullPath("engine://cotw-capybara-male/source/capybara.fbx").generic_string(),
|
||||
characterModelOptions
|
||||
);
|
||||
ModelDocUtils::LogModelDocSummary(charModel, "capybara.fbx");
|
||||
|
||||
const auto& charPrimitive = ModelDocUtils::GetFirstSkinnedPrimitiveOrFirstOrThrow(
|
||||
charModel,
|
||||
"engine://cotw-capybara-male/source/capybara.fbx"
|
||||
);
|
||||
|
||||
const auto charMeshID = resources.meshes().addMesh(gfxDevice, charPrimitive.mesh);
|
||||
|
||||
const auto charTexturePath = ModelDocUtils::PickTexturePath(
|
||||
charModel,
|
||||
charPrimitive,
|
||||
AssetFS::GetInstance().GetFullPath(
|
||||
"engine://cotw-capybara-male/textures/capybara_male_light_brown_dif.ddsc.DECA.RE.pngballs")
|
||||
);
|
||||
|
||||
const auto charTextureID = resources.loadImageFromFile(gfxDevice, charTexturePath);
|
||||
// const auto charTextureID = gfxDevice.loadImageFromFile(AssetFS::GetInstance().GetFullPath("engine://char.jpg"));
|
||||
const auto charMaterialID = resources.materials().addMaterial({
|
||||
.baseColor = ModelDocUtils::GetImportedBaseColor(
|
||||
charModel, charPrimitive),
|
||||
.diffuseTexture = charTextureID,
|
||||
.name = ModelDocUtils::GetImportedMaterialName(
|
||||
charModel, charPrimitive, "CharacterMaterial"),
|
||||
});
|
||||
|
||||
const auto charMeshComp = CharObj->AddComponent<MeshRendererComponent>();
|
||||
charMeshComp->SetMeshID(charMeshID);
|
||||
charMeshComp->SetMaterialID(charMaterialID);
|
||||
|
||||
const auto animator = CharObj->AddComponent<Animator>();
|
||||
animator->setSkeleton(std::move(charModel.skeleton));
|
||||
|
||||
std::string firstAnimationName;
|
||||
|
||||
for (auto& clip : charModel.animations)
|
||||
{
|
||||
spdlog::info("Loaded animation: '{}' ({:.2f}s)", clip.name, clip.duration);
|
||||
|
||||
if (firstAnimationName.empty())
|
||||
firstAnimationName = clip.name;
|
||||
|
||||
animator->addClip(std::make_shared<SkeletalAnimation>(std::move(clip)));
|
||||
}
|
||||
|
||||
if (!firstAnimationName.empty())
|
||||
{
|
||||
spdlog::info("Playing animation: '{}'", firstAnimationName);
|
||||
animator->play(firstAnimationName);
|
||||
}
|
||||
|
||||
animator->play("capybara_canter_fwd_01|capybara_canter_fwd_01|run");
|
||||
|
||||
CharObj->GetTransform().SetWorldPosition(glm::vec3(0.f, 0.f, 0.f));
|
||||
CharObj->GetTransform().SetWorldScale(0.01f, 0.01f, 0.01f);
|
||||
|
||||
// ModelDoc::LoadOptions options{};
|
||||
// options.meshImportMode = ModelDoc::MeshImportMode::MergedPerNode;
|
||||
@@ -450,46 +450,58 @@ void LightKeeper::customInit()
|
||||
triggerObj->GetTransform().SetWorldPosition({0.0f, 0.0f, 0.0f});
|
||||
|
||||
// Forward+ test lights: a dim sun and a ring of colored point lights orbiting the origin
|
||||
auto sunObj = scene.CreateGameObject("Sun");
|
||||
auto* sun = sunObj->AddComponent<LightComponent>(LightType::Directional);
|
||||
sun->SetColor(glm::vec3{1.0f, 0.95f, 0.85f});
|
||||
sun->SetIntensity(1.0f);
|
||||
sunObj->GetTransform().SetWorldRotation(glm::vec3{-50.0f, 30.0f, 0.0f});
|
||||
// auto sunObj = scene.CreateGameObject("Sun");
|
||||
// auto* sun = sunObj->AddComponent<LightComponent>(LightType::Directional);
|
||||
// sun->SetColor(glm::vec3{1.0f, 0.95f, 0.85f});
|
||||
// sun->SetIntensity(1.0f);
|
||||
// sun->SetCastShadows(true);
|
||||
// sunObj->GetTransform().SetWorldRotation(glm::vec3{-50.0f, 30.0f, 0.0f});
|
||||
//
|
||||
// // static shadow casters, so the sun's shadows are visible without spawning balls
|
||||
// for (int i = 0; i < 3; i++)
|
||||
// {
|
||||
// auto casterObj = scene.CreateGameObject("ShadowCaster " + std::to_string(i));
|
||||
// auto* casterMesh = casterObj->AddComponent<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;
|
||||
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});
|
||||
}
|
||||
|
||||
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);
|
||||
// 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");
|
||||
texID = resources.loadImageFromFile(gfxDevice, texPath);
|
||||
|
||||
@@ -457,6 +457,7 @@ namespace {
|
||||
light->SetIntensity(4.0f);
|
||||
light->SetRange(7.5f);
|
||||
light->SetConeAngles(0.2f, 0.4f);
|
||||
light->SetCastShadows(true);
|
||||
|
||||
const auto data = light->Serialize();
|
||||
|
||||
@@ -472,6 +473,8 @@ namespace {
|
||||
"LightComponent must round-trip intensity and range");
|
||||
check(loaded->GetInnerConeAngle() == 0.2f && loaded->GetOuterConeAngle() == 0.4f,
|
||||
"LightComponent must round-trip its cone angles");
|
||||
check(loaded->GetCastShadows(),
|
||||
"LightComponent must round-trip its cast shadows flag");
|
||||
|
||||
loaded->SetConeAngles(0.5f, 0.1f);
|
||||
check(loaded->GetOuterConeAngle() >= loaded->GetInnerConeAngle(),
|
||||
|
||||
Reference in New Issue
Block a user