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

141 lines
4.3 KiB
Zig

allocator: std.mem.Allocator,
ssboScene: *gpu.GPUBuffer = undefined,
ssboSceneUpload: *gpu.GPUTransferBuffer = undefined,
spans: std.ArrayListUnmanaged(core.Span) = .{},
renderInfo: std.ArrayListUnmanaged(SgpuParticleRenderInfo) = .{},
lastParticleCount: u32 = 0,
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "rend.ParticleRenderer");
const SgpuParticleRenderInfo = struct {
mesh: rend.IndexedMesh,
texture: *rend.Texture,
};
pub fn init(self: *@This(), allocator: std.mem.Allocator, first: bool) !void {
if (!first)
return;
self.* = .{
.allocator = allocator,
};
const device = rend.context().device;
const MaxParticleCount = core.configVar(u32, "rend.particles.maxCount", 200_000);
self.ssboScene = device.createGPUBuffer(&.{
.usage = .{
.bufferusageGraphicsStorageRead = true,
.bufferusageVertex = true,
},
.size = MaxParticleCount * @sizeOf(meshes_vert.Scene),
.props = 0,
});
self.ssboSceneUpload = rend.renderer.createGPUTransferBuffer(&.{
.usage = .transferbufferusageUpload,
.size = MaxParticleCount * @sizeOf(meshes_vert.Scene),
.props = 0,
});
}
pub fn uploadSsbos(self: *@This(), copyPass: *gpu.GPUCopyPass) !void {
self.spans.clearRetainingCapacity();
self.renderInfo.clearRetainingCapacity();
var offset: usize = 0;
const device = rend.context().device;
const uploadMapped: [*]meshes_vert.Scene = @ptrCast(@alignCast(device.mapGPUTransferBuffer(self.ssboSceneUpload, true)));
defer device.unmapGPUTransferBuffer(self.ssboSceneUpload);
for (rend.ParticleEmitter.BaseContainer.dense.items) |*c| {
const emitter = &c.value;
for (emitter.finals.items, 0..) |final, i| {
const size = emitter.size.items[i];
uploadMapped[i + offset] = .{
.Model = final,
.textureMode = if (emitter.billboard) 0x2 else 0x0, //0x2, // default 0x2 is fullbright
.animation = -1,
.float0 = size,
.flags = if (emitter.billboard) 0x4 else 0, //0x4 for billboard mode
};
}
try self.renderInfo.append(self.allocator, .{ .texture = emitter.texture, .mesh = emitter.mesh });
try self.spans.append(self.allocator, .{ .start = @intCast(offset), .size = @intCast(emitter.finals.items.len) });
offset += emitter.finals.items.len;
}
self.lastParticleCount = @intCast(offset);
if (self.renderInfo.items.len == 0)
return;
copyPass.uploadToGPUBuffer(&.{ .transfer_buffer = self.ssboSceneUpload, .offset = 0 }, &.{
.buffer = self.ssboScene,
.offset = 0,
.size = @intCast(offset * @sizeOf(meshes_vert.Scene)),
}, true);
}
pub fn postMesh(p: *anyopaque, cmd: *gpu.GPUCommandBuffer, renderpass: *gpu.GPURenderPass) void {
const self: *@This() = @ptrCast(@alignCast(p));
_ = cmd;
renderpass.bindGPUVertexStorageBuffers(0, &self.ssboScene, 1);
renderpass.bindGPUFragmentStorageBuffers(0, &self.ssboScene, 1);
const ctx = rend.context();
for (self.spans.items, 0..) |span, i| {
const ri = self.renderInfo.items[i];
const mesh = ri.mesh;
const _t = ri.texture;
var sampler: *gpu.GPUSampler = undefined;
switch (_t.samplerMode) {
.blocky => {
sampler = ctx.blockySampler;
},
.linear => {
sampler = ctx.linearSampler;
},
}
renderpass.bindGPUVertexStorageBuffers(0, &self.ssboScene, 0);
renderpass.bindGPUFragmentSamplers(0, &.{ .texture = _t.texture, .sampler = sampler }, 1);
renderpass.drawGPUIndexedPrimitives(mesh.index.size, span.size, mesh.index.start, @intCast(mesh.vertex.start), @intCast(span.start));
}
}
pub fn setup(self: *@This(), device: *gpu.GPUDevice) !void {
_ = self;
_ = device;
}
pub fn deinit(self: *@This()) void {
self.spans.deinit(self.allocator);
self.renderInfo.deinit(self.allocator);
}
const assets = @import("assets");
const core = @import("core");
const std = @import("std");
const rend = @import("../rend.zig");
const renderer = rend.renderer;
const particles = rend.particles;
const sdl3 = @import("sdl3");
const gpu = sdl3.gpu;
const meshes_vert = @import("meshes.vert");