#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