Backlog/engine/rend/src/sgpu/DebugDrawSystem.zig

400 lines
13 KiB
Zig

pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "rend.DebugDrawSystem");
allocator: std.mem.Allocator,
debugDraws: core.RingQueueU(DebugPrimitive),
device: *gpu.GPUDevice = undefined,
renderer: *Renderer = undefined,
pipeline: *gpu.GPUGraphicsPipeline = undefined,
ssbo: *gpu.GPUBuffer = undefined,
ssobUpload: *gpu.GPUTransferBuffer = undefined,
meshes: [@as(usize, @intCast(@intFromEnum(DebugPrimitiveType.box) + 1))]?rend.IndexedMesh = .{ null, null, null },
deltaTime: f64 = 0,
drawsThisFrame: std.ArrayListUnmanaged(DebugPrimitive) = .{},
pub const MaxObjectCount = 4096;
const assetReferences = [_]assets.AssetImportReference{
assets.MakeImportRefOptions(
"Mesh",
"m_debug_box",
.{ .path = "embedded:debug_box.obj" },
),
assets.MakeImportRefOptions(
"Mesh",
"m_debug_line",
.{ .path = "embedded:debug_line.obj" },
),
assets.MakeImportRefOptions(
"Mesh",
"m_debug_sphere",
.{ .path = "embedded:debug_sphere.obj" },
),
};
pub fn init(self: *@This(), allocator: std.mem.Allocator, first: bool) !void {
if (!first)
return;
self.* = .{
.debugDraws = try core.RingQueueU(DebugPrimitive).init(allocator, MaxObjectCount),
.allocator = allocator,
};
core.engine_logs("[DebugDrawSystem] starting up...");
_ = try core.fs().installFileBytesMount("embedded:debug_box.obj", @constCast(&primitive_box), true);
_ = try core.fs().installFileBytesMount("embedded:debug_line.obj", @constCast(&primitive_line), true);
_ = try core.fs().installFileBytesMount("embedded:debug_sphere.obj", @constCast(&primitive_sphere), true);
try assets.loadList(assetReferences);
gDebugDrawSys = self;
}
pub fn updateMeshes(self: *@This()) void {
// line = 0,
// sphere = 1,
// box = 2,
const list: [3][]const u8 = .{
"m_debug_line", // 0
"m_debug_sphere", // 1
"m_debug_box", // 2
};
for (list, 0..) |path, i| {
self.meshes[i] = rend.getMesh(path);
}
}
pub fn tick(self: *@This(), dt: f64) void {
self.deltaTime = dt;
if (self.meshes[0] == null) {
self.updateMeshes();
}
}
// ========= Rendering section =========
pub fn onUpload(p: *anyopaque, copyPass: *gpu.GPUCopyPass) void {
const self: *@This() = @ptrCast(@alignCast(p));
const count = self.debugDraws.count();
{
const uploadMapped: [*]debug_vert.Scene = @ptrCast(@alignCast(self.device.mapGPUTransferBuffer(self.ssobUpload, true)));
defer self.device.unmapGPUTransferBuffer(self.ssobUpload);
var offset: usize = 0;
self.drawsThisFrame.clearRetainingCapacity();
while (offset < count) {
var primitive: DebugPrimitive = self.debugDraws.pop().?;
uploadMapped[offset] = .{
.Model = primitive.resolve(),
.Color = @bitCast(primitive.color.toZm()),
};
self.drawsThisFrame.append(self.allocator, primitive) catch unreachable;
primitive.duration -= @as(f32, @floatCast(self.deltaTime));
offset += 1;
if (primitive.duration >= 0) {
// push this primitive so that it goes to the next frame
self.debugDraws.push(primitive) catch continue;
}
}
}
if (count > 0) {
copyPass.uploadToGPUBuffer(&.{ .transfer_buffer = self.ssobUpload, .offset = 0 }, &.{
.buffer = self.ssbo,
.offset = 0,
.size = @intCast(count * @sizeOf(debug_vert.Scene)),
}, false);
}
}
pub fn postMesh(p: *anyopaque, cmd: *gpu.GPUCommandBuffer, renderpass: *gpu.GPURenderPass) void {
const self: *@This() = @ptrCast(@alignCast(p));
if (self.meshes[0] == null) {
return;
}
renderpass.bindGPUGraphicsPipeline(self.pipeline);
renderpass.bindGPUVertexStorageBuffers(0, &self.ssbo, 1);
for (self.drawsThisFrame.items, 0..) |draw, i| {
var mesh: rend.IndexedMesh = undefined;
switch (draw.primitive) {
.box => {
mesh = self.meshes[2].?;
},
.sphere => {
mesh = self.meshes[1].?;
},
.line => {
mesh = self.meshes[0].?;
},
}
renderpass.drawGPUIndexedPrimitives(mesh.index.size, 1, mesh.index.start, @intCast(mesh.vertex.start), @intCast(i));
}
_ = cmd;
}
// ========= End of Rendering section =========
pub fn createBuffers(self: *@This()) !void {
self.ssbo = self.device.createGPUBuffer(&.{
.usage = .{ .bufferusageGraphicsStorageRead = true, .bufferusageVertex = true },
.size = MaxObjectCount * @sizeOf(debug_vert.Scene),
.props = 0,
});
self.ssobUpload = rend.renderer.createGPUTransferBuffer(&.{
.usage = .transferbufferusageUpload,
.size = MaxObjectCount * @sizeOf(debug_vert.Scene),
.props = 0,
});
}
pub fn createPipeline(self: *@This()) !*gpu.GPUGraphicsPipeline {
const vertex = try rend.context().loadShader("debug.vert", debug_vert.LoadArgs);
const fragment = try rend.context().loadShader("debug.frag", debug_frag.LoadArgs);
var pci = std.mem.zeroes(gpu.GPUGraphicsPipelineCreateInfo);
pci.vertex_shader = vertex;
pci.fragment_shader = fragment;
var attributes = std.ArrayList(gpu.GPUVertexAttribute){};
defer attributes.deinit(self.allocator);
var offset: u32 = 0;
{
try addAttribute(self.allocator, &attributes, &offset, @sizeOf(f32) * 3, .vertexelementformatFloat3);
}
pci.vertex_input_state = .{
.num_vertex_buffers = 1,
.vertex_buffer_descriptions = &[_]gpu.GPUVertexBufferDescription{
.{ .slot = 0, .pitch = @sizeOf(rend.MeshVertex), .input_rate = .vertexinputrateVertex, .instance_step_rate = 0 },
},
.num_vertex_attributes = @intCast(attributes.items.len),
.vertex_attributes = @ptrCast(attributes.items.ptr),
};
pci.depth_stencil_state.compare_op = .compareopLess;
pci.depth_stencil_state.enable_depth_test = true;
pci.depth_stencil_state.enable_depth_write = true;
pci.depth_stencil_state.write_mask = 0xff;
pci.target_info.has_depth_stencil_target = true;
pci.target_info.depth_stencil_format = rend.context().depthFormat;
pci.target_info.num_color_targets = 4;
pci.target_info.color_target_descriptions = &[_]gpu.GPUColorTargetDescription{
.{
.format = rend.context().hdrTextureFormat,
.blend_state = std.mem.zeroes(gpu.GPUColorTargetBlendState),
},
.{
.format = rend.context().hdrTextureFormat,
.blend_state = std.mem.zeroes(gpu.GPUColorTargetBlendState),
},
.{
.format = rend.context().positionTargetFormat,
.blend_state = std.mem.zeroes(gpu.GPUColorTargetBlendState),
},
.{
.format = rend.context().normalTargetFormat,
.blend_state = std.mem.zeroes(gpu.GPUColorTargetBlendState),
},
};
pci.rasterizer_state.fill_mode = .fillmodeLine;
const pipe = self.device.createGPUGraphicsPipeline(&pci);
return pipe;
}
fn addAttribute(allocator: std.mem.Allocator, list: *std.ArrayList(gpu.GPUVertexAttribute), offset: *u32, size: u32, format: gpu.GPUVertexElementFormat) !void {
try list.append(allocator, .{ .location = @intCast(list.items.len), .offset = offset.*, .format = format, .buffer_slot = 0 });
offset.* = offset.* + size;
}
pub fn setup(self: *@This(), device: *gpu.GPUDevice) !void {
core.engine_logs("[DebugDrawSystem] registering to renderer...");
self.device = device;
self.renderer = rend.context();
self.pipeline = try self.createPipeline();
try self.createBuffers();
//core.installDebugDrawInterface(self.allocator, newInterface: DebugDrawInterface);
try core.installDebugDrawInterface(self.allocator, .{
.debugSphereFn = debugSphere, // : *const fn (pos: core.Vectorf, radius: f32, DebugDrawParams) void,
.debugBoxFn = debugBox, // : *const fn (pos: core.Vectorf, extents: core.Vectorf, DebugDrawParams) void,
.debugLineFn = debugLine, // : *const fn (start: core.Vectorf, end: core.Vectorf, DebugDrawParams) void,
});
}
pub fn destroy(self: *@This()) void {
self.debugDraws.deinit(self.allocator);
self.drawsThisFrame.deinit(self.allocator);
}
// ==================== Primitives ===================
pub const DebugLine = struct {
start: core.Vectorf,
end: core.Vectorf,
pub fn resolve(self: @This(), _: anytype) core.Transform {
var delta = self.start.sub(self.end);
const d = delta.normalize();
const axz = std.math.atan2(-d.z, d.x) + core.radians(180.0);
const ay = -std.math.asin(d.y);
const mat1 = core.zm.matFromRollPitchYaw(0, 0, ay);
const mat2 = core.zm.rotationY(axz);
const len = delta.length();
return core.zm.mul(core.zm.mul(
core.zm.mul(mat1, mat2),
core.zm.scaling(len, len, len),
), core.zm.translationV(self.start.toZm()));
}
};
pub const DebugSphere = struct {
position: core.Vectorf,
radius: f32,
rotation: core.Quat,
pub fn resolve(self: @This(), _: anytype) core.Transform {
return core.zm.mul(core.zm.mul(
core.zm.matFromQuat(self.rotation),
core.zm.scaling(self.radius, self.radius, self.radius),
), core.zm.translationV(self.position.toZm()));
}
};
pub const DebugBox = struct {
position: core.Vectorf,
extents: core.Vectorf,
rotation: core.Quat,
pub fn resolve(self: @This(), _: anytype) core.Transform {
return core.zm.mul(core.zm.mul(
core.zm.matFromQuat(self.rotation),
core.zm.scalingV(self.extents.toZm()),
), core.zm.translationV(self.position.toZm()));
}
};
const DebugPrimitiveType = enum(u8) {
line = 0,
sphere = 1,
box = 2,
};
pub const DebugPrimitive = struct {
primitive: union(DebugPrimitiveType) {
line: DebugLine,
sphere: DebugSphere,
box: DebugBox,
},
color: core.Vectorf = .{ .x = 0.0, .y = 1.0, .z = 0.0 },
duration: f32 = 0.0,
pub fn resolve(self: @This()) core.Transform {
// comptime core.asserts(@sizeOf(DebugPrimitiveGpu) == DebugPrimitiveGpu.TargetSize, "");
switch (self.primitive) {
.line => |inner| {
return inner.resolve(.{});
},
.sphere => |inner| {
return inner.resolve(.{});
},
.box => |inner| {
return inner.resolve(.{});
},
}
unreachable;
}
};
const DebugPrimitiveGpu = struct {
const UnpaddedSize = @sizeOf(core.Transform) + @sizeOf(core.Vectorf);
const TargetSize = 80;
model: core.Transform,
color: core.Vectorf,
pad: [TargetSize - UnpaddedSize]u8 = std.mem.zeroes([TargetSize - UnpaddedSize]u8),
};
const DebugDrawSharedInstance = struct {};
const DebugSharedData = struct {
drawsThisFrame: std.ArrayListUnmanaged(DebugPrimitive) = .{},
lock: std.Thread.Mutex = .{},
pub fn deinit(self: *@This(), allocator: std.mem.Allocator) void {
self.drawsThisFrame.deinit(allocator);
}
};
const objectCount = 2048;
// ==================== End of primitives ============
var gDebugDrawSys: *@This() = undefined;
pub fn debugSphere(position: core.Vectorf, radius: f32, params: DebugDrawParams) void {
gDebugDrawSys.debugDraws.push(.{
.primitive = .{ .sphere = .{ .position = position, .radius = radius, .rotation = params.rotation } },
.color = params.color,
.duration = params.duration,
}) catch return;
}
pub fn debugLine(start: core.Vectorf, end: core.Vectorf, params: DebugDrawParams) void {
gDebugDrawSys.debugDraws.push(.{
.primitive = .{ .line = .{
.start = start,
.end = end,
} },
.color = params.color,
.duration = params.duration,
}) catch return;
}
pub fn debugBox(position: core.Vectorf, extents: core.Vectorf, params: DebugDrawParams) void {
gDebugDrawSys.debugDraws.push(.{
.primitive = .{
.box = .{ .position = position, .extents = extents, .rotation = params.rotation },
},
.color = params.color,
.duration = params.duration,
}) catch return;
}
const DebugDrawParams = core.DebugDrawParams;
const core = @import("core");
const rend = @import("../rend.zig");
const assets = @import("assets");
const texture = rend.texture;
const sdl3 = @import("sdl3");
const gpu = sdl3.gpu;
const std = @import("std");
const debug_frag = @import("debug.frag");
const debug_vert = @import("debug.vert");
const Renderer = @import("renderer.zig").Renderer;
const primitive_box align(8) = @embedFile("embedded/primitive_box.obj").*;
const primitive_line align(8) = @embedFile("embedded/primitive_line.obj").*;
const primitive_sphere align(8) = @embedFile("embedded/primitive_sphere.obj").*;