#include "shadow.h"
#include <cstdio>
#include "backend/rhi.h"
#include "pipelines.h"
#include "renderer.h"
#include "shaders/renderer_types.h"
namespace shadow {
Resources record(
rhi::Device &device,
Pipelines &pipelines,
rhi::ShaderCompiler &sc,
FrameGraph &fg,
BufferHandle indirect,
BufferHandle draw_count,
BufferHandle visible,
const u32 cascade_sizes[Light::CASCADE_COUNT]
) {
rhi::RenderPipelineDesc pd;
pd.device = &device;
pd.set_shaders(sc, "src/passes/shadow/shadow.slang", "vsMain", "fsMain");
pd.set_cull_mode(rhi::PipelineCullMode::BACK, rhi::PipelineTriangleWindingOrder::CW);
pd.set_depth_testing(true, true, rhi::PipelineCompareOp::GREATER_EQUAL);
pd.set_depth_clamp(true);
pd.set_depth_bias(true, -1.0f, 0.0f, -2.0f);
pd.set_depth_format(rhi::ImageFormat::D32_FLOAT);
rhi::RenderPipeline &pipeline = pipelines.add_render("Shadow", pd);
Resources res;
for (u32 i = 0; i < Light::CASCADE_COUNT; ++i) {
char cascade_name[64];
(void)snprintf(cascade_name, sizeof(cascade_name), "fg/shadow/cascade[%u]", i);
res.cascade[i] = fg.add_image(
{.desc = {.width = cascade_sizes[i], .height = cascade_sizes[i], .format = rhi::ImageFormat::D32_FLOAT},
.name = cascade_name,
.lifetime = Lifetime::Persistent,
.size_class = SizeOp::Absolute,
.view = {.aspect = rhi::ImageAspect::Depth, .type = rhi::ImageViewType::Sampled, .mip_count = 1}}
);
}
auto &builder = fg.add_pass("CSM Gen");
builder.read(indirect, rhi::ResourceState::IndirectFetch);
builder.read(draw_count, rhi::ResourceState::IndirectFetch);
builder.read(visible, rhi::ResourceState::StorageRead);
for (u32 i = 0; i < Light::CASCADE_COUNT; ++i) {
builder.write(res.cascade[i], rhi::ResourceState::DepthDraw);
}
builder.execute([=, &pipeline](PassContext &ctx) {
FrameData &frame = ctx.frame_data[ctx.frame_index];
ShadowCascadesGPU rc{};
rc.cascade_count = Light::CASCADE_COUNT;
auto &sun = ctx.scene->directional_lights[0];
for (u32 ci = 0; ci < Light::CASCADE_COUNT; ++ci) {
ShadowCascade &ls = sun.cascades[ci];
ShadowCascadeGPU &cv = rc.cascade[ci];
cv.view = ls.view;
cv.view_proj = ls.view_proj;
cv.caster_min_ls = ls.caster_min_ls;
cv.caster_max_ls = ls.caster_max_ls;
f32 texel_world = fmax(ls.width, ls.height) / (f32)cascade_sizes[ci];
cv.texel_size = texel_world;
cv.texel_depth = texel_world / ls.depth;
cv.img = (u32)ctx.view(res.cascade[ci]);
}
memcpy((u8 *)frame.frame_ubo.mapped + offsetof(FrameUBO, shadows), &rc, sizeof(ShadowCascadesGPU));
for (u32 ci = 0; ci < Light::CASCADE_COUNT; ++ci) {
auto cascade_view = ctx.image_view(
res.cascade[ci],
{
.aspect = rhi::ImageAspect::Depth,
.dimension = rhi::TextureViewDimension::TEXTURE_2D,
.mip_start = 0,
.mip_count = 1,
}
);
// Dimensions are image properties — read off the resource, not the view.
u32 cw = ctx.get_image(res.cascade[ci]).desc.width;
u32 ch = ctx.get_image(res.cascade[ci]).desc.height;
rhi::RenderingInfo ri{};
ri.depthAttachment.img = &cascade_view;
ri.depthAttachment.loadOp = rhi::LoadOp::CLEAR;
ri.depthAttachment.storeOp = rhi::StoreOp::STORE;
ri.depthAttachment.clearValue.depthStencil = {0.0f, 0};
ri.width = cw;
ri.height = ch;
rhi::begin_rendering(ctx.cmd, ri);
rhi::set_pipeline(ctx.cmd, pipeline);
rhi::Viewport vp{};
vp.width = (f32)cw;
vp.height = (f32)ch;
rhi::set_viewports(ctx.cmd, &vp);
rhi::Rect scissor{};
scissor.width = (i32)cw;
scissor.height = (i32)ch;
rhi::set_scissors(ctx.cmd, &scissor);
ShadowPushConstants pc{};
pc.frame = (u64)ctx.render_data->frame_ubo_view[ctx.frame_index].slot;
pc.visible_items = rhi::buffer_device_address(ctx.device, ctx.get_buffer(visible));
pc.cascade_idx = ci;
pc.vertex = (u64)ctx.render_data->vertex_view.slot;
{
static_assert(sizeof(pc) <= 128, "push constants exceed global 128B range");
char pc128[128] = {};
std::memcpy(pc128, &pc, sizeof(pc));
rhi::set_constants(ctx.cmd, pipeline, rhi::ShaderStage::ALL, sizeof(pc128), pc128);
}
rhi::set_index_buffer(ctx.cmd, ctx.render_data->index_buffer);
rhi::draw_indexed_indirect(
ctx.cmd,
ctx.get_buffer(indirect),
&ctx.get_buffer(draw_count),
ctx.render_data->render_item_count,
rhi::DRAW_INDEXED_INDIRECT_STRIDE,
0,
0
);
rhi::end_rendering(ctx.cmd);
}
});
return res;
}
} // namespace shadow