diff --git a/src/Game.zig b/src/Game.zig index b997697..ad67bc9 100644 --- a/src/Game.zig +++ b/src/Game.zig @@ -21,7 +21,6 @@ const Iterator2 = math.Iterator2; const Materials = @import("engine/Materials.zig"); const Player = @import("Player.zig"); const Skybox = @import("engine/Skybox.zig"); -const StagingBuffer = @import("engine/StagingBuffer.zig"); const Swapchain = @import("engine/Swapchain.zig"); const Texture = @import("engine/Texture.zig"); const Textures = @import("engine/Textures.zig"); @@ -37,7 +36,8 @@ vertex_buffer: shaders.VertexBuffer, index_buffer: shaders.IndexBuffer, global_uniforms: shaders.GlobalUniformsBuffer, -global_uniforms_staging_buffer: StagingBuffer, +global_uniforms_staging_buffer: shaders.GlobalUniformsBuffer, +global_uniforms_staging_ptr: *shaders.GlobalUniforms, global_uniforms_transfer_semaphores: []vk.Semaphore, point_lights: shaders.PointLightBuffer, directional_lights: shaders.DirectionalLightBuffer, @@ -221,12 +221,17 @@ pub fn init() !Game { }); errdefer global_uniforms.deinit(); - var global_uniforms_staging_buffer = try StagingBuffer.init(.{ - .capacity = @sizeOf(shaders.GlobalUniforms), + var global_uniforms_staging_buffer = try shaders.GlobalUniformsBuffer.init(.{ + .usage = .staging, .target_queue = .graphics, + .mappable = true, + .name = "GlobalUniforms Staging", }); errdefer global_uniforms_staging_buffer.deinit(); + const global_uniforms_staging_ptr = try global_uniforms_staging_buffer.map(); + errdefer global_uniforms_staging_buffer.unmap(); + const global_uniforms_transfer_semaphores = blk: { var semaphores: std.ArrayList(vk.Semaphore) = try .initCapacity(allocator_general, swapchain.swapchain_images.len); errdefer semaphores.deinit(allocator_general); @@ -744,6 +749,7 @@ pub fn init() !Game { .global_uniforms = global_uniforms, .global_uniforms_staging_buffer = global_uniforms_staging_buffer, + .global_uniforms_staging_ptr = global_uniforms_staging_ptr, .global_uniforms_transfer_semaphores = global_uniforms_transfer_semaphores, .point_lights = point_lights, .directional_lights = directional_lights, @@ -776,6 +782,7 @@ pub fn deinit(self: *Game) void { self.skybox.deinit(); self.global_uniforms.deinit(); + self.global_uniforms_staging_buffer.unmap(); self.global_uniforms_staging_buffer.deinit(); self.point_lights.deinit(); self.directional_lights.deinit(); @@ -901,7 +908,7 @@ fn render(self: *Game) !void { const ambient_light = vm.Vector3.init(0.01, 0.01, 0.01); - const global_uniforms_data: shaders.GlobalUniforms = .{ + self.global_uniforms_staging_ptr.* = .{ .matrixWStoVS = matrix_ws_to_vs, .matrixVStoCS = matrix_vs_to_cs, .matrixSSPXtoCS = matrix_sspx_to_cs, @@ -910,10 +917,7 @@ fn render(self: *Game) !void { .directionalLightCount = directional_lights_data.len, .ambientLight = ambient_light, }; - - const staging_memory = try self.global_uniforms_staging_buffer.map(); - @memcpy(staging_memory, std.mem.asBytes(&global_uniforms_data)); - self.global_uniforms_staging_buffer.unmap(); + try self.global_uniforms_staging_buffer.flush(0, 1); try self.deferred_command_buffers.ensureUnusedCapacity(allocator_general, 1); const global_uniforms_transfer_command_buffer = try CommandBuffer.init(.transfer); diff --git a/src/engine/GenericBuffer.zig b/src/engine/GenericBuffer.zig index 879c542..ca33157 100644 --- a/src/engine/GenericBuffer.zig +++ b/src/engine/GenericBuffer.zig @@ -5,7 +5,6 @@ const vk = @import("vulkan"); const CommandBuffer = @import("CommandBuffer.zig"); const Engine = @import("Engine.zig"); -const StagingBuffer = @import("StagingBuffer.zig"); const TargetQueue = @import("TargetQueue.zig").TargetQueue; pub const Usage = enum { @@ -14,6 +13,7 @@ pub const Usage = enum { index, vertex, indirect, + staging, pub fn asBufferUsageFlags(self: Usage) vk.BufferUsageFlags { return switch (self) { @@ -22,6 +22,7 @@ pub const Usage = enum { .index => .{ .transfer_dst_bit = true, .index_buffer_bit = true }, .vertex => .{ .transfer_dst_bit = true, .vertex_buffer_bit = true }, .indirect => .{ .transfer_dst_bit = true, .indirect_buffer_bit = true }, + .staging => .{ .transfer_src_bit = true }, }; } }; @@ -29,6 +30,7 @@ pub const Usage = enum { pub const InitInfo = struct { usage: Usage, target_queue: TargetQueue, + mappable: bool = false, capacity: u32 = 1, name: ?[]const u8 = null, }; @@ -39,6 +41,7 @@ pub fn GenericBuffer(comptime T: type) type { buffer: vk.Buffer, device_memory: vk.DeviceMemory, + mappable: bool, target_queue: TargetQueue, capacity: u32, @@ -66,7 +69,10 @@ pub fn GenericBuffer(comptime T: type) type { } const memory_requirements = engine.getBufferMemoryRequirements(buffer); - const device_memory = try engine.allocate(memory_requirements, .{ .device_local_bit = true }); + const device_memory = try engine.allocate(memory_requirements, .{ + .device_local_bit = !init_info.mappable, + .host_visible_bit = init_info.mappable, + }); errdefer engine.freeMemory(device_memory); if (init_info.name) |name| { engine.setObjectName(device_memory, "DM {s} [{s}]", .{ name, @typeName(T) }); @@ -77,6 +83,7 @@ pub fn GenericBuffer(comptime T: type) type { return .{ .buffer = buffer, .device_memory = device_memory, + .mappable = init_info.mappable, .target_queue = init_info.target_queue, .capacity = init_info.capacity, }; @@ -112,37 +119,111 @@ pub fn GenericBuffer(comptime T: type) type { std.debug.assert(first_index + element_count <= self.capacity); const write_size = element_count * element_size; - if (write_size == 0) return; + if (element_count == 0) return; - var staging_buffer: StagingBuffer = try .init(.{ - .target_queue = self.target_queue, - .capacity = write_size, - }); - defer staging_buffer.deinit(); + if (self.mappable) { + const slice = try self.mapSliceAt(first_index, element_count); + defer self.unmap(); - const staging_memory = try staging_buffer.map(); - @memcpy(staging_memory, std.mem.sliceAsBytes(values)); - staging_buffer.unmap(); + @memcpy(slice, values); + try self.flush(first_index, element_count); + } else { + var staging_buffer: GenericBuffer(T) = try .init(.{ + .usage = .staging, + .target_queue = self.target_queue, + .mappable = true, + .capacity = element_count, + }); + defer staging_buffer.deinit(); - var command_buffer: CommandBuffer = try .init(.transfer); - defer command_buffer.deinit(); + { + const staging_slice = try staging_buffer.mapSlice(); + defer staging_buffer.unmap(); - try command_buffer.beginCommandBuffer(); - const regions = [_]vk.BufferCopy{ + @memcpy(staging_slice, values); + try staging_buffer.flush(0, element_count); + } + + var command_buffer: CommandBuffer = try .init(.transfer); + defer command_buffer.deinit(); + + try command_buffer.beginCommandBuffer(); + const regions = [_]vk.BufferCopy{ + .{ + .src_offset = 0, + .dst_offset = first_index * element_size, + .size = write_size, + }, + }; + command_buffer.copyBuffer(staging_buffer.buffer, self.buffer, ®ions); + try command_buffer.endCommandBuffer(); + + const fence = try engine.createFence(.{}); + defer engine.destroyFence(fence); + + try command_buffer.submit(.{ .fence = fence }); + try engine.waitForFence(fence); + } + } + + pub fn map(self: Self) !*T { + std.debug.assert(self.mappable); + + const slice = try self.mapSliceAt(0, 1); + return &slice[0]; + } + + pub fn mapAt(self: Self, index: u32) !*T { + std.debug.assert(self.mappable); + + const slice = try self.mapSliceAt(index, 1); + return &slice[0]; + } + + pub fn mapSlice(self: Self) ![]T { + std.debug.assert(self.mappable); + + return self.mapSliceAt(0, self.capacity); + } + + pub fn mapSliceAt(self: Self, first_index: u32, count: u32) ![]T { + const engine = ctx.engine; + + std.debug.assert(self.mappable); + std.debug.assert(first_index + count <= self.capacity); + + const byte_offset = @as(u64, first_index) * @as(u64, element_size); + const byte_length = @as(u64, count) * @as(u64, element_size); + + const mapped_memory = try engine.mapMemory(self.device_memory, byte_offset, byte_length, .{}); + const slice = @as([*]T, @ptrCast(@alignCast(mapped_memory.ptr)))[0..count]; + + return slice; + } + + pub fn flush(self: Self, first_index: u32, count: u32) !void { + const engine = ctx.engine; + + std.debug.assert(self.mappable); + std.debug.assert(first_index + count <= self.capacity); + + const byte_offset = @as(u64, first_index) * @as(u64, element_size); + const byte_length = @as(u64, count) * @as(u64, element_size); + + try engine.device.flushMappedMemoryRanges(&.{ .{ - .src_offset = 0, - .dst_offset = first_index * element_size, - .size = write_size, + .memory = self.device_memory, + .offset = byte_offset, + .size = byte_length, }, - }; - command_buffer.copyBuffer(staging_buffer.buffer, self.buffer, ®ions); - try command_buffer.endCommandBuffer(); + }); + } - const fence = try engine.createFence(.{}); - defer engine.destroyFence(fence); + pub fn unmap(self: Self) void { + const engine = ctx.engine; - try command_buffer.submit(.{ .fence = fence }); - try engine.waitForFence(fence); + std.debug.assert(self.mappable); + engine.unmapMemory(self.device_memory); } }; } diff --git a/src/engine/Skybox.zig b/src/engine/Skybox.zig index 7cab7bb..bc472f8 100644 --- a/src/engine/Skybox.zig +++ b/src/engine/Skybox.zig @@ -10,7 +10,6 @@ const vm = @import("vecmath"); const CommandBuffer = @import("CommandBuffer.zig"); const Engine = @import("Engine.zig"); const GenericBuffer = @import("GenericBuffer.zig").GenericBuffer; -const StagingBuffer = @import("StagingBuffer.zig"); const Swapchain = @import("Swapchain.zig"); const Texture = @import("Texture.zig"); @@ -69,15 +68,21 @@ pub fn load( // --- LOAD IMAGE INTO STAGING BUFFER -------------------------------------- - var staging_buffer = try StagingBuffer.init(.{ - .capacity = @intCast(img.width * img.height * @sizeOf(vm.ColorHdr)), + var staging_buffer = try GenericBuffer(vm.ColorHdr).init(.{ + .usage = .staging, .target_queue = .compute, + .mappable = true, + .capacity = img.width * img.height, }); defer staging_buffer.deinit(); - const staging_memory = try staging_buffer.map(); - @memcpy(staging_memory, @as([*]const u8, @ptrCast(img.data))); - staging_buffer.unmap(); + { + const staging_memory = try staging_buffer.mapSlice(); + defer staging_buffer.unmap(); + + @memcpy(staging_memory, img.data); + try staging_buffer.flush(0, img.width * img.height); + } // --- CREATE EQUIRECTANGULAR IMAGE ---------------------------------------- diff --git a/src/engine/StagingBuffer.zig b/src/engine/StagingBuffer.zig deleted file mode 100644 index 99bb717..0000000 --- a/src/engine/StagingBuffer.zig +++ /dev/null @@ -1,83 +0,0 @@ -const StagingBuffer = @This(); -const std = @import("std"); - -const ctx = @import("../AppContext.zig"); -const vk = @import("vulkan"); - -const Engine = @import("Engine.zig"); -const TargetQueue = @import("TargetQueue.zig").TargetQueue; - -buffer: vk.Buffer, -device_memory: vk.DeviceMemory, -capacity: u32, - -pub const InitInfo = struct { - target_queue: TargetQueue, - capacity: u32, -}; - -pub fn init(init_info: InitInfo) !StagingBuffer { - const engine = ctx.engine; - - const target_queue_family = switch (init_info.target_queue) { - .graphics => engine.graphics_queue.allocation.family, - .compute => engine.compute_queue.allocation.family, - }; - const transfer_queue_family = engine.transfer_queue.allocation.family; - - const buffer = try engine.createBuffer(.{ - .size = init_info.capacity, - .usage = .{ - .transfer_src_bit = true, - }, - .queue_family_indices = &.{ target_queue_family, transfer_queue_family }, - }); - errdefer engine.destroyBuffer(buffer); - - const memory_requirements = engine.getBufferMemoryRequirements(buffer); - const device_memory = try engine.allocate( - memory_requirements, - .{ - .host_visible_bit = true, - .host_coherent_bit = true, - }, - ); - errdefer engine.freeMemory(device_memory); - - try engine.bindBufferMemory(buffer, device_memory, 0); - - return .{ - .buffer = buffer, - .device_memory = device_memory, - .capacity = init_info.capacity, - }; -} - -pub fn deinit(self: *StagingBuffer) void { - const engine = ctx.engine; - - std.log.scoped(.deinit).debug("Deinitializing {*}", .{self}); - - engine.freeMemory(self.device_memory); - engine.destroyBuffer(self.buffer); - - self.* = undefined; -} - -pub fn map(self: StagingBuffer) ![]u8 { - const mapped_memory = try self.mapPartial(0, self.capacity); - return mapped_memory; -} - -pub fn mapPartial(self: StagingBuffer, offset: u32, len: u32) ![]u8 { - const engine = ctx.engine; - - const mapped_memory = try engine.mapMemory(self.device_memory, offset, len, .{}); - return mapped_memory; -} - -pub fn unmap(self: StagingBuffer) void { - const engine = ctx.engine; - - engine.unmapMemory(self.device_memory); -} diff --git a/src/engine/Texture.zig b/src/engine/Texture.zig index b71e3c2..57aaedd 100644 --- a/src/engine/Texture.zig +++ b/src/engine/Texture.zig @@ -7,7 +7,7 @@ const vk = @import("vulkan"); const CommandBuffer = @import("CommandBuffer.zig"); const DeviceAllocation = @import("DeviceAllocation.zig"); const Engine = @import("Engine.zig"); -const StagingBuffer = @import("StagingBuffer.zig"); +const GenericBuffer = @import("GenericBuffer.zig").GenericBuffer; const TargetQueue = @import("TargetQueue.zig").TargetQueue; pub const Usage = enum { @@ -207,15 +207,21 @@ pub fn writeRaw(self: Texture, data: []const u8) !void { std.debug.assert(data.len == byte_length); std.debug.assert(self.usage != .depth); - var staging_buffer = try StagingBuffer.init(.{ - .capacity = @intCast(byte_length), + var staging_buffer = try GenericBuffer(u8).init(.{ + .usage = .staging, .target_queue = self.target_queue, + .mappable = true, + .capacity = @intCast(byte_length), }); defer staging_buffer.deinit(); - const staging_memory = try staging_buffer.map(); - @memcpy(staging_memory, data); - staging_buffer.unmap(); + { + const staging_memory = try staging_buffer.mapSlice(); + defer staging_buffer.unmap(); + + @memcpy(staging_memory, data); + try staging_buffer.flush(0, byte_length); + } // --- TRANSITION TO TRANSFER_DST_OPTIMAL AND COPY -----------------