diff --git a/src/main.zig b/src/main.zig index 8c91fc7..2895d65 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,4 +1,6 @@ const std = @import("std"); +const GeneralPurposeAllocator = std.heap.GeneralPurposeAllocator(.{}); + const core = @import("mach-core"); const gpu = core.gpu; @@ -51,18 +53,31 @@ const PrimitiveData = struct { pub const App = @This(); +gpa: GeneralPurposeAllocator, +allocator: std.mem.Allocator, + app_timer: core.Timer, title_timer: core.Timer, pipeline: *gpu.RenderPipeline, scene_uniform_buffer: *gpu.Buffer, scene_uniform_bind_group: *gpu.BindGroup, -object_data: [3]ObjectData, -primitives: [2]PrimitiveData, +object_data: []ObjectData, +primitives: []PrimitiveData, pub fn init(app: *App) !void { try core.init(.{}); + // Set up a "general purpose allocator" that will handle allocations for + // the lifetime of the application, for which a more specific allocation + // strategy is not necessary. + // + // Here, `gpa` is an instance of this allocator, which handles the + // allocation logic, while `allocator` is the interface through which + // functions such as `alloc` and `free` are called. + app.gpa = GeneralPurposeAllocator{}; + app.allocator = app.gpa.allocator(); + app.app_timer = try core.Timer.start(); app.title_timer = try core.Timer.start(); @@ -124,7 +139,17 @@ pub fn init(app: *App) !void { // would see the back side of the triangles, which are culled. const rotation = zm.rotationY(std.math.tau / 2.0); - for (object_desc, &app.object_data) |desc, *object| { + // Allocate a slice to store as many ObjectData as we want to create. + // + // Using a slice instead of an array means that we could change how + // many object we want to render at compile time, however it requires + // allocating, and later freeing, memory to store the slice. + app.object_data = try app.allocator.alloc(ObjectData, object_desc.len); + + // Note that for loops in Zig are a little different than you might + // know from other languages. They only look over arrays, slices, + // tuples and ranges, potentially multiple at once. + for (object_desc, app.object_data) |desc, *object| { const translation = zm.translation(desc.pos[0], desc.pos[1], desc.pos[2]); const model_matrix = zm.mul(rotation, translation); @@ -136,6 +161,8 @@ pub fn init(app: *App) !void { }, ); + // The `*object` syntax gets us a pointer to each element in the + // `object_data` slice, allowing us to override it within the loop. object.* = .{ .uniform_buffer = result.buffer, .uniform_bind_group = result.bind_group, @@ -145,6 +172,7 @@ pub fn init(app: *App) !void { } // Set up the primitives we want to render. + app.primitives = try app.allocator.alloc(PrimitiveData, 2); // Triangle app.primitives[0] = createPrimitive( &.{ @@ -246,13 +274,16 @@ pub fn deinit(app: *App) void { // Using `defer` here, so we can specify them // in the order they were created in `init`. defer core.deinit(); + defer _ = app.gpa.deinit(); // TODO: Check for memory leaks? defer app.pipeline.release(); defer app.scene_uniform_buffer.release(); defer app.scene_uniform_bind_group.release(); + defer app.allocator.free(app.object_data); defer for (app.object_data) |o| { o.uniform_buffer.release(); o.uniform_bind_group.release(); }; + defer app.allocator.free(app.primitives); defer for (app.primitives) |p| { p.vertex_buffer.release(); p.index_buffer.release();