From 03162f6f0bac0bc8c90167c0bf43733fb477abad Mon Sep 17 00:00:00 2001 From: Vivianne Langdon Date: Fri, 22 Jul 2022 01:30:43 -0700 Subject: [PATCH] looping with validation errors --- src/rendering/vulkan/Buffer.zig | 305 ++++++++++++++++++++++++++--- src/rendering/vulkan/Command.zig | 5 +- src/rendering/vulkan/MainPass.zig | 34 ++-- src/rendering/vulkan/Pass.zig | 3 +- src/rendering/vulkan/Renderer.zig | 4 +- src/rendering/vulkan/image.zig | 12 +- src/rendering/vulkan/memory.zig | 76 ++++++- src/rendering/vulkan/pipeline.zig | 2 +- src/rendering/vulkan/submit_id.zig | 9 +- src/rendering/vulkan/sync.zig | 3 +- 10 files changed, 386 insertions(+), 67 deletions(-) diff --git a/src/rendering/vulkan/Buffer.zig b/src/rendering/vulkan/Buffer.zig index 7b99aa2..7aca5eb 100644 --- a/src/rendering/vulkan/Buffer.zig +++ b/src/rendering/vulkan/Buffer.zig @@ -2,6 +2,7 @@ const std = @import("std"); const assert = std.debug.assert; const vk = @import("vulkan"); const vma = @import("vma"); +const time = @import("../../common/time.zig"); const ProfileMark = @import("../../common/profiler.zig").ProfileMark; const Device = @import("device.zig").Device; @@ -10,7 +11,7 @@ const settings = @import("settings.zig"); const Command = @import("Command.zig"); const SubmitId = @import("submit_id.zig").SubmitId; const memory = @import("memory.zig"); -const QueueId = @import("queues.zig").QueueId; +const queues = @import("queues.zig"); const Buffer = @This(); @@ -27,16 +28,19 @@ pub fn init(size: usize, usage: vk.BufferUsageFlags, mem_usage: vma.MemoryUsage) pub fn release(self: *Buffer, device: *const Device) !void { if (self.handle != .null_handle) { - try memory.Releasable.init(&.{ - // .frame = get frame count - .submit_id = try self.getSubmit(), - .object = .{ + const id = try self.getSubmit(); + const frame_count = time.getFrameCount(); + _ = try memory.Releasable.init( + device, + frame_count, + id, + .{ .buffer = .{ .handle = self.handle, .allocation = self.allocation, }, }, - }, device); + ); } self.* = std.mem.zeroes(Buffer); } @@ -77,44 +81,44 @@ pub fn unmapRead(self: *const Buffer, device: *const Device) void { memory.unmap(device, self.allocation); } -pub fn mapWrite(self: *const Buffer, device: *const Device) !anyopaque { +pub fn mapWrite(self: *Buffer, device: *const Device) ![*]u8 { if (self.state.stage.toInt() == 0) { // newly created buffer self.state.stage = .{ .host_bit = true }; self.state.access = .{ .host_write_bit = true }; } - if (!self.state.stage.host_bit || !self.state.access.host_write_bit) { + if (!self.state.stage.host_bit or !self.state.access.host_write_bit) { // kara claims this sucks but she's lazy - var cmd = try Command.Buffer.get(.graphics); + var cmd = try Command.Buffer.get(.graphics, device); _ = try State.hostWrite(device, cmd, self); - const submit_id = try cmd.submit(device, .null_handle, .{}, .null_handle); + const submit_id = try cmd.submit(device, null, .{}, null); try submit_id.wait(device); - cmd = try Command.Buffer.get(.graphics); - assert(cmd != .null_handle); + cmd = try Command.Buffer.get(.graphics, device); + assert(cmd.handle != .null_handle); } assert(self.state.stage.host_bit); assert(self.state.access.host_write_bit); - return memory.map(device, self.allocation); + return try memory.map(self.allocation); } -pub fn unmapWrite(self: *const Buffer, device: *const Device) void { +pub fn unmapWrite(self: *const Buffer) void { assert(self.state.stage.host_bit); assert(self.state.access.host_write_bit); - memory.unmap(device, self.allocation); - memory.flush(device, self.allocation); + memory.unmap(self.allocation); + memory.flush(self.allocation); } const pm_write = ProfileMark.init("Buffer.write"); -pub fn write(self: *const Buffer, device: *const Device, src: []const u8) !void { +pub fn write(self: *Buffer, device: *const Device, src: []const u8) !void { pm_write.begin(); defer pm_write.end(); assert(src.len <= self.size); - const dst = try self.mapWrite(device); - defer self.unmapWrite(device); - @memcpy(dst, src, src.len); + var dst = try self.mapWrite(device); + defer self.unmapWrite(); + @memcpy(dst, src.ptr, src.len); } const pm_read = ProfileMark.init("Buffer.read"); @@ -140,14 +144,265 @@ pub fn getSubmit(self: *const Buffer) !SubmitId { return id; } - return SubmitId{}; + return error.InvalidBufferHandle; } pub const State = struct { - owner: QueueId, - cmd_id: u32, + owner: queues.QueueId, + cmd_id: u32 = 0, stage: vk.PipelineStageFlags, access: vk.AccessFlags, + + pub fn hostRead(device: *const Device, cmdbuf: *const Command.Buffer, buf: *Buffer) !bool { + assert(cmdbuf.handle != .null_handle); + assert(cmdbuf.xfer); + const state = State{ + .owner = cmdbuf.queue_id, + .stage = .{ .host_bit = true }, + .access = .{ .host_read_bit = true }, + }; + return try state.handle(device, buf); + } + + pub fn hostWrite(device: *const Device, cmdbuf: *const Command.Buffer, buf: *Buffer) !bool { + assert(cmdbuf.handle != .null_handle); + assert(cmdbuf.xfer); + const state = State{ + .owner = cmdbuf.queue_id, + .stage = .{ .host_bit = true }, + .access = .{ .host_write_bit = true }, + }; + return try state.handle(device, buf); + } + + pub fn transferSrc(device: *const Device, cmdbuf: *const Command.Buffer, buf: *Buffer) !bool { + assert(cmdbuf.handle != .null_handle); + assert(cmdbuf.xfer); + const state = State{ + .owner = cmdbuf.queue_id, + .stage = .{ .transfer_bit = true }, + .access = .{ .transfer_read_bit = true }, + }; + return try state.handle(device, buf); + } + + pub fn transferDst(device: *const Device, cmdbuf: *const Command.Buffer, buf: *Buffer) !bool { + assert(cmdbuf.handle != .null_handle); + assert(cmdbuf.xfer); + const state = State{ + .owner = cmdbuf.queue_id, + .stage = .{ .transfer_bit = true }, + .access = .{ .transfer_write_bit = true }, + }; + return try state.handle(device, buf); + } + + pub fn uniformBuffer(device: *const Device, cmdbuf: *const Command.Buffer, buf: *Buffer) !bool { + assert(cmdbuf.handle != .null_handle); + assert(cmdbuf.gfx or cmdbuf.comp); + const state = State{ + .owner = cmdbuf.queue_id, + .stage = .{ .vertex_shader_bit = true, .fragment_shader_bit = true, .compute_shader_bit = true }, + .access = .{ .uniform_read_bit = true }, + }; + return try state.handle(device, buf); + } + + pub fn indirectDraw(device: *const Device, cmdbuf: *const Command.Buffer, buf: *Buffer) !bool { + assert(cmdbuf.handle != .null_handle); + assert(cmdbuf.gfx); + const state = State{ + .owner = cmdbuf.queue_id, + .stage = .{ .draw_indirect_bit = true }, + .access = .{ .indirect_command_read_bit = true }, + }; + return try state.handle(device, buf); + } + + pub fn indirectDispatch(device: *const Device, cmdbuf: *const Command.Buffer, buf: *Buffer) !bool { + assert(cmdbuf.handle != .null_handle); + assert(cmdbuf.comp); + const state = State{ + .owner = cmdbuf.queue_id, + // kara was not sure if draw indirect stage is needed here or not + .stage = .{ .draw_indirect_bit = true, .compute_shader_bit = true }, + .access = .{ .indirect_command_read_bit = true }, + }; + return try state.handle(device, buf); + } + + pub fn vertexBuffer(device: *const Device, cmdbuf: *const Command.Buffer, buf: *Buffer) !bool { + assert(cmdbuf.handle != .null_handle); + assert(cmdbuf.gfx); + const state = State{ + .owner = cmdbuf.queue_id, + .stage = .{ .vertex_input_bit = true }, + .access = .{ .vertex_attribute_read_bit = true }, + }; + return try state.handle(device, buf); + } + + pub fn indexBuffer(device: *const Device, cmdbuf: *const Command.Buffer, buf: *Buffer) !bool { + assert(cmdbuf.handle != .null_handle); + assert(cmdbuf.gfx); + const state = State{ + .owner = cmdbuf.queue_id, + .stage = .{ .vertex_input_bit = true }, + .access = .{ .index_read_bit = true }, + }; + return try state.handle(device, buf); + } + + pub fn fragLoad(device: *const Device, cmdbuf: *const Command.Buffer, buf: *Buffer) !bool { + assert(cmdbuf.handle != .null_handle); + assert(cmdbuf.gfx); + const state = State{ + .owner = cmdbuf.queue_id, + .stage = .{ .fragment_shader_bit = true }, + .access = .{ .shader_read_bit = true }, + }; + return try state.handle(device, buf); + } + + pub fn fragStore(device: *const Device, cmdbuf: *const Command.Buffer, buf: *Buffer) !bool { + assert(cmdbuf.handle != .null_handle); + assert(cmdbuf.gfx); + const state = State{ + .owner = cmdbuf.queue_id, + .stage = .{ .fragment_shader_bit = true }, + .access = .{ .shader_write_bit = true }, + }; + return try state.handle(device, buf); + } + + pub fn fragLoadStore(device: *const Device, cmdbuf: *const Command.Buffer, buf: *Buffer) !bool { + assert(cmdbuf.handle != .null_handle); + assert(cmdbuf.gfx); + const state = State{ + .owner = cmdbuf.queue_id, + .stage = .{ .fragment_shader_bit = true }, + .access = .{ .shader_read_bit = true, .shader_write_bit = true }, + }; + return try state.handle(device, buf); + } + + pub fn computeLoad(device: *const Device, cmdbuf: *const Command.Buffer, buf: *Buffer) !bool { + assert(cmdbuf.handle != .null_handle); + assert(cmdbuf.comp); + const state = State{ + .owner = cmdbuf.queue_id, + .stage = .{ .compute_shader_bit = true }, + .access = .{ .shader_read_bit = true }, + }; + return try state.handle(device, buf); + } + + pub fn computeStore(device: *const Device, cmdbuf: *const Command.Buffer, buf: *Buffer) !bool { + assert(cmdbuf.handle != .null_handle); + assert(cmdbuf.comp); + const state = State{ + .owner = cmdbuf.queue_id, + .stage = .{ .compute_shader_bit = true }, + .access = .{ .shader_write_bit = true }, + }; + return try state.handle(device, buf); + } + + pub fn computeLoadStore(device: *const Device, cmdbuf: *const Command.Buffer, buf: *Buffer) !bool { + assert(cmdbuf.handle != .null_handle); + assert(cmdbuf.comp); + const state = State{ + .owner = cmdbuf.queue_id, + .stage = .{ .compute_shader_bit = true }, + .access = .{ .shader_read_bit = true, .shader_write_bit = true }, + }; + return try state.handle(device, buf); + } + fn handle(self: *const State, device: *const Device, buf: *Buffer) !bool { + assert(buf.handle != .null_handle); + assert(self.access.toInt() != 0); + assert(self.stage.toInt() != 0); + + var inserted_barrier = false; + const prev = &buf.state; + const prev_queue = queues.get(prev.owner); + const next_queue = queues.get(self.owner); + + assert(self.stage.intersect(next_queue.stage_mask).toInt() == self.stage.toInt()); + assert(self.access.intersect(next_queue.access_mask).toInt() == self.access.toInt()); + + var new_resource = false; + if (prev.stage.toInt() == 0) { + prev.stage = .{ .top_of_pipe_bit = true }; + new_resource = true; + } + + if (prev_queue != next_queue) { + // queue ownership transfer + var src_cmd = try Command.Buffer.get(prev.owner, device); + var dst_cmd = try Command.Buffer.get(self.owner, device); + if (src_cmd.queue_transfer_dst or dst_cmd.queue_transfer_src) { + try Command.Buffer.flushQueueTransfers(device); + } + + const barrier = vk.BufferMemoryBarrier{ + .src_access_mask = prev.access, + .dst_access_mask = self.access, + .src_queue_family_index = prev_queue.family, + .dst_queue_family_index = next_queue.family, + .buffer = buf.handle, + .offset = 0, + .size = vk.WHOLE_SIZE, + }; + + assert(!src_cmd.queue_transfer_dst); + src_cmd.queue_transfer_src = true; + device.dispatch.cmdPipelineBarrier(src_cmd.handle, prev.stage, self.stage, .{}, 0, undefined, 1, @ptrCast([*]const vk.BufferMemoryBarrier, &barrier), 0, undefined); + assert(!dst_cmd.queue_transfer_src); + dst_cmd.queue_transfer_dst = true; + device.dispatch.cmdPipelineBarrier(dst_cmd.handle, prev.stage, self.stage, .{}, 0, undefined, 1, @ptrCast([*]const vk.BufferMemoryBarrier, &barrier), 0, undefined); + prev.* = self.*; + inserted_barrier = true; + } else { + var need_barrier = false; + const host_only = prev.stage.host_bit and self.stage.host_bit; + if (!host_only) { + const src_read = prev.access.intersect(Command.read_access).toInt() != 0; + const src_write = prev.access.intersect(Command.write_access).toInt() != 0; + const dst_read = self.access.intersect(Command.read_access).toInt() != 0; + const dst_write = self.access.intersect(Command.write_access).toInt() != 0; + const read_after_write = src_write and dst_read; + const write_after_read = src_read and dst_write; + const write_after_write = src_write and dst_write; + need_barrier = read_after_write or write_after_read or write_after_write or new_resource; + } + + if (need_barrier) { + // data hazard, insert barrier + const cmd = try Command.Buffer.get(self.owner, device); + assert(!cmd.in_render_pass); + const barrier = vk.BufferMemoryBarrier{ + .src_access_mask = prev.access, + .dst_access_mask = self.access, + .src_queue_family_index = vk.QUEUE_FAMILY_IGNORED, + .dst_queue_family_index = vk.QUEUE_FAMILY_IGNORED, + .buffer = buf.handle, + .offset = 0, + .size = vk.WHOLE_SIZE, + }; + device.dispatch.cmdPipelineBarrier(cmd.handle, prev.stage, self.stage, .{}, 0, undefined, 1, @ptrCast([*]const vk.BufferMemoryBarrier, &barrier), 0, undefined); + prev.* = self.*; + inserted_barrier = true; + } else { + // no data hazard, append usage state + prev.stage = prev.stage.merge(self.stage); + prev.access = prev.access.merge(self.access); + } + } + + (try Command.Buffer.get(self.owner, device)).touchBuffer(buf); + return inserted_barrier; + } }; pub const Set = struct { @@ -173,7 +428,7 @@ pub const Set = struct { return try self.current(swapchain).reserve(device, size, usage, mem_usage); } - pub fn write(self: *const Set, device: *const Device, swapchain: *const Swapchain, src: []const u8) !void { + pub fn write(self: *Set, device: *const Device, swapchain: *const Swapchain, src: []const u8) !void { try self.current(swapchain).write(device, src); } @@ -181,7 +436,7 @@ pub const Set = struct { try self.current(swapchain).read(device, dst); } - pub fn current(self: *const Set, swapchain: *const Swapchain) *const Buffer { + pub fn current(self: *Set, swapchain: *const Swapchain) *Buffer { const sync_index = swapchain.getSyncIndex(); assert(sync_index < self.frames.len); return &self.frames[sync_index]; diff --git a/src/rendering/vulkan/Command.zig b/src/rendering/vulkan/Command.zig index 4b928cd..53254a3 100644 --- a/src/rendering/vulkan/Command.zig +++ b/src/rendering/vulkan/Command.zig @@ -15,6 +15,7 @@ const SubmitId = @import("submit_id.zig").SubmitId; const Fence = @import("sync.zig").Fence; const Semaphore = @import("sync.zig").Semaphore; const Bindings = @import("Bindings.zig"); +const BufferBuffer = @import("Buffer.zig"); const queues = @import("queues.zig"); @@ -304,7 +305,7 @@ pub const Buffer = struct { device.dispatch.cmdDraw(self.handle, vertex_count, 1, first_vertex, 0); } - pub fn touchBuffer(self: *Self, buf: *const Buffer) void { + pub fn touchBuffer(self: *Self, buf: *BufferBuffer) void { assert(self.handle != .null_handle); assert(buf.handle != .null_handle); buf.state.cmd_id = self.id; @@ -378,7 +379,7 @@ pub const Buffer = struct { return submit_id; } - const FlushQueueTransfersError = DeviceDispatch.EndCommandBufferError || DeviceDispatch.QueueSubmitError; + pub const FlushQueueTransfersError = DeviceDispatch.EndCommandBufferError || DeviceDispatch.QueueSubmitError; const pm_buffer_flushqueuetransfers = ProfileMark.init("Command.Buffer.flushqueuetransfers"); pub fn flushQueueTransfers(device: *const Device) FlushQueueTransfersError!void { pm_buffer_flushqueuetransfers.begin(); diff --git a/src/rendering/vulkan/MainPass.zig b/src/rendering/vulkan/MainPass.zig index 8d56d34..04f922f 100644 --- a/src/rendering/vulkan/MainPass.zig +++ b/src/rendering/vulkan/MainPass.zig @@ -48,12 +48,12 @@ pub fn deinit(device: *const Device) void { } const pm_setup = ProfileMark.init("MainPass.setup"); -pub fn setup(device: *const Device, swapchain: *const Swapchain) void { +pub fn setup(device: *const Device, swapchain: *const Swapchain) !void { pm_setup.begin(); defer pm_setup.end(); DepthPass.setup(); - OpaquePass.setup(device, swapchain); + try OpaquePass.setup(device, swapchain); // Exposure.setup(); // UIPass.setup(); } @@ -141,7 +141,6 @@ const DepthPass = struct { .depth_clamp = false, .depth_test_enable = true, .depth_write_enable = true, - .attachment_count = 0, }, }; @@ -309,7 +308,7 @@ const OpaquePass = struct { .{ .binding = 3, .location = 3, - .format = .r32g32b32a32_sfloat, + .format = .r32g32b32a32_uint, .offset = 0, }, }; @@ -335,18 +334,17 @@ const OpaquePass = struct { .depth_clamp = false, .depth_test_enable = true, .depth_write_enable = false, - .attachment_count = 1, - .attachments = undefined, + .attachments = &[_]Pass.BlendState{ + .{ + .color_write_mask = .{ .r_bit = true, .g_bit = true, .b_bit = true, .a_bit = true }, + .blend_enable = true, + .color_blend_op = .add, + .alpha_blend_op = .add, + }, + }, }, }; - desc.fixed_funcs.attachments.?[0] = .{ - .color_write_mask = .{ .r_bit = true, .g_bit = true, .b_bit = true, .a_bit = true }, - .blend_enable = true, - .color_blend_op = .add, - .alpha_blend_op = .add, - }; - try s_pass.init(device, &desc); } @@ -356,17 +354,17 @@ const OpaquePass = struct { } const pm_opaque_setup = ProfileMark.init("OpaquePass.setup"); - pub fn setup(device: *const Device, swapchain: *const Swapchain) void { + pub fn setup(device: *const Device, swapchain: *const Swapchain) !void { pm_opaque_setup.begin(); defer pm_opaque_setup.end(); // TODO: move this into some parameter provider standalone file, says kara { const camera = Camera.get(); - const globals = .{ + const globals = Globals{ .g_WorldToClip = camera.getWorldToClip(Targets.getRenderAspect()), .g_Eye = camera.position, - .g_HdrEnabled = false, // TODO getHdrEnabled + .g_HdrEnabled = 0, // TODO getHdrEnabled .g_Whitepoint = 0, // TODO getWhitepoint .g_DisplayNits = 0, // TODO getDisplayNitsMax .g_UiNits = 0, // TODO getUiNits @@ -380,9 +378,7 @@ const OpaquePass = struct { }, }; - _ = device; - _ = globals; - //try s_per_camera_buffer.write(device, swapchain, &@ptrCast([@sizeOf(Globals)]u8, &globals)); + try s_per_camera_buffer.write(device, swapchain, @ptrCast([*]const u8, &globals)[0..@sizeOf(Globals)]); } Bindings.bindBuffer(settings.Bid.Globals.id, .uniform_buffer, s_per_camera_buffer.current(swapchain)); diff --git a/src/rendering/vulkan/Pass.zig b/src/rendering/vulkan/Pass.zig index e516417..7d91289 100644 --- a/src/rendering/vulkan/Pass.zig +++ b/src/rendering/vulkan/Pass.zig @@ -42,8 +42,7 @@ pub const FixedFuncs = struct { depth_clamp: bool, depth_test_enable: bool, depth_write_enable: bool, - attachment_count: u3, - attachments: ?[8]BlendState = null, + attachments: ?[]const BlendState = null, }; pub const Description = struct { diff --git a/src/rendering/vulkan/Renderer.zig b/src/rendering/vulkan/Renderer.zig index e7de124..3ec052d 100644 --- a/src/rendering/vulkan/Renderer.zig +++ b/src/rendering/vulkan/Renderer.zig @@ -75,7 +75,7 @@ pub fn update(self: *Self) !void { { _ = try self.swapchain.acquireSync(&self.device); _ = try self.swapchain.acquireImage(&self.device); - memory.update(); + try memory.update(&self.device); } // system update @@ -85,7 +85,7 @@ pub fn update(self: *Self) !void { // setup phase { - MainPass.setup(&self.device, &self.swapchain); + try MainPass.setup(&self.device, &self.swapchain); // TODO textable update try Command.flush(&self.device); Bindings.update(&self.device, &self.swapchain); diff --git a/src/rendering/vulkan/image.zig b/src/rendering/vulkan/image.zig index 74b07cf..35d1bda 100644 --- a/src/rendering/vulkan/image.zig +++ b/src/rendering/vulkan/image.zig @@ -277,7 +277,7 @@ pub const Image = struct { try Command.Buffer.flushQueueTransfers(device); } - const barrier: vk.ImageMemoryBarrier = .{ + const barrier = vk.ImageMemoryBarrier{ .src_access_mask = prev.access, .dst_access_mask = self.access, .old_layout = prev.layout, @@ -304,17 +304,17 @@ pub const Image = struct { inserted_barrier = true; } else { const layout_change = prev.layout != self.layout; - const src_read = prev.access.contains(Command.read_access); - const src_write = prev.access.contains(Command.write_access); - const dst_read = self.access.contains(Command.read_access); - const dst_write = self.access.contains(Command.write_access); + const src_read = prev.access.intersect(Command.read_access).toInt() != 0; + const src_write = prev.access.intersect(Command.write_access).toInt() != 0; + const dst_read = self.access.intersect(Command.read_access).toInt() != 0; + const dst_write = self.access.intersect(Command.write_access).toInt() != 0; const read_after_write = src_write and dst_read; const write_after_read = src_read and dst_write; const write_after_write = src_write and dst_write; if (layout_change or read_after_write or write_after_read or write_after_write or new_resource) { // data hazard, insert barrier const cmd = try Command.Buffer.get(self.owner, device); - const barrier: vk.ImageMemoryBarrier = .{ + const barrier = vk.ImageMemoryBarrier{ .src_access_mask = prev.access, .dst_access_mask = self.access, .old_layout = prev.layout, diff --git a/src/rendering/vulkan/memory.zig b/src/rendering/vulkan/memory.zig index b49980e..c5f1f04 100644 --- a/src/rendering/vulkan/memory.zig +++ b/src/rendering/vulkan/memory.zig @@ -3,6 +3,7 @@ const vk = @import("vulkan"); const assert = std.debug.assert; const ProfileMark = @import("../../common/profiler.zig").ProfileMark; +const time = @import("../../common/time.zig"); const framebuffer = @import("framebuffer.zig"); const Base = @import("Base.zig"); @@ -44,7 +45,59 @@ pub fn finalize(device: *const Device) !void { s_allocator.releasables.clearRetainingCapacity(); } -pub fn update() void {} +const pm_map = ProfileMark.init("memory.map"); +pub fn map(allocation: vma.Allocation) ![*]u8 { + pm_map.begin(); + defer pm_map.end(); + + assert(allocation != .Null); + return try s_allocator.handle.mapMemory(allocation, u8); +} + +const pm_unmap = ProfileMark.init("memory.unmap"); +pub fn unmap(allocation: vma.Allocation) void { + pm_unmap.begin(); + defer pm_unmap.end(); + + assert(allocation != .Null); + s_allocator.handle.unmapMemory(allocation); +} + +const pm_flush = ProfileMark.init("memory.flush"); +pub fn flush(allocation: vma.Allocation) void { + pm_flush.begin(); + defer pm_flush.end(); + + s_allocator.handle.flushAllocation(allocation, 0, vk.WHOLE_SIZE); + s_allocator.handle.invalidateAllocation(allocation, 0, vk.WHOLE_SIZE); +} + +const UpdateError = SubmitId.PollError || FinalizeCheckError; +const pm_update = ProfileMark.init("memory.update"); +pub fn update(device: *const Device) UpdateError!void { + pm_update.begin(); + defer pm_update.end(); + + if (s_allocator.handle != .Null) { + const frame = time.getFrameCount(); + s_allocator.handle.setCurrentFrameIndex(@intCast(u32, frame)); // XXX hmm + { + s_allocator.mutex.lock(); + defer s_allocator.mutex.unlock(); + + if (s_allocator.releasables.items.len >= 1) { + var i = s_allocator.releasables.items.len - 1; + while (i >= 0) : (i -= 1) { + if (try s_allocator.releasables.items[i].submit_id.poll(device)) { + _ = s_allocator.releasables.swapRemove(i); + } + } + } + } + + try finalizeCheck(device, s_allocator.releasables.items.len); + } +} const pm_imgnew = ProfileMark.init("memory.imageNew"); pub fn imageNew(img: *image.Image, device: *const Device, info: vk.ImageCreateInfo, mem_usage: vma.MemoryUsage) !void { @@ -159,7 +212,7 @@ fn getBufferPool(usage: vk.BufferUsageFlags, mem_usage: vma.MemoryUsage) !vma.Po assert(usage.intersect(pool_usage).toInt() != 0); return s_allocator.staging_pool.handle; }, - .gpuToCpu => { + .gpuToCpu, .cpuToGpu => { // just a guess const pool_usage = s_allocator.dynamic_buffer_pool.buffer_usage orelse unreachable; assert(usage.intersect(pool_usage).toInt() != 0); return s_allocator.dynamic_buffer_pool.handle; @@ -312,24 +365,32 @@ pub const Releasable = struct { attachment: vk.ImageView, }; + frame_count: u64, submit_id: SubmitId, object: Union, const pm_releasableinit = ProfileMark.init("memory.Releasable.init"); - pub fn init(self: *const Self, device: *const Device) !void { + pub fn init(device: *const Device, frame_count: u64, id: SubmitId, object: Union) !Self { pm_releasableinit.begin(); defer pm_releasableinit.end(); + assert(id.valid); assert(s_allocator.handle != .Null); - assert(self.submit_id.valid); + + const self = Self{ + .submit_id = id, + .object = object, + .frame_count = frame_count, + }; { s_allocator.mutex.lock(); defer s_allocator.mutex.unlock(); - try s_allocator.releasables.append(self.*); + try s_allocator.releasables.append(self); } try finalizeCheck(device, s_allocator.releasables.items.len); + return self; } const pm_releasabledeinit = ProfileMark.init("memory.Releasable.deinit"); @@ -451,11 +512,12 @@ const Allocator = struct { } }; -fn finalizeCheck(device: *const Device, size: usize) !void { +const FinalizeCheckError = Command.Buffer.FlushQueueTransfersError || SubmitId.WaitAllError || error{FinalizeUpdateFail}; +fn finalizeCheck(device: *const Device, size: usize) FinalizeCheckError!void { if (size >= 1024) { // TODO convar std.debug.print("Too many gpu objects, force-finalizing\n", .{}); try Command.flush(device); try SubmitId.waitAll(device); - update(); + update(device) catch return error.FinalizeUpdateFail; } } diff --git a/src/rendering/vulkan/pipeline.zig b/src/rendering/vulkan/pipeline.zig index 1e6eb45..326fcb7 100644 --- a/src/rendering/vulkan/pipeline.zig +++ b/src/rendering/vulkan/pipeline.zig @@ -154,7 +154,7 @@ pub fn newGfx(device: *const Device, fixed_funcs: *const FixedFuncs, vert_layout .logic_op_enable = 0, .logic_op = .clear, .blend_constants = std.mem.zeroes([4]f32), - .attachment_count = fixed_funcs.attachments.?.len, + .attachment_count = if (fixed_funcs.attachments) |att| @intCast(u32, att.len) else 0, .p_attachments = &color_blend_attachments, }; diff --git a/src/rendering/vulkan/submit_id.zig b/src/rendering/vulkan/submit_id.zig index 26bf920..bb46515 100644 --- a/src/rendering/vulkan/submit_id.zig +++ b/src/rendering/vulkan/submit_id.zig @@ -1,17 +1,21 @@ const std = @import("std"); const assert = std.debug.assert; +const vk = @import("vulkan"); const Device = @import("device.zig").Device; +const DeviceDispatch = @import("device.zig").DeviceDispatch; const queues = @import("queues.zig"); const profiler = @import("../../common/profiler.zig"); +const Fence = @import("sync.zig").Fence; pub const SubmitId = packed struct { counter: u32 = 0, queue_id: queues.QueueId = .graphics, valid: bool = false, + pub const PollError = Fence.StatError || DeviceDispatch.ResetCommandBufferError; const pm_poll = profiler.ProfileMark.init("SubmitId.poll"); - pub fn poll(self: *const SubmitId, device: *const Device) !bool { + pub fn poll(self: *const SubmitId, device: *const Device) PollError!bool { pm_poll.begin(); defer pm_poll.end(); @@ -25,7 +29,7 @@ pub const SubmitId = packed struct { return false; } - device.dispatch.resetCommandBuffer(queue.cmds[i], .{}); + try device.dispatch.resetCommandBuffer(queue.cmds[i], .{}); queue.cmd_ids[i] = 0; } @@ -52,6 +56,7 @@ pub const SubmitId = packed struct { } } + pub const WaitAllError = Fence.WaitError || DeviceDispatch.ResetCommandBufferError; const pm_waitall = profiler.ProfileMark.init("SubmitId.waitAll"); pub fn waitAll(device: *const Device) !void { pm_waitall.begin(); diff --git a/src/rendering/vulkan/sync.zig b/src/rendering/vulkan/sync.zig index 684e426..31324d0 100644 --- a/src/rendering/vulkan/sync.zig +++ b/src/rendering/vulkan/sync.zig @@ -65,7 +65,8 @@ pub const Fence = struct { } } - pub fn stat(self: *const Self, device: *const Device) DeviceDispatch.GetFenceStatusError!State { + pub const StatError = DeviceDispatch.GetFenceStatusError; + pub fn stat(self: *const Self, device: *const Device) StatError!State { return @intToEnum(State, @enumToInt(try device.dispatch.getFenceStatus(device.handle, self.handle))); } };