Skip to content

Commit 893f28b

Browse files
committed
core : Revamp depth and shadow map pass classes
+ make them RAII + add destructors
1 parent b92c42a commit 893f28b

File tree

6 files changed

+205
-138
lines changed

6 files changed

+205
-138
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2121
- multibody/RobotScene : early return if pipeline is nullptr (https://github.com/Simple-Robotics/candlewick/pull/58)
2222
- multibody/RobotScene : reorganize pipelines (accomodate for transparent PBR shader)
2323
- shaders : refactor basic PBR shader (move some functions to new `pbr_lighting.glsl`) (https://github.com/Simple-Robotics/candlewick/pull/58)
24-
- interleave debug scene render between robot render system opaque and transparent passes
24+
- interleave debug scene render between robot render system opaque and transparent passes (https://github.com/Simple-Robotics/candlewick/pull/59)
25+
- revamp depth and shadow map pass classes
2526

2627
## [0.0.6] - 2025-05-14
2728

examples/Ur5WithSystems.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -300,12 +300,12 @@ int main(int argc, char **argv) {
300300
robot_debug.addFrameTriad(debug_scene, ee_frame_id);
301301
robot_debug.addFrameVelocityArrow(debug_scene, ee_frame_id);
302302

303-
auto depthPassInfo =
304-
DepthPassInfo::create(renderer, plane_obj.mesh.layout(), NULL,
305-
{SDL_GPU_CULLMODE_NONE, 0.05f, 0.f, true, false});
303+
DepthPass depthPass(renderer.device, plane_obj.mesh.layout(),
304+
renderer.depth_texture,
305+
{SDL_GPU_CULLMODE_NONE, 0.05f, 0.f, true, false});
306306
auto &shadowPassInfo = robot_scene.shadowPass;
307307
auto shadowDebugPass =
308-
DepthDebugPass::create(renderer, shadowPassInfo.depthTexture);
308+
DepthDebugPass::create(renderer, shadowPassInfo.shadowMap);
309309

310310
auto depthDebugPass =
311311
DepthDebugPass::create(renderer, renderer.depth_texture);
@@ -464,7 +464,7 @@ int main(int argc, char **argv) {
464464
auto &castables = robot_scene.castables();
465465
renderShadowPassFromAABB(command_buffer, shadowPassInfo, sceneLight,
466466
castables, worldSpaceBounds);
467-
renderDepthOnlyPass(command_buffer, depthPassInfo, viewProj, castables);
467+
depthPass.render(command_buffer, viewProj, castables);
468468
switch (g_showDebugViz) {
469469
case FULL_RENDER:
470470
robot_scene.renderOpaque(command_buffer, g_camera);
@@ -501,7 +501,7 @@ int main(int argc, char **argv) {
501501

502502
SDL_WaitForGPUIdle(renderer.device);
503503
frustumBoundsDebug.release();
504-
depthPassInfo.release();
504+
depthPass.release();
505505
shadowDebugPass.release(renderer.device);
506506
depthDebugPass.release(renderer.device);
507507
robot_scene.release();

src/candlewick/core/DepthAndShadowPass.cpp

Lines changed: 108 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,10 @@
99

1010
namespace candlewick {
1111

12-
DepthPassInfo DepthPassInfo::create(const Renderer &renderer,
13-
const MeshLayout &layout,
14-
SDL_GPUTexture *depth_texture,
15-
const Config &config) {
16-
if (depth_texture == nullptr)
17-
depth_texture = renderer.depth_texture;
18-
const Device &device = renderer.device;
12+
DepthPass::DepthPass(const Device &device, const MeshLayout &layout,
13+
SDL_GPUTexture *depth_texture, SDL_GPUTextureFormat format,
14+
const Config &config)
15+
: _device(device), depthTexture(depth_texture) {
1916
auto vertexShader = Shader::fromMetadata(device, "ShadowCast.vert");
2017
auto fragmentShader = Shader::fromMetadata(device, "ShadowCast.frag");
2118
SDL_GPUGraphicsPipelineCreateInfo pipeline_desc{
@@ -35,34 +32,88 @@ DepthPassInfo DepthPassInfo::create(const Renderer &renderer,
3532
.enable_depth_write = true},
3633
.target_info{.color_target_descriptions = nullptr,
3734
.num_color_targets = 0,
38-
.depth_stencil_format = renderer.depthFormat(),
35+
.depth_stencil_format = format,
3936
.has_depth_stencil_target = true},
4037
};
41-
return DepthPassInfo{
42-
.depthTexture = depth_texture,
43-
.pipeline = SDL_CreateGPUGraphicsPipeline(device, &pipeline_desc),
44-
._device = device,
38+
pipeline = SDL_CreateGPUGraphicsPipeline(device, &pipeline_desc);
39+
if (!pipeline)
40+
terminate_with_message(
41+
std::format("Failed to create graphics pipeline: %s.", SDL_GetError()));
42+
}
43+
44+
DepthPass::DepthPass(const Device &device, const MeshLayout &layout,
45+
const Texture &texture, const Config &config)
46+
: DepthPass(device, layout, texture, texture.format(), config) {}
47+
48+
DepthPass::DepthPass(DepthPass &&other) noexcept
49+
: _device(other._device)
50+
, depthTexture(other.depthTexture)
51+
, pipeline(other.pipeline) {
52+
other._device = nullptr;
53+
other.depthTexture = nullptr;
54+
other.pipeline = nullptr;
55+
}
56+
57+
DepthPass &DepthPass::operator=(DepthPass &&other) noexcept {
58+
if (this != &other) {
59+
_device = other._device;
60+
depthTexture = other.depthTexture;
61+
pipeline = other.pipeline;
62+
other._device = nullptr;
63+
other.depthTexture = nullptr;
64+
other.pipeline = nullptr;
65+
}
66+
return *this;
67+
}
68+
69+
void DepthPass::render(CommandBuffer &command_buffer, const Mat4f &viewProj,
70+
std::span<const OpaqueCastable> castables) {
71+
SDL_GPUDepthStencilTargetInfo depth_info{
72+
.texture = depthTexture,
73+
.clear_depth = 1.0f,
74+
.load_op = SDL_GPU_LOADOP_CLEAR,
75+
.store_op = SDL_GPU_STOREOP_STORE,
76+
.stencil_load_op = SDL_GPU_LOADOP_DONT_CARE,
77+
.stencil_store_op = SDL_GPU_STOREOP_DONT_CARE,
4578
};
79+
80+
SDL_GPURenderPass *render_pass =
81+
SDL_BeginGPURenderPass(command_buffer, nullptr, 0, &depth_info);
82+
SDL_BindGPUGraphicsPipeline(render_pass, pipeline);
83+
84+
for (auto &[mesh, tr] : castables) {
85+
assert(validateMesh(mesh));
86+
rend::bindMesh(render_pass, mesh);
87+
88+
Mat4f mvp = viewProj * tr;
89+
command_buffer.pushVertexUniform(DepthPass::TRANSFORM_SLOT, mvp);
90+
rend::draw(render_pass, mesh);
91+
}
92+
93+
SDL_EndGPURenderPass(render_pass);
4694
}
4795

48-
void DepthPassInfo::release() {
49-
// do not release depth texture here, because it is assumed to be borrowed.
50-
if (_device && pipeline) {
51-
SDL_ReleaseGPUGraphicsPipeline(_device, pipeline);
96+
void DepthPass::release() noexcept {
97+
if (_device) {
98+
if (pipeline) {
99+
SDL_ReleaseGPUGraphicsPipeline(_device, pipeline);
100+
pipeline = nullptr;
101+
}
102+
if (depthTexture)
103+
depthTexture = nullptr;
52104
}
53-
pipeline = nullptr;
105+
_device = nullptr;
54106
}
55107

56-
ShadowPassInfo ShadowPassInfo::create(const Renderer &renderer,
57-
const MeshLayout &layout,
58-
const Config &config) {
59-
const Device &device = renderer.device;
108+
ShadowMapPass::ShadowMapPass(const Device &device, const MeshLayout &layout,
109+
SDL_GPUTextureFormat format, const Config &config)
110+
: _device(device) {
60111

61112
// TEXTURE
62113
// 2k x 2k texture
63114
SDL_GPUTextureCreateInfo texInfo{
64115
.type = SDL_GPU_TEXTURETYPE_2D,
65-
.format = renderer.depthFormat(),
116+
.format = format,
66117
.usage = SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET |
67118
SDL_GPU_TEXTUREUSAGE_SAMPLER,
68119
.width = config.width,
@@ -73,27 +124,8 @@ ShadowPassInfo ShadowPassInfo::create(const Renderer &renderer,
73124
.props = 0,
74125
};
75126

76-
SDL_GPUTexture *shadowMap = SDL_CreateGPUTexture(device, &texInfo);
77-
SDL_SetGPUTextureName(device, shadowMap, "Shadow map");
78-
if (!shadowMap) {
79-
terminate_with_message(
80-
std::format("Failed to create shadow map texture: %s", SDL_GetError()));
81-
}
82-
83-
DepthPassInfo passInfo =
84-
DepthPassInfo::create(renderer, layout, shadowMap,
85-
{
86-
.cull_mode = SDL_GPU_CULLMODE_NONE,
87-
.depth_bias_constant_factor = 0.f,
88-
.depth_bias_slope_factor = 0.f,
89-
.enable_depth_bias = false,
90-
.enable_depth_clip = false,
91-
});
92-
if (!passInfo.pipeline) {
93-
SDL_ReleaseGPUTexture(device, shadowMap);
94-
terminate_with_message(std::format(
95-
"Failed to create shadow map pipeline: %s", SDL_GetError()));
96-
}
127+
shadowMap = Texture(device, texInfo, "Shadow map");
128+
_depthPass = DepthPass(device, layout, shadowMap, config.depthPassConfig);
97129

98130
SDL_GPUSamplerCreateInfo sample_desc{
99131
.min_filter = SDL_GPU_FILTER_LINEAR,
@@ -105,56 +137,42 @@ ShadowPassInfo ShadowPassInfo::create(const Renderer &renderer,
105137
.compare_op = SDL_GPU_COMPAREOP_LESS,
106138
.enable_compare = true,
107139
};
108-
return ShadowPassInfo{passInfo, SDL_CreateGPUSampler(device, &sample_desc)};
140+
sampler = SDL_CreateGPUSampler(device, &sample_desc);
109141
}
110142

111-
void ShadowPassInfo::release() {
112-
DepthPassInfo::release();
113-
if (depthTexture) {
114-
SDL_ReleaseGPUTexture(_device, depthTexture);
115-
}
116-
depthTexture = nullptr;
117-
if (sampler) {
118-
SDL_ReleaseGPUSampler(_device, sampler);
119-
}
120-
sampler = nullptr;
143+
ShadowMapPass::ShadowMapPass(ShadowMapPass &&other) noexcept
144+
: _device(other._device)
145+
, _depthPass(std::move(other._depthPass))
146+
, shadowMap(std::move(other.shadowMap))
147+
, sampler(other.sampler) {
148+
other._device = nullptr;
149+
other.sampler = nullptr;
121150
}
122151

123-
void renderDepthOnlyPass(CommandBuffer &cmdBuf, const DepthPassInfo &passInfo,
124-
const Mat4f &viewProj,
125-
std::span<const OpaqueCastable> castables) {
126-
SDL_GPUDepthStencilTargetInfo depth_info;
127-
SDL_zero(depth_info);
128-
depth_info.load_op = SDL_GPU_LOADOP_CLEAR;
129-
depth_info.store_op = SDL_GPU_STOREOP_STORE;
130-
depth_info.stencil_load_op = SDL_GPU_LOADOP_DONT_CARE;
131-
depth_info.stencil_store_op = SDL_GPU_STOREOP_DONT_CARE;
132-
depth_info.clear_depth = 1.0f;
133-
// depth texture may be the renderer shared depth texture,
134-
// or a specially created texture.
135-
depth_info.texture = passInfo.depthTexture;
136-
137-
SDL_GPURenderPass *render_pass =
138-
SDL_BeginGPURenderPass(cmdBuf, nullptr, 0, &depth_info);
152+
ShadowMapPass &ShadowMapPass::operator=(ShadowMapPass &&other) noexcept {
153+
_device = other._device;
154+
_depthPass = std::move(other._depthPass);
155+
shadowMap = std::move(other.shadowMap);
156+
sampler = other.sampler;
139157

140-
assert(passInfo.pipeline);
141-
SDL_BindGPUGraphicsPipeline(render_pass, passInfo.pipeline);
158+
other._device = nullptr;
159+
other.sampler = nullptr;
160+
return *this;
161+
}
142162

143-
Mat4f mvp;
144-
for (auto &cs : castables) {
145-
auto &&[mesh, tr] = cs;
146-
assert(validateMesh(mesh));
147-
rend::bindMesh(render_pass, mesh);
148-
mvp.noalias() = viewProj * tr;
149-
cmdBuf.pushVertexUniform(DepthPassInfo::TRANSFORM_SLOT, mvp);
150-
rend::draw(render_pass, mesh);
163+
void ShadowMapPass::release() noexcept {
164+
_depthPass.release();
165+
if (_device) {
166+
if (sampler) {
167+
SDL_ReleaseGPUSampler(_device, sampler);
168+
}
169+
sampler = nullptr;
151170
}
152-
153-
SDL_EndGPURenderPass(render_pass);
171+
shadowMap.destroy();
172+
_device = nullptr;
154173
}
155174

156-
void renderShadowPassFromFrustum(CommandBuffer &cmdBuf,
157-
ShadowPassInfo &passInfo,
175+
void renderShadowPassFromFrustum(CommandBuffer &cmdBuf, ShadowMapPass &passInfo,
158176
const DirectionalLight &dirLight,
159177
std::span<const OpaqueCastable> castables,
160178
const FrustumCornersType &worldSpaceCorners) {
@@ -175,29 +193,30 @@ void renderShadowPassFromFrustum(CommandBuffer &cmdBuf,
175193
shadowOrthographicMatrix({bounds.width(), bounds.height()},
176194
float(bounds.min_.z()), float(bounds.max_.z()));
177195

178-
Mat4f viewProj = passInfo.cam.viewProj();
179-
renderDepthOnlyPass(cmdBuf, passInfo, viewProj, castables);
196+
Mat4f viewProj = lightView * lightProj;
197+
passInfo.render(cmdBuf, viewProj, castables);
180198
}
181199

182-
void renderShadowPassFromAABB(CommandBuffer &cmdBuf, ShadowPassInfo &passInfo,
200+
void renderShadowPassFromAABB(CommandBuffer &cmdBuf, ShadowMapPass &passInfo,
183201
const DirectionalLight &dirLight,
184202
std::span<const OpaqueCastable> castables,
185203
const AABB &worldSceneBounds) {
204+
using Eigen::Vector3d;
205+
186206
Float3 center = worldSceneBounds.center().cast<float>();
187207
float radius = 0.5f * float(worldSceneBounds.size());
188208
radius = std::ceil(radius * 16.f) / 16.f;
189209
Float3 eye = center - radius * dirLight.direction.normalized();
190210
auto &lightView = passInfo.cam.view;
191211
auto &lightProj = passInfo.cam.projection;
192212
lightView = lookAt(eye, center, Float3::UnitZ());
193-
using Eigen::Vector3d;
194213

195214
AABB bounds{Vector3d::Constant(-radius), Vector3d::Constant(radius)};
196215
lightProj =
197216
shadowOrthographicMatrix({bounds.width(), bounds.height()},
198217
float(bounds.min_.z()), float(bounds.max_.z()));
199218

200219
Mat4f viewProj = lightProj * lightView.matrix();
201-
renderDepthOnlyPass(cmdBuf, passInfo, viewProj, castables);
220+
passInfo.render(cmdBuf, viewProj, castables);
202221
}
203222
} // namespace candlewick

0 commit comments

Comments
 (0)