Backlog/engine/rend/src/sgpu/mesh-pool.zig

182 lines
6.6 KiB
Zig

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,
destroyList: std.ArrayList(*gpu.GPUTransferBuffer),
installedMeshes: std.AutoHashMapUnmanaged(u32, meshes.IndexedMesh) = .{},
pub fn create(device: *gpu.GPUDevice, allocator: std.mem.Allocator, settings: MeshPoolCreationSettings) !*@This() {
const self = try allocator.create(@This());
gMeshPool = self;
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),
.destroyList = std.ArrayList(*gpu.GPUTransferBuffer).init(allocator),
.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,
});
core.graphics_log("mesh pool created {d}k vertices, {d}k indices", .{ settings.vertexCount / 1000, settings.indexCount / 1000 });
try assets.gAssetSys.registerLoader(try core.createObject(MeshAssetLoader, .{}));
return self;
}
pub fn onUploadCleanup(p: *anyopaque) void {
const self: *@This() = @ptrCast(@alignCast(p));
for (self.destroyList.items) |transferBuffer| {
self.device.releaseGPUTransferBuffer(transferBuffer);
}
self.destroyList.clearRetainingCapacity();
}
pub fn onUploadInner(self: *@This(), copyPass: *gpu.GPUCopyPass) !void {
self.meshUpdates.lock();
defer self.meshUpdates.unlock();
self.destroyList = std.ArrayList(*gpu.GPUTransferBuffer).init(self.allocator);
while (self.meshUpdates.popFromUnlocked()) |u| {
switch (u) {
.new => |new| {
const indexSpan = try self.addTransfer(copyPass, self.indexBuffer, u32, &self.indexSpans, new.indices);
const vertexSpan = try self.addTransfer(copyPass, self.vertexBuffer, meshes.MeshVertex, &self.vertexSpans, new.vertices);
var name = new.name;
core.graphics_log("uploading {d} vertices and {d} indices", .{ vertexSpan.size, indexSpan.size });
try self.installedMeshes.put(self.allocator, name.handle(), .{
.vertex = vertexSpan,
.index = indexSpan,
.name = new.name,
.jointRemap = null, // joint remaps... parse the installed skeletal meshes to generate this remap
});
},
.free => |free| {
var name = free.name;
if (!self.installedMeshes.remove(name.handle())) {
core.engine_log("unable to find handle from mesh pool.. {s}", .{name.utf8()});
}
self.vertexSpans.removeSpan(free.vertices);
self.indexSpans.removeSpan(free.indices);
},
}
u.deinit(self.allocator);
}
}
pub fn onUpload(p: *anyopaque, copyPass: *gpu.GPUCopyPass) void {
const self: *@This() = @ptrCast(@alignCast(p));
if (self.meshUpdates.count() <= 0)
return;
self.onUploadInner(copyPass) catch unreachable;
}
pub fn addTransfer(
self: *@This(),
copyPass: *gpu.GPUCopyPass,
destBuffer: *gpu.GPUBuffer,
comptime T: type,
mergedSpans: *core.MergedSpans,
uploadSlice: []const T,
) !core.Span {
const newSpan = try mergedSpans.allocate(@intCast(uploadSlice.len));
const upload = self.device.createGPUTransferBuffer(&.{ .usage = .transferbufferusageUpload, .size = @sizeOf(T) * newSpan.size, .props = 0 });
try self.destroyList.append(upload);
var mappedSlice: []T = undefined;
mappedSlice.ptr = @ptrCast(@alignCast(self.device.mapGPUTransferBuffer(upload, false)));
defer self.device.unmapGPUTransferBuffer(upload);
mappedSlice.len = uploadSlice.len;
std.mem.copyForwards(T, mappedSlice, uploadSlice);
//copyPass.uploadToGPUBuffer(source: [*c]const GPUTransferBufferLocation, destination: [*c]const GPUBufferRegion, cycle: bool)
copyPass.uploadToGPUBuffer(
&.{ .transfer_buffer = upload, .offset = 0 },
&.{ .buffer = destBuffer, .offset = newSpan.start, .size = newSpan.size },
false,
);
return newSpan;
}
// pushes the meshUpdate into the pool, either an insert or a free.
pub fn pushMeshUpdate(self: *@This(), meshUpdate: rend.MeshUpdate) !void {
try self.meshUpdates.pushLocked(meshUpdate);
}
pub fn destroy(p: *anyopaque) void {
const self: *@This() = @ptrCast(@alignCast(p));
self.device.releaseGPUBuffer(self.indexBuffer);
self.device.releaseGPUBuffer(self.vertexBuffer);
self.installedMeshes.deinit(self.allocator);
self.destroyList.deinit();
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,
};
};
/// === renderer interface implementation ===
var gMeshPool: *MeshPool = undefined;
pub fn getMesh(name: []const u8) ?rend.IndexedMesh {
var n = core.MakeName(name);
return getMeshByName(&n);
}
pub fn getMeshByName(name: *core.Name) ?rend.IndexedMesh {
return gMeshPool.installedMeshes.get(name.handle());
}
pub fn pushMeshUpdate(meshUpdate: rend.MeshUpdate) !void {
try gMeshPool.pushMeshUpdate(meshUpdate);
}
const MeshAssetLoader = @import("MeshAssetLoader.zig");
const sdl3 = @import("sdl3");
const gpu = sdl3.gpu;
const rend = @import("../rend.zig");
const std = @import("std");
const core = @import("core");
const assets = @import("assets");
const meshes = rend.meshes;