From 2402f29742a0be71fabe7c8d26c3eb8d7bf59b01 Mon Sep 17 00:00:00 2001 From: David Allemang Date: Sat, 30 Mar 2024 00:41:48 -0400 Subject: [PATCH] incremental remove GraphicsContext --- src/main.zig | 279 ++++++++++++++++++++++++---------------------- src/swapchain.zig | 74 ++++++++---- 2 files changed, 198 insertions(+), 155 deletions(-) diff --git a/src/main.zig b/src/main.zig index 7d97f11..43eea52 100644 --- a/src/main.zig +++ b/src/main.zig @@ -2,8 +2,9 @@ const std = @import("std"); const vk = @import("vk"); const c = @import("c.zig"); const shaders = @import("shaders"); -const GraphicsContext = @import("graphics_context.zig").GraphicsContext; +// const GraphicsContext = @import("graphics_context.zig").GraphicsContext; const Swapchain = @import("swapchain.zig").Swapchain; +const Context = @import("swapchain.zig").Context; const Allocator = std.mem.Allocator; const gfx = @import("gfx.zig"); @@ -100,7 +101,7 @@ fn create_window(extent: vk.Extent2D, title: [*:0]const u8) !*c.GLFWwindow { ) orelse error.WindowInitFailed; } -const DevicePair = std.meta.Tuple(&.{ vk.PhysicalDevice, vk.Device, gfx.DeviceDispatch, vk.Queue }); +const DevicePair = std.meta.Tuple(&.{ vk.PhysicalDevice, vk.Device, gfx.DeviceDispatch, u32 }); /// note: destroy with vkd.destroyDevice(dev, null) fn create_device( @@ -195,9 +196,7 @@ fn create_device( const vkd = try gfx.DeviceDispatch.load(dev, vki.dispatch.vkGetDeviceProcAddr); errdefer vkd.destroyDevice(dev, null); - const queue = vkd.getDeviceQueue(dev, graphics_family, 0); - - return .{ pdev, dev, vkd, queue }; + return .{ pdev, dev, vkd, graphics_family }; } return error.NoSuitableDevice; @@ -229,142 +228,154 @@ pub fn main() !void { const surface = try create_surface(instance, window); defer vki.destroySurfaceKHR(instance, surface, null); - const pdev: vk.PhysicalDevice, const dev: vk.Device, const vkd: gfx.DeviceDispatch, const queue: vk.Queue = + const pdev: vk.PhysicalDevice, const dev: vk.Device, const vkd: gfx.DeviceDispatch, const family: u32 = try create_device(ally, instance, surface, vki); defer vkd.destroyDevice(dev, null); - var swapchain: vk.SwapchainKHR = .null_handle; - defer vkd.destroySwapchainKHR(dev, swapchain, null); + const queue = vkd.getDeviceQueue(dev, family, 0); - swapchain = try vkd.createSwapchainKHR(dev, &.{ + // var swapchain: vk.SwapchainKHR = .null_handle; + // defer vkd.destroySwapchainKHR(dev, swapchain, null); + // + // swapchain = try vkd.createSwapchainKHR(dev, &.{ + // .surface = surface, + // .min_image_count = 3, // todo compute + // .image_format = .r8g8b8a8_sint, // todo compute + // // .image_format = .r8g8b8a8_sint, // todo compute + // .image_color_space = .srgb_nonlinear_khr, // todo compute + // .image_extent = extent, // todo compute + // .image_array_layers = 1, + // .image_usage = .{ .color_attachment_bit = true, .transfer_dst_bit = true }, + // .image_sharing_mode = .exclusive, // since we only choose one queue family + // .pre_transform = .{ .identity_bit_khr = true }, // todo compute + // .composite_alpha = .{ .opaque_bit_khr = true }, + // .present_mode = .mailbox_khr, // todo compute + // .clipped = vk.TRUE, + // .old_swapchain = swapchain, + // }, null); + // + // _ = try vkd.queuePresentKHR(queue, &.{ + // .wait_semaphore_count = 0, + // .swapchain_count = 1, + // .p_swapchains = &[_]vk.SwapchainKHR{swapchain}, + // .p_image_indices = &[_]u32{0}, + // }); + // + // try vkd.deviceWaitIdle(dev); + // + // _ = pdev; + // extent = undefined; + + const gc: Context = .{ + .vki = vki, + .vkd = vkd, + .pdev = pdev, + .dev = dev, .surface = surface, - .min_image_count = 3, // todo compute - .image_format = .r8g8b8a8_sint, // todo compute - // .image_format = .r8g8b8a8_sint, // todo compute - .image_color_space = .srgb_nonlinear_khr, // todo compute - .image_extent = extent, // todo compute - .image_array_layers = 1, - .image_usage = .{ .color_attachment_bit = true, .transfer_dst_bit = true }, - .image_sharing_mode = .exclusive, // since we only choose one queue family - .pre_transform = .{ .identity_bit_khr = true }, // todo compute - .composite_alpha = .{ .opaque_bit_khr = true }, - .present_mode = .mailbox_khr, // todo compute - .clipped = vk.TRUE, - .old_swapchain = swapchain, + .queue = queue, + .family = family, + }; + + var swapchain = try Swapchain.init(&gc, ally, extent); + defer swapchain.deinit(); + + const pipeline_layout = try gc.vkd.createPipelineLayout(gc.dev, &.{ + .flags = .{}, + .set_layout_count = 0, + .p_set_layouts = undefined, + .push_constant_range_count = 0, + .p_push_constant_ranges = undefined, }, null); + defer gc.vkd.destroyPipelineLayout(gc.dev, pipeline_layout, null); - _ = try vkd.queuePresentKHR(queue, &.{ - .wait_semaphore_count = 0, - .swapchain_count = 1, - .p_swapchains = &[_]vk.SwapchainKHR{swapchain}, - .p_image_indices = &[_]u32{0}, - }); + const pipeline = try createPipeline(&gc, pipeline_layout, swapchain); + defer gc.vkd.destroyPipeline(gc.dev, pipeline, null); - try vkd.deviceWaitIdle(dev); + const pool = try gc.vkd.createCommandPool(gc.dev, &.{ + .queue_family_index = family, + }, null); + defer gc.vkd.destroyCommandPool(gc.dev, pool, null); - _ = pdev; - extent = undefined; + const vertex_buffer = try gc.vkd.createBuffer(gc.dev, &.{ + .size = @sizeOf(@TypeOf(vertices)), + .usage = .{ .transfer_dst_bit = true, .vertex_buffer_bit = true }, + .sharing_mode = .exclusive, + }, null); + defer gc.vkd.destroyBuffer(gc.dev, vertex_buffer, null); + const vertex_mem_reqs = gc.vkd.getBufferMemoryRequirements(gc.dev, vertex_buffer); + const vertex_memory = try gc.allocate(vertex_mem_reqs, .{ .device_local_bit = true }); + defer gc.vkd.freeMemory(gc.dev, vertex_memory, null); + try gc.vkd.bindBufferMemory(gc.dev, vertex_buffer, vertex_memory, 0); - // var swapchain = try Swapchain.init(&gc, ally, extent); - // defer swapchain.deinit(); + try uploadData(Vertex, &gc, pool, vertex_buffer, &vertices); - // const pipeline_layout = try gc.vkd.createPipelineLayout(gc.dev, &.{ - // .flags = .{}, - // .set_layout_count = 0, - // .p_set_layouts = undefined, - // .push_constant_range_count = 0, - // .p_push_constant_ranges = undefined, - // }, null); - // defer gc.vkd.destroyPipelineLayout(gc.dev, pipeline_layout, null); - // - // const pipeline = try createPipeline(&gc, pipeline_layout, swapchain); - // defer gc.vkd.destroyPipeline(gc.dev, pipeline, null); - // - // const pool = try gc.vkd.createCommandPool(gc.dev, &.{ - // .queue_family_index = gc.graphics_queue.family, - // }, null); - // defer gc.vkd.destroyCommandPool(gc.dev, pool, null); - // - // const vertex_buffer = try gc.vkd.createBuffer(gc.dev, &.{ - // .size = @sizeOf(@TypeOf(vertices)), - // .usage = .{ .transfer_dst_bit = true, .vertex_buffer_bit = true }, - // .sharing_mode = .exclusive, - // }, null); - // defer gc.vkd.destroyBuffer(gc.dev, vertex_buffer, null); - // const vertex_mem_reqs = gc.vkd.getBufferMemoryRequirements(gc.dev, vertex_buffer); - // const vertex_memory = try gc.allocate(vertex_mem_reqs, .{ .device_local_bit = true }); - // defer gc.vkd.freeMemory(gc.dev, vertex_memory, null); - // try gc.vkd.bindBufferMemory(gc.dev, vertex_buffer, vertex_memory, 0); - // - // try uploadData(Vertex, &gc, pool, vertex_buffer, &vertices); - // - // const index_buffer = try gc.vkd.createBuffer(gc.dev, &.{ - // .size = @sizeOf(@TypeOf(indices)), - // .usage = .{ .transfer_dst_bit = true, .index_buffer_bit = true }, - // .sharing_mode = .exclusive, - // }, null); - // defer gc.vkd.destroyBuffer(gc.dev, index_buffer, null); - // const index_mem_reqs = gc.vkd.getBufferMemoryRequirements(gc.dev, index_buffer); - // const index_memory = try gc.allocate(index_mem_reqs, .{ .device_local_bit = true }); - // defer gc.vkd.freeMemory(gc.dev, index_memory, null); - // try gc.vkd.bindBufferMemory(gc.dev, index_buffer, index_memory, 0); - // - // try uploadData(Index, &gc, pool, index_buffer, &indices); - // - // var cmdbufs = try createCommandBuffers( - // &gc, - // pool, - // ally, - // vertex_buffer, - // index_buffer, - // pipeline, - // swapchain, - // ); - // defer destroyCommandBuffers(&gc, pool, ally, cmdbufs); - // - // while (c.glfwWindowShouldClose(window) == c.GLFW_FALSE) { - // var w: c_int = undefined; - // var h: c_int = undefined; - // c.glfwGetFramebufferSize(window, &w, &h); - // - // // Don't present or resize swapchain while the window is minimized - // if (w == 0 or h == 0) { - // c.glfwPollEvents(); - // continue; - // } - // - // const cmdbuf = cmdbufs[swapchain.image_index]; - // - // const state = swapchain.present(cmdbuf) catch |err| switch (err) { - // error.OutOfDateKHR => Swapchain.PresentState.suboptimal, - // else => |narrow| return narrow, - // }; - // - // if (state == .suboptimal or extent.width != @as(u32, @intCast(w)) or extent.height != @as(u32, @intCast(h))) { - // extent.width = @intCast(w); - // extent.height = @intCast(h); - // try swapchain.recreate(extent); - // - // destroyCommandBuffers(&gc, pool, ally, cmdbufs); - // cmdbufs = try createCommandBuffers( - // &gc, - // pool, - // ally, - // vertex_buffer, - // index_buffer, - // pipeline, - // swapchain, - // ); - // } - // - // c.glfwPollEvents(); - // } - // - // try swapchain.waitForAllFences(); - // try gc.vkd.deviceWaitIdle(gc.dev); + const index_buffer = try gc.vkd.createBuffer(gc.dev, &.{ + .size = @sizeOf(@TypeOf(indices)), + .usage = .{ .transfer_dst_bit = true, .index_buffer_bit = true }, + .sharing_mode = .exclusive, + }, null); + defer gc.vkd.destroyBuffer(gc.dev, index_buffer, null); + const index_mem_reqs = gc.vkd.getBufferMemoryRequirements(gc.dev, index_buffer); + const index_memory = try gc.allocate(index_mem_reqs, .{ .device_local_bit = true }); + defer gc.vkd.freeMemory(gc.dev, index_memory, null); + try gc.vkd.bindBufferMemory(gc.dev, index_buffer, index_memory, 0); + + try uploadData(Index, &gc, pool, index_buffer, &indices); + + var cmdbufs = try createCommandBuffers( + &gc, + pool, + ally, + vertex_buffer, + index_buffer, + pipeline, + swapchain, + ); + defer destroyCommandBuffers(&gc, pool, ally, cmdbufs); + + while (c.glfwWindowShouldClose(window) == c.GLFW_FALSE) { + var w: c_int = undefined; + var h: c_int = undefined; + c.glfwGetFramebufferSize(window, &w, &h); + + // Don't present or resize swapchain while the window is minimized + if (w == 0 or h == 0) { + c.glfwPollEvents(); + continue; + } + + const cmdbuf = cmdbufs[swapchain.image_index]; + + const state = swapchain.present(cmdbuf) catch |err| switch (err) { + error.OutOfDateKHR => Swapchain.PresentState.suboptimal, + else => |narrow| return narrow, + }; + + if (state == .suboptimal or extent.width != @as(u32, @intCast(w)) or extent.height != @as(u32, @intCast(h))) { + extent.width = @intCast(w); + extent.height = @intCast(h); + try swapchain.recreate(extent); + + destroyCommandBuffers(&gc, pool, ally, cmdbufs); + cmdbufs = try createCommandBuffers( + &gc, + pool, + ally, + vertex_buffer, + index_buffer, + pipeline, + swapchain, + ); + } + + c.glfwPollEvents(); + } + + try swapchain.waitForAllFences(); + try gc.vkd.deviceWaitIdle(gc.dev); } -fn uploadData(comptime T: type, gc: *const GraphicsContext, pool: vk.CommandPool, buffer: vk.Buffer, source: []const T) !void { +fn uploadData(comptime T: type, gc: *const Context, pool: vk.CommandPool, buffer: vk.Buffer, source: []const T) !void { // if (@typeInfo(T) == .Struct and @typeInfo(T).Struct.layout == .auto) @compileError("Requires defined T layout"); const size = @sizeOf(T) * source.len; @@ -396,7 +407,7 @@ fn uploadData(comptime T: type, gc: *const GraphicsContext, pool: vk.CommandPool try copyBuffer(gc, pool, buffer, staging_buffer, size); } -fn copyBuffer(gc: *const GraphicsContext, pool: vk.CommandPool, dst: vk.Buffer, src: vk.Buffer, size: vk.DeviceSize) !void { +fn copyBuffer(gc: *const Context, pool: vk.CommandPool, dst: vk.Buffer, src: vk.Buffer, size: vk.DeviceSize) !void { var cmdbuf: vk.CommandBuffer = undefined; try gc.vkd.allocateCommandBuffers(gc.dev, &.{ .command_pool = pool, @@ -426,12 +437,12 @@ fn copyBuffer(gc: *const GraphicsContext, pool: vk.CommandPool, dst: vk.Buffer, // creating and submitting a queue for every copy operation seems a bad idea for "streamed" data // gonna want a way to send a copy operation WITH SYNCHRONIZATION PRIMITIVES on a particular queue // see https://stackoverflow.com/a/62183243 - try gc.vkd.queueSubmit(gc.graphics_queue.handle, 1, @ptrCast(&si), .null_handle); - try gc.vkd.queueWaitIdle(gc.graphics_queue.handle); + try gc.vkd.queueSubmit(gc.queue, 1, @ptrCast(&si), .null_handle); + try gc.vkd.queueWaitIdle(gc.queue); } fn createCommandBuffers( - gc: *const GraphicsContext, + gc: *const Context, pool: vk.CommandPool, allocator: Allocator, vertex_buffer: vk.Buffer, @@ -512,12 +523,12 @@ fn createCommandBuffers( return cmdbufs; } -fn destroyCommandBuffers(gc: *const GraphicsContext, pool: vk.CommandPool, allocator: Allocator, cmdbufs: []vk.CommandBuffer) void { +fn destroyCommandBuffers(gc: *const Context, pool: vk.CommandPool, allocator: Allocator, cmdbufs: []vk.CommandBuffer) void { gc.vkd.freeCommandBuffers(gc.dev, pool, @truncate(cmdbufs.len), cmdbufs.ptr); allocator.free(cmdbufs); } -fn createPipeline(gc: *const GraphicsContext, layout: vk.PipelineLayout, swapchain: Swapchain) !vk.Pipeline { +fn createPipeline(gc: *const Context, layout: vk.PipelineLayout, swapchain: Swapchain) !vk.Pipeline { const vert = try gc.vkd.createShaderModule(gc.dev, &.{ .code_size = shaders.triangle_vert.len, .p_code = @as([*]const u32, @ptrCast(&shaders.triangle_vert)), diff --git a/src/swapchain.zig b/src/swapchain.zig index 09519ff..13d2ce0 100644 --- a/src/swapchain.zig +++ b/src/swapchain.zig @@ -1,15 +1,55 @@ const std = @import("std"); const vk = @import("vk"); -const GraphicsContext = @import("graphics_context.zig").GraphicsContext; +const gfx = @import("gfx.zig"); const Allocator = std.mem.Allocator; +pub const Context = struct { + vki: gfx.InstanceDispatch, + vkd: gfx.DeviceDispatch, + + pdev: vk.PhysicalDevice, + dev: vk.Device, + + surface: vk.SurfaceKHR, + + queue: vk.Queue, + family: u32, + + pub fn findMemoryTypeIndex( + self: @This(), + memory_type_bits: u32, + flags: vk.MemoryPropertyFlags, + ) !u32 { + const mem_props = self.vki.getPhysicalDeviceMemoryProperties(self.pdev); + + for (mem_props.memory_types[0..mem_props.memory_type_count], 0..) |mem_type, i| { + if (memory_type_bits & (@as(u32, 1) << @truncate(i)) != 0 and mem_type.property_flags.contains(flags)) { + return @truncate(i); + } + } + + return error.NoSuitableMemoryType; + } + + pub fn allocate( + self: @This(), + requirements: vk.MemoryRequirements, + flags: vk.MemoryPropertyFlags, + ) !vk.DeviceMemory { + return try self.vkd.allocateMemory(self.dev, &.{ + .allocation_size = requirements.size, + .memory_type_index = try self.findMemoryTypeIndex(requirements.memory_type_bits, flags), + }, null); + } +}; + pub const Swapchain = struct { pub const PresentState = enum { optimal, suboptimal, }; - gc: *const GraphicsContext, + gc: *const Context, allocator: Allocator, surface_format: vk.SurfaceFormatKHR, @@ -21,11 +61,11 @@ pub const Swapchain = struct { image_index: u32, next_image_acquired: vk.Semaphore, - pub fn init(gc: *const GraphicsContext, allocator: Allocator, extent: vk.Extent2D) !Swapchain { + pub fn init(gc: *const Context, allocator: Allocator, extent: vk.Extent2D) !Swapchain { return try initRecycle(gc, allocator, extent, .null_handle); } - pub fn initRecycle(gc: *const GraphicsContext, allocator: Allocator, extent: vk.Extent2D, old_handle: vk.SwapchainKHR) !Swapchain { + pub fn initRecycle(gc: *const Context, allocator: Allocator, extent: vk.Extent2D, old_handle: vk.SwapchainKHR) !Swapchain { const caps = try gc.vki.getPhysicalDeviceSurfaceCapabilitiesKHR(gc.pdev, gc.surface); const actual_extent = findActualExtent(caps, extent); if (actual_extent.width == 0 or actual_extent.height == 0) { @@ -40,12 +80,6 @@ pub const Swapchain = struct { image_count = @min(image_count, caps.max_image_count); } - const qfi = [_]u32{ gc.graphics_queue.family, gc.present_queue.family }; - const sharing_mode: vk.SharingMode = if (gc.graphics_queue.family != gc.present_queue.family) - .concurrent - else - .exclusive; - const handle = try gc.vkd.createSwapchainKHR(gc.dev, &.{ .surface = gc.surface, .min_image_count = image_count, @@ -54,9 +88,7 @@ pub const Swapchain = struct { .image_extent = actual_extent, .image_array_layers = 1, .image_usage = .{ .color_attachment_bit = true, .transfer_dst_bit = true }, - .image_sharing_mode = sharing_mode, - .queue_family_index_count = qfi.len, - .p_queue_family_indices = &qfi, + .image_sharing_mode = .exclusive, .pre_transform = caps.current_transform, .composite_alpha = .{ .opaque_bit_khr = true }, .present_mode = present_mode, @@ -154,7 +186,7 @@ pub const Swapchain = struct { // Step 2: Submit the command buffer const wait_stage = [_]vk.PipelineStageFlags{.{ .top_of_pipe_bit = true }}; - try self.gc.vkd.queueSubmit(self.gc.graphics_queue.handle, 1, &[_]vk.SubmitInfo{.{ + try self.gc.vkd.queueSubmit(self.gc.queue, 1, &[_]vk.SubmitInfo{.{ .wait_semaphore_count = 1, .p_wait_semaphores = @ptrCast(¤t.image_acquired), .p_wait_dst_stage_mask = &wait_stage, @@ -165,7 +197,7 @@ pub const Swapchain = struct { }}, current.frame_fence); // Step 3: Present the current frame - _ = try self.gc.vkd.queuePresentKHR(self.gc.present_queue.handle, &.{ + _ = try self.gc.vkd.queuePresentKHR(self.gc.queue, &.{ .wait_semaphore_count = 1, .p_wait_semaphores = @as([*]const vk.Semaphore, @ptrCast(¤t.render_finished)), .swapchain_count = 1, @@ -200,7 +232,7 @@ const SwapImage = struct { render_finished: vk.Semaphore, frame_fence: vk.Fence, - fn init(gc: *const GraphicsContext, image: vk.Image, format: vk.Format) !SwapImage { + fn init(gc: *const Context, image: vk.Image, format: vk.Format) !SwapImage { const view = try gc.vkd.createImageView(gc.dev, &.{ .image = image, .view_type = .@"2d", @@ -234,7 +266,7 @@ const SwapImage = struct { }; } - fn deinit(self: SwapImage, gc: *const GraphicsContext) void { + fn deinit(self: SwapImage, gc: *const Context) void { self.waitForFence(gc) catch return; gc.vkd.destroyImageView(gc.dev, self.view, null); gc.vkd.destroySemaphore(gc.dev, self.image_acquired, null); @@ -242,12 +274,12 @@ const SwapImage = struct { gc.vkd.destroyFence(gc.dev, self.frame_fence, null); } - fn waitForFence(self: SwapImage, gc: *const GraphicsContext) !void { + fn waitForFence(self: SwapImage, gc: *const Context) !void { _ = try gc.vkd.waitForFences(gc.dev, 1, @ptrCast(&self.frame_fence), vk.TRUE, std.math.maxInt(u64)); } }; -fn initSwapchainImages(gc: *const GraphicsContext, swapchain: vk.SwapchainKHR, format: vk.Format, allocator: Allocator) ![]SwapImage { +fn initSwapchainImages(gc: *const Context, swapchain: vk.SwapchainKHR, format: vk.Format, allocator: Allocator) ![]SwapImage { var count: u32 = undefined; _ = try gc.vkd.getSwapchainImagesKHR(gc.dev, swapchain, &count, null); const images = try allocator.alloc(vk.Image, count); @@ -268,7 +300,7 @@ fn initSwapchainImages(gc: *const GraphicsContext, swapchain: vk.SwapchainKHR, f return swap_images; } -fn findSurfaceFormat(gc: *const GraphicsContext, allocator: Allocator) !vk.SurfaceFormatKHR { +fn findSurfaceFormat(gc: *const Context, allocator: Allocator) !vk.SurfaceFormatKHR { const preferred = vk.SurfaceFormatKHR{ .format = .b8g8r8a8_srgb, .color_space = .srgb_nonlinear_khr, @@ -289,7 +321,7 @@ fn findSurfaceFormat(gc: *const GraphicsContext, allocator: Allocator) !vk.Surfa return surface_formats[0]; // There must always be at least one supported surface format } -fn findPresentMode(gc: *const GraphicsContext, allocator: Allocator) !vk.PresentModeKHR { +fn findPresentMode(gc: *const Context, allocator: Allocator) !vk.PresentModeKHR { var count: u32 = undefined; _ = try gc.vki.getPhysicalDeviceSurfacePresentModesKHR(gc.pdev, gc.surface, &count, null); const present_modes = try allocator.alloc(vk.PresentModeKHR, count);