From 9baf42e838e540e7ae1d37a79a412aefa56e51f5 Mon Sep 17 00:00:00 2001 From: Szymon Nowakowski Date: Fri, 5 Dec 2025 22:07:06 +0100 Subject: [PATCH] Complete all Vulkan wrappers. Maybe worth it. --- .vscode/extensions.json | 1 + src/Game.zig | 130 +++++++++--------- src/Player.zig | 2 +- src/assets/Chunk.zig | 9 +- src/engine/CommandBuffer.zig | 50 +------ src/engine/Engine.zig | 247 +++++++++++++++++++++++++++++++---- src/engine/Swapchain.zig | 193 +++++++++++---------------- src/main.zig | 2 +- src/math/Matrix4x4.zig | 46 +++---- src/math/Quaternion.zig | 16 +-- src/math/Vector3.zig | 16 +-- 11 files changed, 410 insertions(+), 302 deletions(-) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 7a693ea..4b33eeb 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,5 +1,6 @@ { "recommendations": [ "vadimcn.vscode-lldb", + "ziglang.vscode-zig", ], } diff --git a/src/Game.zig b/src/Game.zig index f715f21..ba410ad 100644 --- a/src/Game.zig +++ b/src/Game.zig @@ -686,9 +686,62 @@ pub fn deinit(self: *Game) void { pub fn update(self: *Game, dt: f32) void { self.player.update(dt); + self.render() catch |err| { + std.log.err("Failed to render: {s}", .{@errorName(err)}); + @panic("Frame update failed"); + }; +} + +pub fn onKeyDown(self: *Game, key: glfw.Key) void { + self.player.onKeyDown(key); +} + +pub fn onKeyUp(self: *Game, key: glfw.Key) void { + self.player.onKeyUp(key); +} + +pub fn onMouseMove(self: *Game, dx: f32, dy: f32) void { + self.player.onMouseMove(dx, dy); +} + +pub fn onMouseDown(self: *Game, button: glfw.MouseButton) void { + _ = self; + _ = button; +} + +pub fn onMouseUp(self: *Game, button: glfw.MouseButton) void { + _ = self; + _ = button; +} + +fn render(self: *Game) !void { + const framebuffer_width, const framebuffer_height = blk: { + const w, const h = self.engine.mode.surface.window.getFramebufferSize(); + break :blk [_]u32{ @intCast(w), @intCast(h) }; + }; + + if (framebuffer_width == 0 or framebuffer_height == 0) { + return; + } + + if (framebuffer_width != self.swapchain.extent.width or framebuffer_height != self.swapchain.extent.height) { + try self.recreateSwapchain(); + } + + if (self.swapchain.swapchain == .null_handle) { + return; + } + + self.swapchain.acquire(self.engine) catch |err| switch (err) { + error.OutOfDateKHR => return self.recreateSwapchain(), + else => return err, + }; + + const extent = self.swapchain.extent; + const framebuffer_size = Vector2.init( - @floatFromInt(self.swapchain.extent.width), - @floatFromInt(self.swapchain.extent.height), + @floatFromInt(extent.width), + @floatFromInt(extent.height), ); const camera_position = self.player.position.add(.init(0, 0, Player.camera_height)); @@ -722,63 +775,22 @@ pub fn update(self: *Game, dt: f32) void { .ambientLight = ambient_light.asArray(), }; - const staging_memory = self.global_uniforms_staging_buffer.map(self.engine) catch |err| { - std.log.err("Failed to map global uniforms staging buffer: {s}", .{@errorName(err)}); - @panic("Frame update failed"); - }; + const staging_memory = try self.global_uniforms_staging_buffer.map(self.engine); @memcpy(staging_memory, std.mem.asBytes(&global_uniforms_data)); self.global_uniforms_staging_buffer.unmap(self.engine); - self.global_uniforms_transfer_command_buffer.submit(self.engine, .{ - .signal_semaphores = &.{self.global_uniforms_transfer_semaphores[self.swapchain.image_index]}, - }) catch |err| { - std.log.err("Failed to submit global uniforms transfer: {s}", .{@errorName(err)}); - @panic("Frame update failed"); - }; + try self.global_uniforms_transfer_command_buffer.submit(self.engine, .{ + .signal_semaphores = &.{self.global_uniforms_transfer_semaphores[self.swapchain.image_index.?]}, + }); - self.render() catch |err| { - std.log.err("Failed to render: {s}", .{@errorName(err)}); - @panic("Frame update failed"); - }; -} - -pub fn onKeyDown(self: *Game, key: glfw.Key) void { - self.player.onKeyDown(key); -} - -pub fn onKeyUp(self: *Game, key: glfw.Key) void { - self.player.onKeyUp(key); -} - -pub fn onMouseMove(self: *Game, dx: f32, dy: f32) void { - self.player.onMouseMove(dx, dy); -} - -pub fn onMouseDown(self: *Game, button: glfw.MouseButton) void { - _ = self; - _ = button; -} - -pub fn onMouseUp(self: *Game, button: glfw.MouseButton) void { - _ = self; - _ = button; -} - -fn render(self: *Game) !void { - if (self.swapchain.swapchain == .null_handle) { - return; - } - - const extent = self.swapchain.extent; - - const command_buffer: CommandBuffer = try .init(self.engine, .graphics, .transient); - // NOTE Do not free command buffer yet + var command_buffer: CommandBuffer = try .init(self.engine, .graphics, .transient); + defer command_buffer.deinit(self.engine); try command_buffer.beginCommandBuffer(); { command_buffer.beginRenderPass(.{ .render_pass = self.swapchain.render_pass, - .framebuffer = self.swapchain.swapchain_images[self.swapchain.image_index].framebuffer, + .framebuffer = self.swapchain.swapchain_images[self.swapchain.image_index.?].framebuffer, .render_area = .{ .offset = .{ .x = 0, .y = 0 }, .extent = extent, @@ -833,26 +845,22 @@ fn render(self: *Game) !void { } try command_buffer.endCommandBuffer(); - const res = try self.swapchain.present(self.engine, .{ + self.swapchain.present(self.engine, .{ .command_buffer = command_buffer, .wait_semaphores = &.{ .{ - .semaphore = self.global_uniforms_transfer_semaphores[self.swapchain.image_index], + .semaphore = self.global_uniforms_transfer_semaphores[self.swapchain.image_index.?], .stage_flags = .{ .vertex_shader_bit = true }, }, }, - }); - - if (res == .suboptimal) { - try self.recreateSwapchain(); - } -} - -fn recreateSwapchain(self: *Game) !void { - self.swapchain.recreate(self.engine) catch |err| switch (err) { + }) catch |err| switch (err) { error.OutOfDateKHR => return self.recreateSwapchain(), else => return err, }; +} + +fn recreateSwapchain(self: *Game) !void { + try self.swapchain.recreate(self.engine); for (self.global_uniforms_transfer_semaphores, 0..) |*semaphore, i| { self.engine.destroySemaphore(semaphore.*); semaphore.* = try self.engine.createSemaphore(); diff --git a/src/Player.zig b/src/Player.zig index 2d84ab0..909c840 100644 --- a/src/Player.zig +++ b/src/Player.zig @@ -131,5 +131,5 @@ pub fn update(self: *Player, dt: f32) void { const displacement = horizontal_velocity.asVector3(vertical_velocity).mulScalar(dt); - self.position = Vector3.add(self.position, displacement); + self.position = .add(self.position, displacement); } diff --git a/src/assets/Chunk.zig b/src/assets/Chunk.zig index ffd22d9..9d6d255 100644 --- a/src/assets/Chunk.zig +++ b/src/assets/Chunk.zig @@ -39,14 +39,7 @@ pub const InitInfo = struct { per_batch_descriptor_set_layout: vk.DescriptorSetLayout, }; -pub const Neighbors = struct { - positive_x: ?*const Chunk, - negative_x: ?*const Chunk, - positive_y: ?*const Chunk, - negative_y: ?*const Chunk, - positive_z: ?*const Chunk, - negative_z: ?*const Chunk, -}; +pub const Neighbors = std.enums.EnumFieldStruct(voxels.Orientation, ?*const Chunk, null); pub fn init(engine: *Engine, init_info: InitInfo) !Chunk { const descriptor_set = try engine.allocateDescriptorSet(.{ diff --git a/src/engine/CommandBuffer.zig b/src/engine/CommandBuffer.zig index ead9687..af92dee 100644 --- a/src/engine/CommandBuffer.zig +++ b/src/engine/CommandBuffer.zig @@ -14,7 +14,6 @@ const vk = @import("vulkan"); const Engine = @import("Engine.zig"); const QueueType = @import("QueueType.zig").QueueType; const Transient = @import("Transient.zig").Transient; -const WaitSemaphore = @import("WaitSemaphore.zig"); proxy: vk.CommandBufferProxy, allocator: std.mem.Allocator, @@ -46,8 +45,8 @@ pub fn deinit(self: *CommandBuffer, engine: *Engine) void { self.* = undefined; } -pub fn submit(self: CommandBuffer, engine: *Engine, submit_info: SubmitInfo) !void { - try submitCommandBuffer(engine, self.queue_type, self.proxy.handle, submit_info); +pub fn submit(self: CommandBuffer, engine: *Engine, submit_info: Engine.SubmitInfo) !void { + try engine.queueSubmit(self.queue_type, self.proxy.handle, submit_info); } // --- VULKAN WRAPPERS --------------------------------------------------------- @@ -76,12 +75,6 @@ pub const RenderPassBeginInfo = struct { clear_values: []const vk.ClearValue = &.{}, }; -pub const SubmitInfo = struct { - wait_semaphores: []const WaitSemaphore = &.{}, - signal_semaphores: []const vk.Semaphore = &.{}, - fence: vk.Fence = .null_handle, -}; - pub const VertexBufferBinding = struct { buffer: vk.Buffer, offset: usize, @@ -227,42 +220,3 @@ pub fn setScissor(self: CommandBuffer, first_scissor: u32, scissors: []const vk. pub fn setViewport(self: CommandBuffer, first_viewport: u32, viewports: []const vk.Viewport) void { self.proxy.setViewport(first_viewport, @intCast(viewports.len), viewports.ptr); } - -// --- HELPER FUNCTIONS -------------------------------------------------------- - -fn submitCommandBuffer( - engine: *Engine, - queue_type: QueueType, - command_buffer: vk.CommandBuffer, - submit_info: SubmitInfo, -) !void { - const queue = switch (queue_type) { - .graphics => engine.graphics_queue.handle, - .compute => engine.compute_queue.handle, - .transfer => engine.transfer_queue.handle, - }; - - const command_buffers = [_]vk.CommandBuffer{command_buffer}; - - var wait_semaphores = std.MultiArrayList(WaitSemaphore){}; - defer wait_semaphores.deinit(engine.vk_allocator.allocator); - try wait_semaphores.ensureTotalCapacity(engine.vk_allocator.allocator, submit_info.wait_semaphores.len); - - for (submit_info.wait_semaphores) |wait_semaphore| { - wait_semaphores.appendAssumeCapacity(wait_semaphore); - } - - const submits = [_]vk.SubmitInfo{ - .{ - .wait_semaphore_count = @intCast(wait_semaphores.len), - .p_wait_semaphores = wait_semaphores.items(.semaphore).ptr, - .p_wait_dst_stage_mask = wait_semaphores.items(.stage_flags).ptr, - .command_buffer_count = command_buffers.len, - .p_command_buffers = &command_buffers, - .signal_semaphore_count = @intCast(submit_info.signal_semaphores.len), - .p_signal_semaphores = submit_info.signal_semaphores.ptr, - }, - }; - - try engine.device.queueSubmit(queue, submits.len, &submits, submit_info.fence); -} diff --git a/src/engine/Engine.zig b/src/engine/Engine.zig index be958c3..5d4de3e 100644 --- a/src/engine/Engine.zig +++ b/src/engine/Engine.zig @@ -9,6 +9,7 @@ const Queue = @import("Queue.zig"); const QueueType = @import("QueueType.zig").QueueType; const Transient = @import("Transient.zig").Transient; const VkAllocator = @import("VkAllocator.zig"); +const WaitSemaphore = @import("WaitSemaphore.zig"); pub const Mode = union(enum) { headless: void, @@ -531,6 +532,69 @@ pub fn setObjectName(self: *const Engine, handle: anytype, comptime fmt: []const } } +pub const SubmitInfo = struct { + wait_semaphores: []const WaitSemaphore = &.{}, + signal_semaphores: []const vk.Semaphore = &.{}, + fence: vk.Fence = .null_handle, +}; + +pub fn queueSubmit( + self: *const Engine, + queue_type: QueueType, + command_buffer: vk.CommandBuffer, + submit_info: SubmitInfo, +) !void { + const queue = switch (queue_type) { + .graphics => self.graphics_queue.handle, + .compute => self.compute_queue.handle, + .transfer => self.transfer_queue.handle, + }; + + const command_buffers = [_]vk.CommandBuffer{command_buffer}; + + var wait_semaphores = std.MultiArrayList(WaitSemaphore){}; + defer wait_semaphores.deinit(self.vk_allocator.allocator); + try wait_semaphores.ensureTotalCapacity(self.vk_allocator.allocator, submit_info.wait_semaphores.len); + + for (submit_info.wait_semaphores) |wait_semaphore| { + wait_semaphores.appendAssumeCapacity(wait_semaphore); + } + + const submits = [_]vk.SubmitInfo{ + .{ + .wait_semaphore_count = @intCast(wait_semaphores.len), + .p_wait_semaphores = wait_semaphores.items(.semaphore).ptr, + .p_wait_dst_stage_mask = wait_semaphores.items(.stage_flags).ptr, + .command_buffer_count = command_buffers.len, + .p_command_buffers = &command_buffers, + .signal_semaphore_count = @intCast(submit_info.signal_semaphores.len), + .p_signal_semaphores = submit_info.signal_semaphores.ptr, + }, + }; + + try self.device.queueSubmit(queue, submits.len, &submits, submit_info.fence); +} + +pub const PresentInfo = struct { + wait_semaphores: []const vk.Semaphore = &.{}, + image_index: u32, + swapchain: vk.SwapchainKHR, +}; + +pub fn queuePresent(self: *Engine, present_info: PresentInfo) !vk.Result { + const swapchains = [_]vk.SwapchainKHR{present_info.swapchain}; + const image_indices = [_]u32{present_info.image_index}; + + const res = try self.device.queuePresentKHR(self.mode.surface.presentation_queue.handle, &.{ + .wait_semaphore_count = @intCast(present_info.wait_semaphores.len), + .p_wait_semaphores = present_info.wait_semaphores.ptr, + .swapchain_count = swapchains.len, + .p_swapchains = &swapchains, + .p_image_indices = &image_indices, + }); + return res; +} + fn debugUtilsMessengerCallback( severity: vk.DebugUtilsMessageSeverityFlagsEXT, message_type: vk.DebugUtilsMessageTypeFlagsEXT, @@ -657,6 +721,12 @@ fn resolveCommandPool(self: *const Engine, queue_type: QueueType, transient: Tra // --- VULKAN WRAPPERS --------------------------------------------------------- +pub const AcquireInfo = struct { + timeout: u64 = std.math.maxInt(u64), + semaphore: vk.Semaphore = .null_handle, + fence: vk.Fence = .null_handle, +}; + pub const BufferCreateInfo = struct { flags: vk.BufferCreateFlags = .{}, size: vk.DeviceSize, @@ -807,6 +877,13 @@ pub const PipelineViewportStateCreateInfo = struct { scissors: []const vk.Rect2D = &.{}, }; +pub const RenderPassCreateInfo = struct { + flags: vk.RenderPassCreateFlags = .{}, + attachments: []const vk.AttachmentDescription = &.{}, + subpasses: []const SubpassDescription, + dependencies: []const vk.SubpassDependency = &.{}, +}; + pub const ShaderModuleCreateInfo = struct { flags: vk.ShaderModuleCreateFlags = .{}, code: []align(@alignOf(u32)) const u8, @@ -817,6 +894,33 @@ pub const SpecializationInfo = struct { data: []const u8 = &.{}, }; +pub const SubpassDescription = struct { + flags: vk.SubpassDescriptionFlags = .{}, + pipeline_bind_point: vk.PipelineBindPoint, + input_attachments: []const vk.AttachmentReference = &.{}, + color_attachments: []const vk.AttachmentReference = &.{}, + resolve_attachments: ?[]const vk.AttachmentReference = null, + depth_stencil_attachment: ?vk.AttachmentReference = null, + preserve_attachments: []const u32 = &.{}, +}; + +pub const SwapchainCreateInfo = struct { + flags: vk.SwapchainCreateFlagsKHR = .{}, + surface: vk.SurfaceKHR, + min_image_count: u32, + image_format: vk.Format, + image_color_space: vk.ColorSpaceKHR, + image_extent: vk.Extent2D, + image_array_layers: u32, + image_usage: vk.ImageUsageFlags, + queue_family_indices: []const u32 = &.{}, + pre_transform: vk.SurfaceTransformFlagsKHR, + composite_alpha: vk.CompositeAlphaFlagsKHR, + present_mode: vk.PresentModeKHR, + clipped: vk.Bool32, + old_swapchain: vk.SwapchainKHR = .null_handle, +}; + pub const WriteDescriptorSet = struct { dst_set: vk.DescriptorSet, dst_binding: u32, @@ -829,27 +933,14 @@ pub const WriteDescriptorSet = struct { }, }; -pub fn createBuffer(self: *Engine, create_info: BufferCreateInfo) !vk.Buffer { - var arena: std.heap.ArenaAllocator = .init(self.vk_allocator.allocator); - defer arena.deinit(); - const allocator = arena.allocator(); - - var queue_family_indices_set: std.AutoArrayHashMapUnmanaged(u32, void) = .{}; - for (create_info.queue_family_indices) |queue_family_index| { - try queue_family_indices_set.put(allocator, queue_family_index, {}); - } - - const queue_family_indices = queue_family_indices_set.keys(); - - const buffer = self.device.createBuffer(&.{ - .flags = create_info.flags, - .size = create_info.size, - .usage = create_info.usage, - .sharing_mode = if (queue_family_indices.len > 1) .concurrent else .exclusive, - .queue_family_index_count = if (queue_family_indices.len > 1) @intCast(queue_family_indices.len) else 0, - .p_queue_family_indices = if (queue_family_indices.len > 1) queue_family_indices.ptr else null, - }, &self.vk_allocator.interface); - return buffer; +pub fn acquireNextImage(self: *Engine, swapchain: vk.SwapchainKHR, acquire_info: AcquireInfo) !vk.DeviceProxy.AcquireNextImageKHRResult { + const res = try self.device.acquireNextImageKHR( + swapchain, + acquire_info.timeout, + acquire_info.semaphore, + acquire_info.fence, + ); + return res; } pub fn allocateCommandBuffer(self: *Engine, allocate_info: CommandBufferAllocateInfo) !vk.CommandBuffer { @@ -904,6 +995,29 @@ pub fn bindImageMemory(self: *Engine, image: vk.Image, memory: vk.DeviceMemory, try self.device.bindImageMemory(image, memory, memory_offset); } +pub fn createBuffer(self: *Engine, create_info: BufferCreateInfo) !vk.Buffer { + var arena: std.heap.ArenaAllocator = .init(self.vk_allocator.allocator); + defer arena.deinit(); + const allocator = arena.allocator(); + + var queue_family_indices_set: std.AutoArrayHashMapUnmanaged(u32, void) = .{}; + for (create_info.queue_family_indices) |queue_family_index| { + try queue_family_indices_set.put(allocator, queue_family_index, {}); + } + + const queue_family_indices = queue_family_indices_set.keys(); + + const buffer = self.device.createBuffer(&.{ + .flags = create_info.flags, + .size = create_info.size, + .usage = create_info.usage, + .sharing_mode = if (queue_family_indices.len > 1) .concurrent else .exclusive, + .queue_family_index_count = if (queue_family_indices.len > 1) @intCast(queue_family_indices.len) else 0, + .p_queue_family_indices = if (queue_family_indices.len > 1) queue_family_indices.ptr else null, + }, &self.vk_allocator.interface); + return buffer; +} + pub fn createDescriptorSetLayout(self: *Engine, create_info: DescriptorSetLayoutCreateInfo) !vk.DescriptorSetLayout { var arena: std.heap.ArenaAllocator = .init(self.vk_allocator.allocator); defer arena.deinit(); @@ -986,7 +1100,7 @@ pub fn createGraphicsPipeline(self: *Engine, create_info: GraphicsPipelineCreate const allocator = arena.allocator(); const stages = try allocator.alloc(vk.PipelineShaderStageCreateInfo, create_info.stages.len); - for (stages, create_info.stages) |*x, *stage| { + for (stages, create_info.stages) |*x, stage| { x.* = .{ .flags = stage.flags, .stage = stage.stage, @@ -1112,6 +1226,39 @@ pub fn createPipelineLayout(self: *Engine, create_info: PipelineLayoutCreateInfo return pipeline_layout; } +pub fn createRenderPass(self: *Engine, create_info: RenderPassCreateInfo) !vk.RenderPass { + var arena: std.heap.ArenaAllocator = .init(self.vk_allocator.allocator); + defer arena.deinit(); + const allocator = arena.allocator(); + + const subpasses = try allocator.alloc(vk.SubpassDescription, create_info.subpasses.len); + for (subpasses, create_info.subpasses) |*x, *subpass| { + x.* = .{ + .flags = subpass.flags, + .pipeline_bind_point = subpass.pipeline_bind_point, + .input_attachment_count = @intCast(subpass.input_attachments.len), + .p_input_attachments = subpass.input_attachments.ptr, + .color_attachment_count = @intCast(subpass.color_attachments.len), + .p_color_attachments = subpass.color_attachments.ptr, + .p_resolve_attachments = if (subpass.resolve_attachments) |resolve_attachments| resolve_attachments.ptr else null, + .p_depth_stencil_attachment = if (subpass.depth_stencil_attachment) |*y| y else null, + .preserve_attachment_count = @intCast(subpass.preserve_attachments.len), + .p_preserve_attachments = subpass.preserve_attachments.ptr, + }; + } + + const render_pass = try self.device.createRenderPass(&.{ + .flags = create_info.flags, + .attachment_count = @intCast(create_info.attachments.len), + .p_attachments = create_info.attachments.ptr, + .subpass_count = @intCast(subpasses.len), + .p_subpasses = subpasses.ptr, + .dependency_count = @intCast(create_info.dependencies.len), + .p_dependencies = create_info.dependencies.ptr, + }, &self.vk_allocator.interface); + return render_pass; +} + pub fn createSampler(self: *Engine, create_info: vk.SamplerCreateInfo) !vk.Sampler { const sampler = try self.device.createSampler(&create_info, &self.vk_allocator.interface); return sampler; @@ -1131,6 +1278,39 @@ pub fn createShaderModule(self: *Engine, create_info: ShaderModuleCreateInfo) !v return shader_module; } +pub fn createSwapchain(self: *Engine, create_info: SwapchainCreateInfo) !vk.SwapchainKHR { + var arena: std.heap.ArenaAllocator = .init(self.vk_allocator.allocator); + defer arena.deinit(); + const allocator = arena.allocator(); + + var queue_family_indices_set: std.AutoArrayHashMapUnmanaged(u32, void) = .{}; + for (create_info.queue_family_indices) |queue_family_index| { + try queue_family_indices_set.put(allocator, queue_family_index, {}); + } + + const queue_family_indices = queue_family_indices_set.keys(); + + const swapchain = try self.device.createSwapchainKHR(&.{ + .flags = create_info.flags, + .surface = create_info.surface, + .min_image_count = create_info.min_image_count, + .image_format = create_info.image_format, + .image_color_space = create_info.image_color_space, + .image_extent = create_info.image_extent, + .image_array_layers = create_info.image_array_layers, + .image_usage = create_info.image_usage, + .image_sharing_mode = if (queue_family_indices.len > 1) .concurrent else .exclusive, + .queue_family_index_count = if (queue_family_indices.len > 1) @intCast(queue_family_indices.len) else 0, + .p_queue_family_indices = if (queue_family_indices.len > 1) queue_family_indices.ptr else null, + .pre_transform = create_info.pre_transform, + .composite_alpha = create_info.composite_alpha, + .present_mode = create_info.present_mode, + .clipped = create_info.clipped, + .old_swapchain = create_info.old_swapchain, + }, &self.vk_allocator.interface); + return swapchain; +} + pub fn destroyBuffer(self: *Engine, buffer: vk.Buffer) void { self.device.destroyBuffer(buffer, &self.vk_allocator.interface); } @@ -1167,6 +1347,10 @@ pub fn destroyPipelineLayout(self: *Engine, pipeline_layout: vk.PipelineLayout) self.device.destroyPipelineLayout(pipeline_layout, &self.vk_allocator.interface); } +pub fn destroyRenderPass(self: *Engine, render_pass: vk.RenderPass) void { + self.device.destroyRenderPass(render_pass, &self.vk_allocator.interface); +} + pub fn destroySampler(self: *Engine, sampler: vk.Sampler) void { self.device.destroySampler(sampler, &self.vk_allocator.interface); } @@ -1179,6 +1363,10 @@ pub fn destroyShaderModule(self: *Engine, shader_module: vk.ShaderModule) void { self.device.destroyShaderModule(shader_module, &self.vk_allocator.interface); } +pub fn destroySwapchain(self: *Engine, swapchain: vk.SwapchainKHR) void { + self.device.destroySwapchainKHR(swapchain, &self.vk_allocator.interface); +} + pub fn deviceWaitIdle(self: *Engine) !void { try self.device.deviceWaitIdle(); } @@ -1206,6 +1394,21 @@ pub fn getImageMemoryRequirements(self: *Engine, image: vk.Image) vk.MemoryRequi return self.device.getImageMemoryRequirements(image); } +pub fn getPhysicalDeviceSurfaceCapabilities(self: *Engine) !vk.SurfaceCapabilitiesKHR { + const surface_capabilities = try self.instance.getPhysicalDeviceSurfaceCapabilitiesKHR(self.physical_device, self.mode.surface.surface); + return surface_capabilities; +} + +pub fn getPhysicalDeviceSurfaceFormatsAlloc(self: *Engine, allocator: std.mem.Allocator) ![]vk.SurfaceFormatKHR { + const surface_formats = try self.instance.getPhysicalDeviceSurfaceFormatsAllocKHR(self.physical_device, self.mode.surface.surface, allocator); + return surface_formats; +} + +pub fn getSwapchainImagesAlloc(self: *Engine, swapchain: vk.SwapchainKHR, allocator: std.mem.Allocator) ![]vk.Image { + const images = try self.device.getSwapchainImagesAllocKHR(swapchain, allocator); + return images; +} + pub fn mapMemory(self: *Engine, memory: vk.DeviceMemory, offset: vk.DeviceSize, size: vk.DeviceSize, flags: vk.MemoryMapFlags) ![]u8 { const mapped_memory = try self.device.mapMemory(memory, offset, size, flags); return @as([*]u8, @ptrCast(mapped_memory))[0..size]; diff --git a/src/engine/Swapchain.zig b/src/engine/Swapchain.zig index 194be04..7d8f01f 100644 --- a/src/engine/Swapchain.zig +++ b/src/engine/Swapchain.zig @@ -16,7 +16,8 @@ extent: vk.Extent2D = .{ .width = 0, .height = 0 }, swapchain: vk.SwapchainKHR = .null_handle, swapchain_images: []SwapchainImage = &.{}, depth_texture: ?Texture = null, -image_index: u32 = 0, +image_index: ?u32 = null, +fence: vk.Fence = .null_handle, semaphore_image_acquired: vk.Semaphore = .null_handle, pub const PresentResult = enum { @@ -27,8 +28,8 @@ pub const PresentResult = enum { pub fn init(engine: *Engine) !Swapchain { const params: Params = try .init(engine); - const render_pass = blk: { - const attachments = [_]vk.AttachmentDescription{ + const render_pass = try engine.createRenderPass(.{ + .attachments = &.{ .{ .format = params.surface_format.format, .samples = .{ .@"1_bit" = true }, @@ -49,30 +50,23 @@ pub fn init(engine: *Engine) !Swapchain { .initial_layout = .undefined, .final_layout = .depth_stencil_attachment_optimal, }, - }; - - const color_attachments = [_]vk.AttachmentReference{ - .{ - .attachment = 0, - .layout = .color_attachment_optimal, - }, - }; - - const depth_stencil_attachment: vk.AttachmentReference = .{ - .attachment = 1, - .layout = .depth_stencil_attachment_optimal, - }; - - const subpasses = [_]vk.SubpassDescription{ + }, + .subpasses = &.{ .{ .pipeline_bind_point = .graphics, - .color_attachment_count = color_attachments.len, - .p_color_attachments = &color_attachments, - .p_depth_stencil_attachment = &depth_stencil_attachment, + .color_attachments = &.{ + .{ + .attachment = 0, + .layout = .color_attachment_optimal, + }, + }, + .depth_stencil_attachment = .{ + .attachment = 1, + .layout = .depth_stencil_attachment_optimal, + }, }, - }; - - const dependencies = [_]vk.SubpassDependency{ + }, + .dependencies = &.{ .{ .src_subpass = 0, .dst_subpass = 0, @@ -92,18 +86,9 @@ pub fn init(engine: *Engine) !Swapchain { .depth_stencil_attachment_write_bit = true, }, }, - }; - - break :blk try engine.device.createRenderPass(&.{ - .attachment_count = attachments.len, - .p_attachments = &attachments, - .subpass_count = subpasses.len, - .p_subpasses = &subpasses, - .dependency_count = dependencies.len, - .p_dependencies = &dependencies, - }, &engine.vk_allocator.interface); - }; - errdefer engine.device.destroyRenderPass(render_pass, &engine.vk_allocator.interface); + }, + }); + errdefer engine.destroyRenderPass(render_pass); engine.setObjectName(render_pass, "RP", .{}); var swapchain: Swapchain = .{ @@ -130,11 +115,12 @@ pub fn deinit(self: *Swapchain, engine: *Engine) void { } if (self.swapchain != .null_handle) { - engine.device.destroySwapchainKHR(self.swapchain, &engine.vk_allocator.interface); + engine.destroySwapchain(self.swapchain); } - engine.device.destroyRenderPass(self.render_pass, &engine.vk_allocator.interface); + engine.destroyRenderPass(self.render_pass); engine.destroySemaphore(self.semaphore_image_acquired); + engine.destroyFence(self.fence); self.* = undefined; } @@ -145,8 +131,9 @@ pub fn recreate(self: *Swapchain, engine: *Engine) !void { const old_swapchain = self.swapchain; const old_swapchain_images = self.swapchain_images; - var old_depth_texture = self.depth_texture; + const old_depth_texture = &self.depth_texture; const old_semaphore_image_acquired = self.semaphore_image_acquired; + const old_fence = self.fence; const extent = try getCurrentExtent(engine); @@ -154,10 +141,8 @@ pub fn recreate(self: *Swapchain, engine: *Engine) !void { // --- CREATE NEW SWAPCHAIN ------------------------------------------------ - const surface_capabilities = try engine.instance.getPhysicalDeviceSurfaceCapabilitiesKHR(engine.physical_device, mode.surface); - - const qsm = QSM.resolve(engine.graphics_queue.allocation.family, mode.presentation_queue.allocation.family); - const new_swapchain = try engine.device.createSwapchainKHR(&.{ + const surface_capabilities = try engine.getPhysicalDeviceSurfaceCapabilities(); + const new_swapchain = try engine.createSwapchain(.{ .surface = mode.surface, .min_image_count = self.params.image_count, .image_format = self.params.surface_format.format, @@ -165,16 +150,17 @@ pub fn recreate(self: *Swapchain, engine: *Engine) !void { .image_extent = extent, .image_array_layers = 1, .image_usage = .{ .color_attachment_bit = true }, - .image_sharing_mode = qsm.sharing_mode, - .queue_family_index_count = qsm.queue_family_index_count, - .p_queue_family_indices = qsm.p_queue_family_indices, + .queue_family_indices = &.{ + engine.graphics_queue.allocation.family, + mode.presentation_queue.allocation.family, + }, .pre_transform = surface_capabilities.current_transform, .composite_alpha = .{ .opaque_bit_khr = true }, .present_mode = .fifo_khr, .clipped = .true, .old_swapchain = old_swapchain, - }, &engine.vk_allocator.interface); - errdefer engine.device.destroySwapchainKHR(new_swapchain, &engine.vk_allocator.interface); + }); + errdefer engine.destroySwapchain(new_swapchain); engine.setObjectName(new_swapchain, "SC {d}×{d}", .{ extent.width, extent.height }); // --- DESTROY OLD SWAPCHAIN AND ITS IMAGES -------------------------------- @@ -189,22 +175,28 @@ pub fn recreate(self: *Swapchain, engine: *Engine) !void { allocator.free(self.swapchain_images); self.swapchain_images = &.{}; - if (old_depth_texture) |*depth_texture| { + if (old_depth_texture.*) |*depth_texture| { depth_texture.deinit(engine); } if (old_swapchain != .null_handle) { - engine.device.destroySwapchainKHR(old_swapchain, &engine.vk_allocator.interface); + engine.destroySwapchain(old_swapchain); self.swapchain = .null_handle; } self.extent = .{ .width = 0, .height = 0 }; + self.image_index = null; if (old_semaphore_image_acquired != .null_handle) { engine.destroySemaphore(old_semaphore_image_acquired); self.semaphore_image_acquired = .null_handle; } + if (old_fence != .null_handle) { + engine.destroyFence(old_fence); + self.fence = .null_handle; + } + // --- CREATE DEPTH TEXTURE ------------------------------------------------ var new_depth_texture = try Texture.init(engine, .{ @@ -219,7 +211,7 @@ pub fn recreate(self: *Swapchain, engine: *Engine) !void { // --- CREATE NEW SWAPCHAIN IMAGES ----------------------------------------- const new_swapchain_images = blk: { - const images = try engine.device.getSwapchainImagesAllocKHR(new_swapchain, allocator); + const images = try engine.getSwapchainImagesAlloc(new_swapchain, allocator); defer allocator.free(images); var swapchain_images: std.ArrayList(SwapchainImage) = try .initCapacity(allocator, images.len); @@ -247,18 +239,17 @@ pub fn recreate(self: *Swapchain, engine: *Engine) !void { allocator.free(new_swapchain_images); } - // --- ACQUIRE NEXT IMAGE -------------------------------------------------- + // --- CREATE SEMAPHORES AND FENCES ---------------------------------------- - var semaphore_image_acquired = try engine.createSemaphore(); + const semaphore_image_acquired = try engine.createSemaphore(); errdefer engine.destroySemaphore(semaphore_image_acquired); engine.setObjectName(semaphore_image_acquired, "S Acquired[E]", .{}); - const res = try engine.device.acquireNextImageKHR(new_swapchain, std.math.maxInt(u64), semaphore_image_acquired, .null_handle); - if (res.result == .not_ready or res.result == .timeout) { - return error.ImageAcquireFailed; - } - - std.mem.swap(vk.Semaphore, &new_swapchain_images[res.image_index].semaphore_image_acquired, &semaphore_image_acquired); + const fence = try engine.createFence(.{ + .flags = .{ .signaled_bit = true }, + }); + errdefer engine.destroyFence(fence); + engine.setObjectName(fence, "F", .{}); // --- COMMIT -------------------------------------------------------------- @@ -266,8 +257,20 @@ pub fn recreate(self: *Swapchain, engine: *Engine) !void { self.swapchain = new_swapchain; self.swapchain_images = new_swapchain_images; self.depth_texture = new_depth_texture; - self.image_index = res.image_index; + self.image_index = null; self.semaphore_image_acquired = semaphore_image_acquired; + self.fence = fence; +} + +pub fn acquire(self: *Swapchain, engine: *Engine) !void { + try engine.waitForFence(self.fence); + try engine.resetFence(self.fence); + + const res = try engine.acquireNextImage(self.swapchain, .{ + .semaphore = self.semaphore_image_acquired, + }); + std.mem.swap(vk.Semaphore, &self.swapchain_images[res.image_index].semaphore_image_acquired, &self.semaphore_image_acquired); + self.image_index = res.image_index; } pub const PresentInfo = struct { @@ -275,20 +278,9 @@ pub const PresentInfo = struct { wait_semaphores: []const WaitSemaphore = &.{}, }; -pub fn present(self: *Swapchain, engine: *Engine, present_info: PresentInfo) !PresentResult { +pub fn present(self: *Swapchain, engine: *Engine, present_info: PresentInfo) !void { const allocator = engine.vk_allocator.allocator; - const device = engine.device; - const mode = &engine.mode.surface; - - // --- WAIT FOR CURRENT FRAME TO FINISH ------------------------------------ - - const current_swapchain_image = &self.swapchain_images[self.image_index]; - try engine.waitForFence(current_swapchain_image.fence); - if (current_swapchain_image.command_buffer) |*command_buffer| { - command_buffer.deinit(engine); - current_swapchain_image.command_buffer = null; - } - try engine.resetFence(current_swapchain_image.fence); + const current_swapchain_image = &self.swapchain_images[self.image_index.?]; // --- SUBMIT COMMAND BUFFER ----------------------------------------------- @@ -301,45 +293,24 @@ pub fn present(self: *Swapchain, engine: *Engine, present_info: PresentInfo) !Pr }); wait_semaphores.appendSliceAssumeCapacity(present_info.wait_semaphores); - current_swapchain_image.command_buffer = present_info.command_buffer; try present_info.command_buffer.submit(engine, .{ .wait_semaphores = wait_semaphores.items, .signal_semaphores = &.{current_swapchain_image.semaphore_render_finished}, - .fence = current_swapchain_image.fence, + .fence = self.fence, }); // --- PRESENT CURRENT FRAME ----------------------------------------------- - _ = device.queuePresentKHR(mode.presentation_queue.handle, &.{ - .wait_semaphore_count = 1, - .p_wait_semaphores = @ptrCast(¤t_swapchain_image.semaphore_render_finished), - .swapchain_count = 1, - .p_swapchains = @ptrCast(&self.swapchain), - .p_image_indices = @ptrCast(&self.image_index), - }) catch |err| switch (err) { - error.OutOfDateKHR => return .suboptimal, - else => return err, - }; - - // --- ACQUIRE NEXT FRAME -------------------------------------------------- - - const res = device.acquireNextImageKHR(self.swapchain, std.math.maxInt(u64), self.semaphore_image_acquired, .null_handle) catch |err| switch (err) { - error.OutOfDateKHR => return .suboptimal, - else => return err, - }; - std.mem.swap(vk.Semaphore, &self.swapchain_images[res.image_index].semaphore_image_acquired, &self.semaphore_image_acquired); - self.image_index = res.image_index; - - return switch (res.result) { - .success => .optimal, - .suboptimal_khr => .suboptimal, - else => unreachable, - }; + _ = try engine.queuePresent(.{ + .wait_semaphores = &.{current_swapchain_image.semaphore_render_finished}, + .swapchain = self.swapchain, + .image_index = self.image_index.?, + }); } fn getCurrentExtent(engine: *Engine) !vk.Extent2D { const mode = &engine.mode.surface; - const surface_capabilities = try engine.instance.getPhysicalDeviceSurfaceCapabilitiesKHR(engine.physical_device, mode.surface); + const surface_capabilities = try engine.getPhysicalDeviceSurfaceCapabilities(); if (surface_capabilities.current_extent.width != std.math.maxInt(u32) and surface_capabilities.current_extent.height != std.math.maxInt(u32)) { return surface_capabilities.current_extent; @@ -366,13 +337,12 @@ const Params = struct { image_count: u32, pub fn init(engine: *Engine) !Params { - const mode = &engine.mode.surface; const allocator = engine.vk_allocator.allocator; - const surface_capabilities = try engine.instance.getPhysicalDeviceSurfaceCapabilitiesKHR(engine.physical_device, mode.surface); + const surface_capabilities = try engine.getPhysicalDeviceSurfaceCapabilities(); const surface_format = blk: { - const surface_formats = try engine.instance.getPhysicalDeviceSurfaceFormatsAllocKHR(engine.physical_device, mode.surface, allocator); + const surface_formats = try engine.getPhysicalDeviceSurfaceFormatsAlloc(allocator); defer allocator.free(surface_formats); // Look for 8-bit BGRA sRGB surface format. @@ -409,9 +379,7 @@ const SwapchainImage = struct { image_view: vk.ImageView, semaphore_image_acquired: vk.Semaphore, semaphore_render_finished: vk.Semaphore, - fence: vk.Fence, framebuffer: vk.Framebuffer, - command_buffer: ?CommandBuffer, const InitProps = struct { image: vk.Image, @@ -447,12 +415,6 @@ const SwapchainImage = struct { errdefer engine.destroySemaphore(semaphore_render_finished); engine.setObjectName(image_view, "S Rendered[{d}]", .{props.index}); - const fence = try engine.createFence(.{ - .flags = .{ .signaled_bit = true }, - }); - errdefer engine.destroyFence(fence); - engine.setObjectName(image_view, "F Rendered[{d}]", .{props.index}); - const framebuffer = try engine.createFramebuffer(.{ .render_pass = props.render_pass, .attachments = &.{ image_view, props.depth_stencil_image_view }, @@ -468,21 +430,14 @@ const SwapchainImage = struct { .image_view = image_view, .semaphore_image_acquired = semaphore_image_acquired, .semaphore_render_finished = semaphore_render_finished, - .fence = fence, .framebuffer = framebuffer, - .command_buffer = null, }; } fn deinit(self: *SwapchainImage, engine: *Engine) void { std.log.scoped(.deinit).debug("Deinitializing {*} with {*}", .{ self, engine }); - engine.waitForFence(self.fence) catch {}; - if (self.command_buffer) |*command_buffer| { - command_buffer.deinit(engine); - } engine.destroyFramebuffer(self.framebuffer); - engine.destroyFence(self.fence); engine.destroySemaphore(self.semaphore_render_finished); engine.destroySemaphore(self.semaphore_image_acquired); engine.destroyImageView(self.image_view); diff --git a/src/main.zig b/src/main.zig index f388777..7fec816 100644 --- a/src/main.zig +++ b/src/main.zig @@ -100,7 +100,7 @@ pub fn main() !void { std.log.debug("Exitted the game loop", .{}); - for (swapchain.swapchain_images) |x| engine.waitForFence(x.fence) catch {}; + engine.waitForFence(swapchain.fence) catch {}; engine.deviceWaitIdle() catch {}; } diff --git a/src/math/Matrix4x4.zig b/src/math/Matrix4x4.zig index 38632c5..83cb57a 100644 --- a/src/math/Matrix4x4.zig +++ b/src/math/Matrix4x4.zig @@ -109,9 +109,9 @@ pub const Matrix4x4 = extern struct { const rotation_k = Vector4.init(2 * (xz + yw), 2 * (yz - xw), (1 - 2 * (xx + yy)), 0); return .initVector4( - Vector4.mulScalar(rotation_i, scale.getX()), - Vector4.mulScalar(rotation_j, scale.getY()), - Vector4.mulScalar(rotation_k, scale.getZ()), + .mulScalar(rotation_i, scale.getX()), + .mulScalar(rotation_j, scale.getY()), + .mulScalar(rotation_k, scale.getZ()), translation.asVector4(1), ); } @@ -130,47 +130,47 @@ pub const Matrix4x4 = extern struct { pub inline fn add(self: Matrix4x4, other: Matrix4x4) Matrix4x4 { return .initVector4( - Vector4.add(self.i, other.i), - Vector4.add(self.j, other.j), - Vector4.add(self.k, other.k), - Vector4.add(self.t, other.t), + .add(self.i, other.i), + .add(self.j, other.j), + .add(self.k, other.k), + .add(self.t, other.t), ); } pub inline fn sub(self: Matrix4x4, other: Matrix4x4) Matrix4x4 { return .initVector4( - Vector4.sub(self.i, other.i), - Vector4.sub(self.j, other.j), - Vector4.sub(self.k, other.k), - Vector4.sub(self.t, other.t), + .sub(self.i, other.i), + .sub(self.j, other.j), + .sub(self.k, other.k), + .sub(self.t, other.t), ); } pub inline fn negate(self: Matrix4x4) Matrix4x4 { return .initVector4( - Vector4.negate(self.i), - Vector4.negate(self.j), - Vector4.negate(self.k), - Vector4.negate(self.t), + .negate(self.i), + .negate(self.j), + .negate(self.k), + .negate(self.t), ); } pub inline fn mulScalar(self: Matrix4x4, scalar: f32) Matrix4x4 { return .initVector4( - Vector4.mulScalar(self.i, scalar), - Vector4.mulScalar(self.j, scalar), - Vector4.mulScalar(self.k, scalar), - Vector4.mulScalar(self.t, scalar), + .mulScalar(self.i, scalar), + .mulScalar(self.j, scalar), + .mulScalar(self.k, scalar), + .mulScalar(self.t, scalar), ); } pub inline fn divScalar(self: Matrix4x4, scalar: f32) Matrix4x4 { const inv_scalar = 1 / scalar; return .initVector4( - Vector4.mulScalar(self.i, inv_scalar), - Vector4.mulScalar(self.j, inv_scalar), - Vector4.mulScalar(self.k, inv_scalar), - Vector4.mulScalar(self.t, inv_scalar), + .mulScalar(self.i, inv_scalar), + .mulScalar(self.j, inv_scalar), + .mulScalar(self.k, inv_scalar), + .mulScalar(self.t, inv_scalar), ); } diff --git a/src/math/Quaternion.zig b/src/math/Quaternion.zig index 634b255..336a656 100644 --- a/src/math/Quaternion.zig +++ b/src/math/Quaternion.zig @@ -97,27 +97,27 @@ pub const Quaternion = extern struct { // --- COMPONENT-WISE --- pub inline fn add(self: Quaternion, other: Quaternion) Quaternion { - return .initVector4(Vector4.add(self.vector, other.vector)); + return .initVector4(.add(self.vector, other.vector)); } pub inline fn sub(self: Quaternion, other: Quaternion) Quaternion { - return .initVector4(Vector4.sub(self.vector, other.vector)); + return .initVector4(.sub(self.vector, other.vector)); } pub inline fn negate(self: Quaternion) Quaternion { - return .initVector4(Vector4.negate(self.vector)); + return .initVector4(.negate(self.vector)); } pub inline fn conjugate(self: Quaternion) Quaternion { - return .initVector4(Vector4.mul(self.vector, Vector4.init(-1, -1, -1, 1))); + return .initVector4(.mul(self.vector, .init(-1, -1, -1, 1))); } pub inline fn mulScalar(self: Quaternion, scalar: f32) Quaternion { - return .initVector4(Vector4.mulScalar(self.vector, scalar)); + return .initVector4(.mulScalar(self.vector, scalar)); } pub inline fn divScalar(self: Quaternion, scalar: f32) Quaternion { - return .initVector4(Vector4.divScalar(self.vector, scalar)); + return .initVector4(.divScalar(self.vector, scalar)); } // --- OTHER --- @@ -131,7 +131,7 @@ pub const Quaternion = extern struct { } pub inline fn normalize(self: Quaternion) Quaternion { - return .initVector4(Vector4.normalize(self.vector)); + return .initVector4(.normalize(self.vector)); } pub inline fn dot(self: Quaternion, other: Quaternion) f32 { @@ -139,7 +139,7 @@ pub const Quaternion = extern struct { } pub inline fn lerp(self: Quaternion, other: Quaternion, t: f32) Quaternion { - return .initVector4(Vector4.lerp(self.vector, other.vector, t)); + return .initVector4(.lerp(self.vector, other.vector, t)); } pub inline fn mulQuaternion(self: Quaternion, other: Quaternion) Quaternion { diff --git a/src/math/Vector3.zig b/src/math/Vector3.zig index 4711a24..7ff5a6b 100644 --- a/src/math/Vector3.zig +++ b/src/math/Vector3.zig @@ -157,19 +157,13 @@ pub const Vector3 = extern struct { return .{ .vector = self * s_vector + other * t_vector }; } - pub inline fn rotate(self: Vector3, quaternion: Quaternion) Vector2 { + pub inline fn rotate(self: Vector3, quaternion: Quaternion) Vector3 { const quaternion_scalar = quaternion.getScalar(); const quaternion_vector = quaternion.getVector(); - return Vector3.add( - self, - Vector3.cross( - Vector3.add(quaternion_vector, quaternion_vector), - Vector3.add( - Vector3.cross(quaternion_vector, self), - Vector3.mulScalar(self, quaternion_scalar), - ), - ), - ); + return .add(self, .cross( + .add(quaternion_vector, quaternion_vector), + .add(.cross(quaternion_vector, self), .mulScalar(self, quaternion_scalar)), + )); } };