commit eef47ff65e16f23f208037a3d3e77e0b550b93ee
parent 52a2e2dbe5582a34abbde0eeacd445f0054c2f38
Author: Christian Ermann <christianermann@gmail.com>
Date: Tue, 30 Apr 2024 18:37:18 -0400
Prototype mesh buffers
Diffstat:
M | src/load_obj.zig | | | 35 | ++++++++++++++++++++++++++++------- |
M | src/main.zig | | | 180 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------- |
2 files changed, 172 insertions(+), 43 deletions(-)
diff --git a/src/load_obj.zig b/src/load_obj.zig
@@ -1,6 +1,8 @@
const std = @import("std");
+const gpu = @import("mach_gpu");
const Mesh = @import("main.zig").Mesh;
+const ArenaBuffer = @import("main.zig").ArenaBuffer;
const f32x2 = @Vector(2, f32);
const f32x3 = @Vector(3, f32);
@@ -97,7 +99,18 @@ fn parseVertex(line: []const u8) !Vertex {
return vertex;
}
-pub fn loadFile(allocator: std.mem.Allocator, path: []const u8) !Mesh {
+pub const LoadFileOptions = struct {
+ allocator: std.mem.Allocator,
+ vtx_buffer: *ArenaBuffer,
+ idx_buffer: *ArenaBuffer,
+ path: []const u8,
+ queue: *gpu.Queue,
+};
+
+pub fn loadFile(options: LoadFileOptions) !Mesh {
+ const allocator = options.allocator;
+ const path = options.path;
+
var file = try std.fs.cwd().openFile(path, .{ .mode = .read_only });
defer file.close();
@@ -172,10 +185,18 @@ pub fn loadFile(allocator: std.mem.Allocator, path: []const u8) !Mesh {
std.log.info("NUM NORMALS: {}", .{normals_indexed.items.len});
std.log.info("NUM TEXCOORDS: {}", .{tex_coords_indexed.items.len});
- return Mesh{
- .indices = try indices.toOwnedSlice(),
- .positions = try positions_indexed.toOwnedSlice(),
- .normals = try normals_indexed.toOwnedSlice(),
- .tex_coords = try tex_coords_indexed.toOwnedSlice(),
- };
+ const pslice = try positions_indexed.toOwnedSlice();
+ const nslice = try normals_indexed.toOwnedSlice();
+ const tslice = try tex_coords_indexed.toOwnedSlice();
+ const islice = try indices.toOwnedSlice();
+
+ return Mesh.init(.{
+ .queue = options.queue,
+ .vtx_buffer = options.vtx_buffer,
+ .idx_buffer = options.idx_buffer,
+ .positions = pslice,
+ .normals = nslice,
+ .tex_coords = tslice,
+ .indices = islice,
+ });
}
diff --git a/src/main.zig b/src/main.zig
@@ -332,45 +332,139 @@ const DefaultRenderPipeline = struct {
const f32x3 = @Vector(3, f32);
const f32x4 = @Vector(4, f32);
-pub const Mesh = struct {
- indices: []u32,
- positions: []f32x4,
- normals: ?[]f32x3,
- tex_coords: ?[]f32x3,
-};
-
-const UnlitRenderPipeline = struct {
- gpu_pipeline: *gpu.RenderPipeline,
- vtx_buffer: *gpu.Buffer,
- idx_buffer: *gpu.Buffer,
- mesh: *const Mesh,
-
- pub fn init(app: *App, mesh: *const Mesh) UnlitRenderPipeline {
- const vtx_buffer_descriptor = gpu.Buffer.Descriptor{
- .size = mesh.positions.len * @sizeOf(f32x4),
+pub const ArenaBuffer = struct {
+ buffer: *gpu.Buffer,
+ size: u32,
+ offset: u32 = 0,
+
+ pub fn init_vtx(size: u32, device: *gpu.Device) ArenaBuffer {
+ const descriptor = gpu.Buffer.Descriptor{
+ .size = size,
.usage = .{ .vertex = true, .copy_dst = true },
.mapped_at_creation = .false,
};
- const vtx_buffer = app.device.createBuffer(&vtx_buffer_descriptor);
- app.queue.writeBuffer(vtx_buffer, 0, mesh.positions);
+ const buffer = device.createBuffer(&descriptor);
+ return .{
+ .buffer = buffer,
+ .size = size,
+ };
+ }
- const vtx_attributes = [_]gpu.VertexAttribute{
- .{ .format = .float32x4, .shader_location = 0, .offset = 0 },
+ pub fn init_idx(size: u32, device: *gpu.Device) ArenaBuffer {
+ const descriptor = gpu.Buffer.Descriptor{
+ .size = size,
+ .usage = .{ .index = true, .copy_dst = true },
+ .mapped_at_creation = .false,
+ };
+ const buffer = device.createBuffer(&descriptor);
+ return .{
+ .buffer = buffer,
+ .size = size,
};
- const vtx_buffer_layout = gpu.VertexBufferLayout.init(.{
+ }
+
+ pub fn alloc(self: *ArenaBuffer, size: u32) !u32 {
+ const offset_start = self.offset;
+ const offset_after = self.offset + size;
+ if (offset_after > self.size) {
+ std.log.err("arena buffer out of memory", .{});
+ return error.ArenaBufferEmpty;
+ }
+ std.log.info("arena buffer alloc: {}", .{size});
+ self.offset = offset_after;
+ return offset_start;
+ }
+};
+
+pub const MeshInitOptions = struct {
+ queue: *gpu.Queue,
+ vtx_buffer: *ArenaBuffer,
+ idx_buffer: *ArenaBuffer,
+ positions: []f32x4,
+ normals: []f32x3,
+ tex_coords: []f32x3,
+ indices: []u32,
+};
+
+pub const Mesh = struct {
+ vtx_buffer: *ArenaBuffer,
+ idx_buffer: *ArenaBuffer,
+
+ num_vertices: usize,
+ offset_positions: usize,
+ offset_normals: ?usize,
+ offset_tex_coords: ?usize,
+
+ num_indices: usize,
+ offset_indices: usize,
+
+ pub fn vertex_buffer_layouts() [3]gpu.VertexBufferLayout {
+ const p_buffer_layout = gpu.VertexBufferLayout.init(.{
.array_stride = @sizeOf(f32x4),
.step_mode = .vertex,
- .attributes = &vtx_attributes,
+ .attributes = &.{
+ .{ .format = .float32x4, .shader_location = 0, .offset = 0 },
+ },
});
+ const n_buffer_layout = gpu.VertexBufferLayout.init(.{
+ .array_stride = @sizeOf(f32x4),
+ .step_mode = .vertex,
+ .attributes = &.{
+ .{ .format = .float32x3, .shader_location = 1, .offset = 0 },
+ },
+ });
+ const t_buffer_layout = gpu.VertexBufferLayout.init(.{
+ .array_stride = @sizeOf(f32x4),
+ .step_mode = .vertex,
+ .attributes = &.{
+ .{ .format = .float32x3, .shader_location = 2, .offset = 0 },
+ },
+ });
+ return .{ p_buffer_layout, n_buffer_layout, t_buffer_layout };
+ }
- const idx_buffer_descriptor = gpu.Buffer.Descriptor{
- .size = mesh.indices.len * @sizeOf(u32),
- .usage = .{ .index = true, .copy_dst = true },
- .mapped_at_creation = .false,
+ pub fn init(options: MeshInitOptions) !Mesh {
+ const num_vertices = options.positions.len;
+ const num_indices = options.indices.len;
+ const vtx_buffer = options.vtx_buffer;
+ const idx_buffer = options.idx_buffer;
+
+ const size_p = num_vertices * @sizeOf(f32x4);
+ const size_n = num_vertices * @sizeOf(f32x3);
+ const size_t = num_vertices * @sizeOf(f32x3);
+ const size_vtx = size_p + size_n + size_t;
+
+ const offset_p = try vtx_buffer.alloc(@intCast(size_vtx));
+ const offset_n = offset_p + size_p;
+ const offset_t = offset_n + size_n;
+
+ const size_i = num_indices * @sizeOf(u32);
+ const offset_i = try idx_buffer.alloc(@intCast(size_i));
+
+ const queue = options.queue;
+ queue.writeBuffer(vtx_buffer.buffer, offset_p, options.positions);
+ queue.writeBuffer(vtx_buffer.buffer, offset_n, options.normals);
+ queue.writeBuffer(vtx_buffer.buffer, offset_t, options.tex_coords);
+ queue.writeBuffer(idx_buffer.buffer, offset_i, options.indices);
+
+ return .{
+ .vtx_buffer = vtx_buffer,
+ .idx_buffer = idx_buffer,
+ .num_vertices = num_vertices,
+ .offset_positions = offset_p,
+ .offset_normals = offset_n,
+ .offset_tex_coords = offset_t,
+ .num_indices = num_indices,
+ .offset_indices = offset_i,
};
- const idx_buffer = app.device.createBuffer(&idx_buffer_descriptor);
- app.queue.writeBuffer(idx_buffer, 0, mesh.indices);
+ }
+};
+
+const UnlitRenderPipeline = struct {
+ gpu_pipeline: *gpu.RenderPipeline,
+ mesh: *const Mesh,
+ pub fn init(app: *App, mesh: *const Mesh) UnlitRenderPipeline {
const vs =
\\ struct VertexInput {
\\ @location(0) position: vec4<f32>,
@@ -383,10 +477,11 @@ const UnlitRenderPipeline = struct {
const vs_module = app.device.createShaderModuleWGSL("default vertex shader", vs);
defer vs_module.release();
+ const buffer_layouts = Mesh.vertex_buffer_layouts();
const vertex = gpu.VertexState.init(.{
.module = vs_module,
.entry_point = "main",
- .buffers = &.{vtx_buffer_layout},
+ .buffers = &buffer_layouts,
});
const fs =
@@ -425,8 +520,6 @@ const UnlitRenderPipeline = struct {
return .{
.gpu_pipeline = app.device.createRenderPipeline(&pipeline_descriptor),
- .vtx_buffer = vtx_buffer,
- .idx_buffer = idx_buffer,
.mesh = mesh,
};
}
@@ -435,9 +528,15 @@ const UnlitRenderPipeline = struct {
const self: *UnlitRenderPipeline = @ptrCast(@alignCast(ptr));
pass.setPipeline(self.gpu_pipeline);
- pass.setVertexBuffer(0, self.vtx_buffer, 0, @sizeOf(f32x4) * self.mesh.positions.len);
- pass.setIndexBuffer(self.idx_buffer, .uint32, 0, @sizeOf(u32) * self.mesh.indices.len);
- pass.drawIndexed(@intCast(self.mesh.indices.len), 1, 0, 0, 0);
+ pass.setVertexBuffer(0, self.mesh.vtx_buffer.buffer, self.mesh.offset_positions, @sizeOf(f32x4) * self.mesh.num_vertices);
+ pass.setVertexBuffer(1, self.mesh.vtx_buffer.buffer, self.mesh.offset_normals.?, @sizeOf(f32x3) * self.mesh.num_vertices);
+ pass.setVertexBuffer(2, self.mesh.vtx_buffer.buffer, self.mesh.offset_normals.?, @sizeOf(f32x3) * self.mesh.num_vertices);
+ pass.setIndexBuffer(self.mesh.idx_buffer.buffer, .uint32, self.mesh.offset_indices, @sizeOf(u32) * self.mesh.num_indices);
+ pass.drawIndexed(@intCast(self.mesh.num_indices), 1, 0, 0, 0);
+
+ //pass.setVertexBuffer(0, self.vtx_buffer, 0, @sizeOf(f32x4) * self.mesh.positions.len);
+ //pass.setIndexBuffer(self.idx_buffer, .uint32, 0, @sizeOf(u32) * self.mesh.indices.len);
+ //pass.drawIndexed(@intCast(self.mesh.indices.len), 1, 0, 0, 0);
}
pub fn pipeline(self: *UnlitRenderPipeline) RenderPipeline {
@@ -452,12 +551,21 @@ pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
- const mesh = try load_obj.loadFile(allocator, "bunny-fixed.obj");
-
var app: App = undefined;
try app.init(allocator);
defer app.deinit();
+ var vtx_buffer = ArenaBuffer.init_vtx(1000000, app.device);
+ var idx_buffer = ArenaBuffer.init_idx(500000, app.device);
+
+ const mesh = try load_obj.loadFile(.{
+ .queue = app.queue,
+ .allocator = allocator,
+ .vtx_buffer = &vtx_buffer,
+ .idx_buffer = &idx_buffer,
+ .path = "bunny-fixed.obj",
+ });
+
var drp = UnlitRenderPipeline.init(&app, &mesh);
const pipelines = [_]RenderPipeline{drp.pipeline()};