shadow.cpp

cross platform rendering playground

src/passes/shadow/shadow.cpp

5.16 KB
#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