From b95ef8149235ad19e70cf08a0394e5a367351d7a Mon Sep 17 00:00:00 2001 From: Peter Li Date: Wed, 16 Apr 2025 08:17:22 -0700 Subject: [PATCH] renderer interface --- engine/rend/src/meshes/gltfLoader.zig | 57 ++--------------- engine/rend/src/meshes/meshPool.zig | 0 engine/rend/src/meshes/meshes.zig | 59 ++++++++++++++++++ engine/rend/src/rend.zig | 20 ++++-- engine/rend/src/sgpu/mesh-pool.zig | 89 +++++++++++++++++++++++++++ engine/rend/src/sgpu/renderer.zig | 74 +++++++++++++++------- 6 files changed, 220 insertions(+), 79 deletions(-) delete mode 100644 engine/rend/src/meshes/meshPool.zig create mode 100644 engine/rend/src/meshes/meshes.zig create mode 100644 engine/rend/src/sgpu/mesh-pool.zig diff --git a/engine/rend/src/meshes/gltfLoader.zig b/engine/rend/src/meshes/gltfLoader.zig index b41b5a0..3b6ff91 100644 --- a/engine/rend/src/meshes/gltfLoader.zig +++ b/engine/rend/src/meshes/gltfLoader.zig @@ -274,59 +274,10 @@ pub fn loadIndexedMeshForPoolingGltf(allocator: std.mem.Allocator, meshName: cor return rv; } -pub const MeshPoolCreationSettings = struct { - vertexCount: u32 = 4_000_000, - indexCount: u32 = 16_000_000, -}; - -pub const MeshUpdate = union(enum(u8)) { - new: struct { - vertices: []MeshVertex, - indices: []u32, - jointNames: []JointNameEntry, - name: core.Name, - skeletonName: ?core.Name, - }, - - free: struct { - vertices: core.Span, - indices: core.Span, - name: core.Name, - }, - - pub fn deinit(self: @This(), allocator: std.mem.Allocator) void { - switch (self) { - .new => |new| { - allocator.free(new.vertices); - allocator.free(new.indices); - for (new.jointNames) |entry| { - entry.deinit(allocator); - } - allocator.free(new.jointNames); - }, - .free => {}, - } - } -}; - -pub const JointNameEntry = struct { - name: []u8 = undefined, - index: u32 = 0, - - pub fn deinit(self: @This(), allocator: std.mem.Allocator) void { - // const allocator = graphics.getContext().allocator; - allocator.free(self.name); - } -}; - -pub const MeshVertex = extern struct { - position: core.Vectorf = .{}, - normal: core.Vectorf = .{}, - color: core.colors.Color = .{}, - uv: core.Vector2f = .{}, - bones: [4]u8 = .{ 0, 0, 0, 0 }, - weights: [4]u8 = .{ 0, 0, 0, 0 }, -}; +const meshes = @import("meshes.zig"); +const MeshUpdate = meshes.MeshUpdate; +const JointNameEntry = meshes.JointNameEntry; +const MeshVertex = meshes.MeshVertex; const core = @import("core"); const std = @import("std"); diff --git a/engine/rend/src/meshes/meshPool.zig b/engine/rend/src/meshes/meshPool.zig deleted file mode 100644 index e69de29..0000000 diff --git a/engine/rend/src/meshes/meshes.zig b/engine/rend/src/meshes/meshes.zig new file mode 100644 index 0000000..9f2d412 --- /dev/null +++ b/engine/rend/src/meshes/meshes.zig @@ -0,0 +1,59 @@ +pub const MeshVertex = extern struct { + position: core.Vectorf = .{}, + normal: core.Vectorf = .{}, + color: core.colors.Color = .{}, + uv: core.Vector2f = .{}, + bones: [4]u8 = .{ 0, 0, 0, 0 }, + weights: [4]u8 = .{ 0, 0, 0, 0 }, +}; + +pub const MeshUpdate = union(enum(u8)) { + new: struct { + vertices: []MeshVertex, + indices: []u32, + jointNames: []JointNameEntry, + name: core.Name, + skeletonName: ?core.Name, + }, + + free: struct { + vertices: core.Span, + indices: core.Span, + name: core.Name, + }, + + pub fn deinit(self: @This(), allocator: std.mem.Allocator) void { + switch (self) { + .new => |new| { + allocator.free(new.vertices); + allocator.free(new.indices); + for (new.jointNames) |entry| { + entry.deinit(allocator); + } + allocator.free(new.jointNames); + }, + .free => {}, + } + } +}; + +pub const JointNameEntry = struct { + name: []u8 = undefined, + index: u32 = 0, + + pub fn deinit(self: @This(), allocator: std.mem.Allocator) void { + // const allocator = graphics.getContext().allocator; + allocator.free(self.name); + } +}; + +pub const IndexedMesh = struct { + vertex: core.Span, + index: core.Span, + name: core.Name, + jointRemap: ?[]u8, // this is NOT a string, they're joint indices.. which happen to be u8s +}; + +const core = @import("core"); +const std = @import("std"); +const zgltf = @import("zgltf"); diff --git a/engine/rend/src/rend.zig b/engine/rend/src/rend.zig index ad6d6a0..25a65b6 100644 --- a/engine/rend/src/rend.zig +++ b/engine/rend/src/rend.zig @@ -1,12 +1,24 @@ const std = @import("std"); pub const core = @import("core"); -const sgpu_renderer = @import("sgpu/renderer.zig"); +// meshes api +pub const meshes = @import("meshes/meshes.zig"); +pub const JointNameEntry = meshes.JointNameEntry; +pub const MeshUpdate = meshes.MeshUpdate; +pub const MeshVertex = meshes.MeshVertex; +pub const IndexedMesh = meshes.IndexedMesh; + +// asset loaders pub const gltfLoader = @import("meshes/gltfLoader.zig"); +// switch out based on backend implementation +pub const renderer = @import("sgpu/renderer.zig"); +pub const context = renderer.context; // context getter func + +pub const MeshPool = renderer.MeshPool; + // controls glfw and general windowing // graphics depends on this one - pub const Module: core.ModuleDescription = .{ .name = "rend", .enabledByDefault = true, @@ -16,10 +28,10 @@ pub fn start_module(comptime programSpec: anytype, args: anytype, allocator: std _ = args; _ = programSpec; _ = allocator; - try sgpu_renderer.start(); + try renderer.start(); } pub fn shutdown_module(allocator: std.mem.Allocator) void { _ = allocator; - sgpu_renderer.shutdown(); + renderer.shutdown(); } diff --git a/engine/rend/src/sgpu/mesh-pool.zig b/engine/rend/src/sgpu/mesh-pool.zig new file mode 100644 index 0000000..b8dd5ed --- /dev/null +++ b/engine/rend/src/sgpu/mesh-pool.zig @@ -0,0 +1,89 @@ +pub const MeshPool = struct { + allocator: std.mem.Allocator, + + meshMap: std.AutoHashMapUnmanaged(u32, rend.IndexedMesh) = .{}, + indexSpans: core.MergedSpans, + vertexSpans: core.MergedSpans, + + indexBuffer: *gpu.GPUBuffer = undefined, + vertexBuffer: *gpu.GPUBuffer = undefined, + + meshUpdates: core.RingQueue(rend.MeshUpdate), + + device: *gpu.GPUDevice, + + pub fn create(device: *gpu.GPUDevice, allocator: std.mem.Allocator, settings: MeshPoolCreationSettings) !*@This() { + const self = try allocator.create(@This()); + + self.* = .{ + .allocator = allocator, + .indexSpans = try core.MergedSpans.init(allocator, settings.indexCount), + .vertexSpans = try core.MergedSpans.init(allocator, settings.vertexCount), + .meshUpdates = try core.RingQueue(rend.MeshUpdate).init(allocator, 128), + .device = device, + }; + + self.vertexBuffer = self.device.createGPUBuffer(&.{ + .usage = .{ .bufferusageVertex = true }, + .size = settings.vertexCount * @sizeOf(rend.MeshVertex), + .props = 0, + }); + + self.indexBuffer = self.device.createGPUBuffer(&.{ + .usage = .{ .bufferusageIndex = true }, + .size = settings.indexCount * @sizeOf(u32), + .props = 0, + }); + + return self; + } + + pub fn onUpload(p: *anyopaque, copyPass: *gpu.GPUCopyPass) void { + const self: *@This() = @ptrCast(@alignCast(p)); + if (self.meshUpdates.count() <= 0) + return; + _ = copyPass; + } + + // pushes the meshUpdate into the pool, either an insert or a free. + pub fn pushMeshUpdate(self: *@This(), meshUpdate: rend.MeshUpdate) !void { + self.meshUpdates.pushLocked(meshUpdate); + } + + pub fn destroy(p: *anyopaque) void { + const self: *@This() = @ptrCast(@alignCast(p)); + + self.indexSpans.deinit(); + self.meshUpdates.deinit(); + self.vertexSpans.deinit(); + self.allocator.destroy(self); + } + + pub const MeshPoolCreationSettings = struct { + vertexCount: u32 = 4_000_000, + indexCount: u32 = 16_000_000, + }; +}; + +var gMeshPool: *MeshPool = undefined; + +pub fn getIndexedMesh(name: []const u8) ?rend.IndexedMesh { + var n = core.MakeName(name); + return getIndexedMeshByName(&n); +} + +pub fn getIndexedMeshByName(name: *core.Name) ?rend.IndexedMesh { + // var name = _name; + // gMeshPoolBuffer.vertexMapLock.lock(); + // defer gMeshPoolBuffer.vertexMapLock.unlock(); + // return gMeshPoolBuffer.vertexMap.get(name.handle()); + _ = name; + return null; +} + +const sdl3 = @import("sdl3"); +const gpu = sdl3.gpu; +const rend = @import("../rend.zig"); +const std = @import("std"); +const core = @import("core"); +const meshes = rend.meshes; diff --git a/engine/rend/src/sgpu/renderer.zig b/engine/rend/src/sgpu/renderer.zig index 2489089..b47cbfc 100644 --- a/engine/rend/src/sgpu/renderer.zig +++ b/engine/rend/src/sgpu/renderer.zig @@ -21,6 +21,11 @@ pub const Renderer = struct { colorBufferTransfer: *gpu.GPUTransferBuffer = undefined, window: *sdl3.Window = undefined, + meshPool: *MeshPool = undefined, + + uploads: std.ArrayListUnmanaged(struct { ptr: *anyopaque, func: *const fn (*anyopaque, *gpu.GPUCopyPass) void }) = .{}, + destroys: std.ArrayListUnmanaged(struct { ptr: *anyopaque, func: *const fn (*anyopaque) void }) = .{}, + pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This()); pub fn init(allocator: std.mem.Allocator) !*@This() { @@ -38,6 +43,13 @@ pub const Renderer = struct { @as(*@This(), @ptrCast(@alignCast(p))).startRenderer() catch return error.BadInit; } + pub fn registerRendererObject(self: *@This(), T: type) !void { + const object = try T.create(self.device, self.allocator, .{}); + + try self.uploads.append(self.allocator, .{ .ptr = object, .func = T.onUpload }); + try self.destroys.append(self.allocator, .{ .ptr = object, .func = T.destroy }); + } + pub fn startRenderer(self: *@This()) !void { self.device = gpu.createGPUDevice(.{ .shaderformatSpirv = true, @@ -55,6 +67,8 @@ pub const Renderer = struct { core.engine_log("sgpu device created, using shader format: {s}", .{self.shaderSuffix}); try self.createPipeline(); + + try self.registerRendererObject(MeshPool); } pub fn discoverFormats(self: *@This()) !void { @@ -151,33 +165,37 @@ pub const Renderer = struct { return rv; } + pub fn frameUploads(self: *@This(), cmd: *gpu.GPUCommandBuffer) void { + const buffer = self.device.mapGPUTransferBuffer(self.colorBufferTransfer, true); + + var b: [*][4]f32 = @ptrCast(@alignCast(buffer)); + + b[0] = .{ @floatCast(std.math.sin(self.totalTime * 3 * 2 + 0.8) * 0.2 + 0.8), 0.4, 0.4, 1.0 }; + b[1] = .{ 0.4, @floatCast(std.math.sin(self.totalTime * 2 * 2 + 0.3) * 0.2 + 0.8), 0.4, 1.0 }; + b[2] = .{ 0.4, 0.4, @floatCast(std.math.sin(self.totalTime * 4 * 2) * 0.2 + 0.8), 1.0 }; + + self.device.unmapGPUTransferBuffer(self.colorBufferTransfer); + + const copyPass = cmd.beginGPUCopyPass(); + defer copyPass.endGPUCopyPass(); + copyPass.uploadToGPUBuffer(&.{ .transfer_buffer = self.colorBufferTransfer, .offset = 0 }, &.{ + .buffer = self.colorBuffer, + .offset = 0, + .size = 4 * 12, + }, true); + + for (self.uploads.items) |upload| { + upload.func(upload.ptr, copyPass); + } + } + pub fn tick(self: *@This(), dt: f64) void { self.totalTime += dt; const cmd = self.device.acquireGPUCommandBuffer(); + self.frameUploads(cmd); + var swapchain_texture: *gpu.GPUTexture = undefined; - { - const buffer = self.device.mapGPUTransferBuffer(self.colorBufferTransfer, true); - - var b: [*][4]f32 = @ptrCast(@alignCast(buffer)); - - b[0] = .{ @floatCast(std.math.sin(self.totalTime * 3 * 2 + 0.8) * 0.2 + 0.8), 0.4, 0.4, 1.0 }; - b[1] = .{ 0.4, @floatCast(std.math.sin(self.totalTime * 2 * 2 + 0.3) * 0.2 + 0.8), 0.4, 1.0 }; - b[2] = .{ 0.4, 0.4, @floatCast(std.math.sin(self.totalTime * 4 * 2) * 0.2 + 0.8), 1.0 }; - - self.device.unmapGPUTransferBuffer(self.colorBufferTransfer); - } - - { - const copyPass = cmd.beginGPUCopyPass(); - defer copyPass.endGPUCopyPass(); - copyPass.uploadToGPUBuffer(&.{ .transfer_buffer = self.colorBufferTransfer, .offset = 0 }, &.{ - .buffer = self.colorBuffer, - .offset = 0, - .size = 4 * 12, - }, true); - } - if (cmd.waitAndAcquireGPUSwapchainTexture(self.window, &swapchain_texture, null, null)) { var targetInfo: gpu.GPUColorTargetInfo = std.mem.zeroes(gpu.GPUColorTargetInfo); targetInfo.texture = swapchain_texture; @@ -197,6 +215,11 @@ pub const Renderer = struct { } pub fn deinit(self: *@This()) void { + for (self.destroys.items) |d| { + d.func(d.ptr); + } + self.uploads.deinit(self.allocator); + self.destroys.deinit(self.allocator); self.allocator.destroy(self); } }; @@ -211,8 +234,15 @@ pub fn start() !void { pub fn shutdown() void {} +pub fn context() *Renderer { + return gRenderer; +} + const rend = @import("../rend.zig"); +pub const mesh_pool = @import("mesh-pool.zig"); +pub const MeshPool = mesh_pool.MeshPool; + const std = @import("std"); const core = @import("core"); const platform = @import("platform");