renderer interface

This commit is contained in:
Peter Li 2025-04-16 08:17:22 -07:00
parent 8cdac4e2e7
commit b95ef81492
6 changed files with 220 additions and 79 deletions

View File

@ -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");

View File

@ -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");

View File

@ -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();
}

View File

@ -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;

View File

@ -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");