debug_lines.cpp

cross platform rendering playground

src/passes/debug/debug_lines.cpp

7 KB
#include "debug_lines.h"

#include "core/logger.h"
#include "debug_draw.h"
#include "pipelines.h"
#include "renderer.h"
#include "shaders/renderer_types.h"

namespace debug_lines {

static rhi::RenderPipeline &make_pipeline(
    rhi::Device &device,
    Pipelines &pipelines,
    rhi::ShaderCompiler &sc,
    const char *name,
    rhi::PipelineCompareOp depth_op
) {
    rhi::RenderPipelineDesc pd;
    pd.device = &device;
    pd.set_shaders(sc, "src/passes/debug/debug_lines.slang", "vsThickMain", "fsMain");
    pd.set_input_topology(rhi::PipelineTopology::TRIANGLES);
    pd.set_depth_testing(true, false, depth_op);
    pd.set_blending();
    pd.set_cull_mode(rhi::PipelineCullMode::NONE, rhi::PipelineTriangleWindingOrder::CW);
    pd.set_color_format(rhi::ImageFormat::RGBA16_FLOAT);
    pd.set_depth_format(rhi::ImageFormat::D32_FLOAT);
    return pipelines.add_render(name, pd);
}

void record(
    rhi::Device &device,
    Pipelines &pipelines,
    rhi::ShaderCompiler &sc,
    FrameGraph &fg,
    ImageHandle main_image,
    ImageHandle depth_image,
    const Settings &settings
) {
    rhi::RenderPipeline &test_pipeline =
        make_pipeline(device, pipelines, sc, "DebugLinesTest", rhi::PipelineCompareOp::GREATER_EQUAL);
    rhi::RenderPipeline &xray_pipeline =
        make_pipeline(device, pipelines, sc, "DebugLinesXray", rhi::PipelineCompareOp::ALWAYS);

    const u64 max_bytes = (u64)MAX_DEBUG_LINES * sizeof(DebugLineVertex);
    auto vb = fg.add_buffer({
        .desc =
            {
                .size = max_bytes,
                .usage = rhi::BufferUsage::StorageBuffer | rhi::BufferUsage::ShaderAddr,
                .memory = rhi::BufferMemType::Upload,
            },
        .name = "fg/debug-lines/verts",
    });

    fg.add_pass("DebugLines")
        .write(main_image, rhi::ResourceState::ColorDraw)
        .read_write(depth_image, rhi::ResourceState::DepthDraw)
        .execute([=, &test_pipeline, &xray_pipeline](PassContext &ctx) {
            const DebugDraw *draw = settings.draw;
            if (draw == nullptr) {
                return;
            }
            usize test_count = settings.enable_test ? draw->test_lines.size() : 0;
            usize xray_count = settings.enable_xray ? draw->xray_lines.size() : 0;
            if (test_count == 0 && xray_count == 0) {
                return;
            }

            auto &buf = ctx.get_buffer(vb);
            u64 test_bytes = (u64)test_count * sizeof(DebugLineVertex);
            u64 xray_bytes = (u64)xray_count * sizeof(DebugLineVertex);
            if (test_bytes + xray_bytes > max_bytes) {
                VEL_WARN("DebugLines truncated: need {}B, have {}B", (test_bytes + xray_bytes), max_bytes);
                // Prefer keeping the overlay: clamp test list, keep xray whole if it fits.
                if (xray_bytes >= max_bytes) {
                    xray_count = MAX_DEBUG_LINES;
                    xray_bytes = max_bytes;
                    test_count = 0;
                    test_bytes = 0;
                } else {
                    test_count = (usize)((max_bytes - xray_bytes) / sizeof(DebugLineVertex));
                    test_count &= ~1u; // keep line pairs whole
                    test_bytes = (u64)test_count * sizeof(DebugLineVertex);
                }
            }
            if (buf.mapped) {
                u8 *dst = (u8 *)buf.mapped;
                if (test_count > 0) {
                    memcpy(dst, draw->test_lines.data(), (usize)test_bytes);
                    dst += test_bytes;
                }
                if (xray_count > 0) {
                    memcpy(dst, draw->xray_lines.data(), (usize)xray_bytes);
                }
            }
            u64 bda = rhi::buffer_device_address(ctx.device, buf);

            auto main_view = ctx.image_view(
                main_image,
                {
                    .aspect = rhi::ImageAspect::Color,
                    .dimension = rhi::TextureViewDimension::TEXTURE_2D,
                    .mip_start = 0,
                    .mip_count = 1,
                    .layer_start = 0,
                    .layer_count = 1,
                }
            );
            auto depth_view = ctx.image_view(
                depth_image,
                {
                    .aspect = rhi::ImageAspect::Depth,
                    .dimension = rhi::TextureViewDimension::TEXTURE_2D,
                    .mip_start = 0,
                    .mip_count = 1,
                    .layer_start = 0,
                    .layer_count = 1,
                }
            );

            rhi::AttachmentDesc color_att{};
            color_att.img = &main_view;
            color_att.loadOp = rhi::LoadOp::LOAD;
            color_att.storeOp = rhi::StoreOp::STORE;
            rhi::AttachmentDesc depth_att{};
            depth_att.img = &depth_view;
            depth_att.loadOp = rhi::LoadOp::LOAD;
            depth_att.storeOp = rhi::StoreOp::STORE;

            rhi::RenderingInfo ri{};
            ri.colorAttachments[0] = color_att;
            ri.colorAttachmentCount = 1;
            ri.depthAttachment = depth_att;
            ri.width = ctx.render_width;
            ri.height = ctx.render_height;

            rhi::begin_rendering(ctx.cmd, ri);

            rhi::Viewport viewport{};
            viewport.width = (f32)ctx.render_width;
            viewport.height = (f32)ctx.render_height;
            rhi::set_viewports(ctx.cmd, &viewport);
            rhi::Rect scissor{};
            scissor.width = (i32)ctx.render_width;
            scissor.height = (i32)ctx.render_height;
            rhi::set_scissors(ctx.cmd, &scissor);

            u64 base_verts = 0;
            for (u32 pass = 0; pass < 2; ++pass) {
                usize count = (pass == 0) ? test_count : xray_count;
                if (count == 0) {
                    continue;
                }
                // Odd counts can't happen via DebugDraw::line() but guard anyway.
                count &= ~1u;
                if (count == 0) {
                    continue;
                }
                rhi::RenderPipeline &pl = (pass == 0) ? test_pipeline : xray_pipeline;
                rhi::set_pipeline(ctx.cmd, pl);

                DebugLinesPushConstants pc{};
                pc.frame = (u64)ctx.render_data->frame_ubo_view[ctx.frame_index].slot;
                pc.lines = bda + base_verts * sizeof(DebugLineVertex);
                pc.viewport_w = (f32)ctx.render_width;
                pc.viewport_h = (f32)ctx.render_height;
                {
                    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, pl, rhi::ShaderStage::ALL, sizeof(pc128), pc128);
                }
                // Thick quads: 6 verts per input line (count is input verts).
                u32 line_count = (u32)count / 2;
                rhi::draw(ctx.cmd, line_count * 6, 1, 0, 0);
                base_verts += count;
            }

            rhi::end_rendering(ctx.cmd);
        });
}

} // namespace debug_lines