diff --git a/src/main.zig b/src/main.zig index 336e8cf..50e5cea 100644 --- a/src/main.zig +++ b/src/main.zig @@ -52,9 +52,14 @@ pub fn update(app: *App) !bool { var iter = core.pollEvents(); while (iter.next()) |event| { switch (event) { + // Allow the renderer to act on the window being resized. + // This is required so we can resize necessary buffers. + .framebuffer_resize => |_| app.renderer.resize(), + // Close the window when requested, such as when // pressing the X button in the window title bar. .close => return true, + else => {}, } } diff --git a/src/renderer.zig b/src/renderer.zig index 6da40d9..5460130 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -38,26 +38,17 @@ const Renderer = @This(); app: *App, -depth_texture: *gpu.Texture, -depth_texture_view: *gpu.TextureView, pipeline: *gpu.RenderPipeline, scene_uniform_buffer: *gpu.Buffer, scene_uniform_bind_group: *gpu.BindGroup, +depth_texture: ?*gpu.Texture = null, +depth_texture_view: ?*gpu.TextureView = null, + primitive_data: []PrimitiveData, object_data: []ObjectData, pub fn init(app: *App) !*Renderer { - // Create a depth texture. This is used to ensure that when things are - // rendered, an object behind another won't draw over one in front, simply - // because it was rendered at a later point in time. - const depth_texture = core.device.createTexture(&.{ - .usage = .{ .render_attachment = true }, - .size = .{ .width = core.descriptor.width, .height = core.descriptor.height }, - .format = .depth24_plus, - }); - const depth_texture_view = depth_texture.createView(null); - const shader_module = core.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.wgsl")); defer shader_module.release(); @@ -167,14 +158,17 @@ pub fn init(app: *App) !*Renderer { const result = try app.allocator.create(Renderer); result.* = .{ .app = app, - .depth_texture = depth_texture, - .depth_texture_view = depth_texture_view, .pipeline = pipeline, .scene_uniform_buffer = scene_uniform.buffer, .scene_uniform_bind_group = scene_uniform.bind_group, .primitive_data = primitive_data, .object_data = object_data, }; + + // Initialize the depth texture. + // This is called also whenever the window is resized. + result.recreateDepthTexture(); + return result; } @@ -183,8 +177,6 @@ pub fn deinit(self: *Renderer) void { // in the order they were created in `init`. defer self.app.allocator.destroy(self); - defer self.depth_texture.release(); - defer self.depth_texture_view.release(); defer self.pipeline.release(); defer self.scene_uniform_buffer.release(); defer self.scene_uniform_bind_group.release(); @@ -199,6 +191,15 @@ pub fn deinit(self: *Renderer) void { o.uniform_buffer.release(); o.uniform_bind_group.release(); }; + + defer if (self.depth_texture) |t| t.release(); + defer if (self.depth_texture_view) |v| v.release(); +} + +pub fn resize(self: *Renderer) void { + // Recreate depth texture with the proper size, otherwise + // the application may crash when the window is resized. + self.recreateDepthTexture(); } pub fn update(self: *Renderer) void { @@ -236,7 +237,7 @@ pub fn update(self: *Renderer) void { .store_op = .store, }}, .depth_stencil_attachment = &.{ - .view = self.depth_texture_view, + .view = self.depth_texture_view.?, .depth_load_op = .clear, .depth_store_op = .store, .depth_clear_value = 1.0, @@ -285,6 +286,22 @@ pub fn update(self: *Renderer) void { core.queue.submit(&.{command}); } +/// Creates a depth texture. This is used to ensure that when things are +/// rendered, an object behind another won't draw over one in front, simply +/// because it was rendered at a later point in time. +pub fn recreateDepthTexture(self: *Renderer) void { + // Release previous depth butter and view, if any. + if (self.depth_texture) |t| t.release(); + if (self.depth_texture_view) |v| v.release(); + + self.depth_texture = core.device.createTexture(&.{ + .usage = .{ .render_attachment = true }, + .size = .{ .width = core.descriptor.width, .height = core.descriptor.height }, + .format = .depth24_plus, + }); + self.depth_texture_view = self.depth_texture.?.createView(null); +} + /// Creates a buffer on the GPU to store uniform parameter information as /// well as a bind group with the specified layout pointing to that buffer. /// Additionally, immediately fills the buffer with the provided data.