CommandBuffer wrapper

This commit is contained in:
2025-11-28 17:09:37 +01:00
parent 94c43a170f
commit 2541dee18d
9 changed files with 461 additions and 289 deletions

View File

@@ -8,6 +8,7 @@ const math = @import("math.zig");
const Materials = @import("assets/Materials.zig");
const Textures = @import("assets/Textures.zig");
const CommandBuffer = @import("engine/CommandBuffer.zig").CommandBuffer;
const Engine = @import("engine/Engine.zig");
const GenericBuffer = @import("engine/GenericBuffer.zig").GenericBuffer;
const StagingBuffer = @import("engine/StagingBuffer.zig");
@@ -115,7 +116,7 @@ index_buffer: IndexBuffer,
global_uniforms: GlobalUniformsBuffer,
global_uniforms_staging_buffer: StagingBuffer,
global_uniforms_transfer_command_buffer: vk.CommandBuffer,
global_uniforms_transfer_command_buffer: CommandBuffer(.graphics, .persistent),
global_uniforms_transfer_semaphores: []vk.Semaphore,
point_lights: PointLightBuffer,
directional_lights: DirectionalLightBuffer,
@@ -305,14 +306,13 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain
});
errdefer global_uniforms_staging_buffer.deinit(engine);
const global_uniforms_transfer_command_buffer = try engine.allocateTransferCommandBuffer();
errdefer engine.freeTransferCommandBuffer(global_uniforms_transfer_command_buffer);
var global_uniforms_transfer_command_buffer: CommandBuffer(.graphics, .persistent) = try .init(engine);
errdefer global_uniforms_transfer_command_buffer.deinit(engine);
try global_uniforms_transfer_command_buffer.beginCommandBuffer(&.{});
try global_uniforms_transfer_command_buffer.beginCommandBuffer(.{});
global_uniforms_transfer_command_buffer.copyBuffer(
global_uniforms_staging_buffer.buffer,
global_uniforms.buffer,
1,
&.{
.{
.src_offset = 0,
@@ -685,7 +685,7 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain
.global_uniforms = global_uniforms,
.global_uniforms_staging_buffer = global_uniforms_staging_buffer,
.global_uniforms_transfer_command_buffer = global_uniforms_transfer_command_buffer.handle,
.global_uniforms_transfer_command_buffer = global_uniforms_transfer_command_buffer,
.global_uniforms_transfer_semaphores = global_uniforms_transfer_semaphores,
.point_lights = point_lights,
.directional_lights = directional_lights,
@@ -706,6 +706,7 @@ pub fn deinit(self: *Game) void {
self.global_uniforms.deinit(self.engine);
self.global_uniforms_staging_buffer.deinit(self.engine);
self.global_uniforms_transfer_command_buffer.deinit(self.engine);
self.point_lights.deinit(self.engine);
self.directional_lights.deinit(self.engine);
self.object_uniforms.deinit(self.engine);
@@ -782,16 +783,9 @@ pub fn update(self: *Game, dt: f32) void {
@memcpy(staging_memory, std.mem.asBytes(&global_uniforms_data));
self.global_uniforms_staging_buffer.unmap(self.engine);
const submits = [_]vk.SubmitInfo{
.{
.command_buffer_count = 1,
.p_command_buffers = @ptrCast(&self.global_uniforms_transfer_command_buffer),
.signal_semaphore_count = 1,
.p_signal_semaphores = @ptrCast(&self.global_uniforms_transfer_semaphores[self.swapchain.image_index]),
},
};
self.engine.device.queueSubmit(self.engine.transfer_queue.handle, submits.len, &submits, .null_handle) catch |err| {
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");
};
@@ -877,41 +871,37 @@ pub fn onMouseUp(self: *Game, button: glfw.MouseButton, mods: glfw.Mods) void {
}
fn render(self: *Game) !void {
const engine = self.engine;
const extent = self.swapchain.extent;
const command_buffer = try engine.allocateGraphicsCommandBuffer();
const command_buffer: CommandBuffer(.graphics, .transient) = try .init(self.engine);
// NOTE Do not free command buffer yet
try command_buffer.beginCommandBuffer(&.{ .flags = .{ .one_time_submit_bit = true } });
try command_buffer.beginCommandBuffer(.{});
{
const clear_values = [_]vk.ClearValue{
.{
.color = .{
.float_32 = .{ 0, 0, 0, 0 },
},
},
.{
.depth_stencil = .{
.depth = 0,
.stencil = 0,
},
},
};
command_buffer.beginRenderPass(&.{
command_buffer.beginRenderPass(.{
.render_pass = self.swapchain.render_pass,
.framebuffer = self.swapchain.swapchain_images[self.swapchain.image_index].framebuffer,
.render_area = .{
.offset = .{ .x = 0, .y = 0 },
.extent = extent,
},
.clear_value_count = clear_values.len,
.p_clear_values = &clear_values,
.clear_values = &.{
.{
.color = .{
.float_32 = .{ 0, 0, 0, 0 },
},
},
.{
.depth_stencil = .{
.depth = 0,
.stencil = 0,
},
},
},
}, .@"inline");
defer command_buffer.endRenderPass();
const viewports = [_]vk.Viewport{
command_buffer.setViewport(0, &.{
.{
.x = 0,
.y = 0,
@@ -920,29 +910,33 @@ fn render(self: *Game) !void {
.min_depth = 0,
.max_depth = 1,
},
};
command_buffer.setViewport(0, viewports.len, &viewports);
const scissors = [_]vk.Rect2D{
});
command_buffer.setScissor(0, &.{
.{
.offset = .{ .x = 0, .y = 0 },
.extent = extent,
},
};
command_buffer.setScissor(0, scissors.len, &scissors);
});
command_buffer.bindPipeline(.graphics, self.pipeline);
command_buffer.bindVertexBuffers(0, 1, @ptrCast(&self.vertex_buffer.buffer), &.{0});
try command_buffer.bindVertexBuffers(0, &.{
.{
.buffer = self.vertex_buffer.buffer,
.offset = 0,
},
});
command_buffer.bindIndexBuffer(self.index_buffer.buffer, 0, .uint16);
command_buffer.bindDescriptorSets(.graphics, self.pipeline_layout, 0, self.descriptor_sets.len, &self.descriptor_sets, 0, null);
command_buffer.bindDescriptorSets(.graphics, self.pipeline_layout, 0, &self.descriptor_sets, &.{});
command_buffer.drawIndexed(@intCast(self.index_buffer.array_capacity), self.object_count, 0, 0, 0);
command_buffer.drawIndexed(.{
.index_count = self.index_buffer.array_capacity,
.instance_count = self.object_count,
});
}
try command_buffer.endCommandBuffer();
const res = try self.swapchain.present(self.engine, .{
.command_buffer = command_buffer.handle,
.command_buffer = command_buffer,
.wait_semaphores = &.{
.{
.semaphore = self.global_uniforms_transfer_semaphores[self.swapchain.image_index],

View File

@@ -0,0 +1,272 @@
const std = @import("std");
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");
pub const DrawIndexed = struct {
index_count: u32,
instance_count: u32 = 1,
first_index: u32 = 0,
vertex_offset: i32 = 0,
first_instance: u32 = 0,
};
pub const PipelineBarrier = struct {
src_stage_mask: vk.PipelineStageFlags,
dst_stage_mask: vk.PipelineStageFlags,
dependency_flags: vk.DependencyFlags = .{},
memory_barriers: []const vk.MemoryBarrier = &.{},
buffer_memory_barriers: []const vk.BufferMemoryBarrier = &.{},
image_memory_barriers: []const vk.ImageMemoryBarrier = &.{},
};
pub const RenderPassBeginInfo = struct {
render_pass: vk.RenderPass,
framebuffer: vk.Framebuffer,
render_area: vk.Rect2D,
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,
};
pub fn CommandBuffer(comptime queue_type: QueueType, comptime transient: Transient) type {
return struct {
const Self = @This();
proxy: vk.CommandBufferProxy,
allocator: std.mem.Allocator,
pub fn init(engine: *const Engine) !Self {
const handle = try allocateCommandBuffer(engine, queue_type, transient);
const proxy: vk.CommandBufferProxy = .init(handle, engine.device.wrapper);
const allocator = engine.vk_allocator.allocator;
return .{ .proxy = proxy, .allocator = allocator };
}
pub fn deinit(self: *Self, engine: *const Engine) void {
freeCommandBuffer(engine, queue_type, transient, self.proxy.handle);
self.* = undefined;
}
pub fn submit(self: Self, engine: *const Engine, submit_info: SubmitInfo) !void {
try submitCommandBuffer(engine, queue_type, self.proxy.handle, submit_info);
}
pub const CommandBufferBeginInfo = struct {
flags: vk.CommandBufferUsageFlags = .{
.one_time_submit_bit = switch (transient) {
.persistent => false,
.transient => true,
},
},
inheritance_info: ?vk.CommandBufferInheritanceInfo = null,
};
pub fn beginCommandBuffer(self: Self, begin_info: CommandBufferBeginInfo) !void {
try self.proxy.beginCommandBuffer(&.{
.flags = begin_info.flags,
.p_inheritance_info = if (begin_info.inheritance_info) |*x| x else null,
});
}
pub inline fn beginRenderPass(self: Self, render_pass_begin: RenderPassBeginInfo, contents: vk.SubpassContents) void {
self.proxy.beginRenderPass(&.{
.render_pass = render_pass_begin.render_pass,
.framebuffer = render_pass_begin.framebuffer,
.render_area = render_pass_begin.render_area,
.clear_value_count = @intCast(render_pass_begin.clear_values.len),
.p_clear_values = render_pass_begin.clear_values.ptr,
}, contents);
}
pub inline fn bindDescriptorSets(
self: Self,
pipeline_bind_point: vk.PipelineBindPoint,
layout: vk.PipelineLayout,
first_set: u32,
descriptor_sets: []const vk.DescriptorSet,
dynamic_offsets: []const u32,
) void {
self.proxy.bindDescriptorSets(
pipeline_bind_point,
layout,
first_set,
@intCast(descriptor_sets.len),
descriptor_sets.ptr,
@intCast(dynamic_offsets.len),
dynamic_offsets.ptr,
);
}
pub inline fn bindPipeline(self: Self, pipeline_bind_point: vk.PipelineBindPoint, pipeline: vk.Pipeline) void {
self.proxy.bindPipeline(pipeline_bind_point, pipeline);
}
pub inline fn bindIndexBuffer(self: Self, buffer: vk.Buffer, offset: u64, index_type: vk.IndexType) void {
self.proxy.bindIndexBuffer(buffer, offset, index_type);
}
pub inline fn bindVertexBuffers(self: Self, first_binding: u32, bindings: []const VertexBufferBinding) !void {
var vertex_buffer_bindings = std.MultiArrayList(VertexBufferBinding){};
defer vertex_buffer_bindings.deinit(self.allocator);
try vertex_buffer_bindings.ensureTotalCapacity(self.allocator, bindings.len);
for (bindings) |binding| {
vertex_buffer_bindings.appendAssumeCapacity(binding);
}
self.proxy.bindVertexBuffers(
first_binding,
@intCast(vertex_buffer_bindings.len),
vertex_buffer_bindings.items(.buffer).ptr,
vertex_buffer_bindings.items(.offset).ptr,
);
}
pub inline fn drawIndexed(self: Self, draw_indexed: DrawIndexed) void {
self.proxy.drawIndexed(
draw_indexed.index_count,
draw_indexed.instance_count,
draw_indexed.first_index,
draw_indexed.vertex_offset,
draw_indexed.first_instance,
);
}
pub inline fn endCommandBuffer(self: Self) !void {
try self.proxy.endCommandBuffer();
}
pub inline fn endRenderPass(self: Self) void {
self.proxy.endRenderPass();
}
pub inline fn copyBuffer(
self: Self,
src_buffer: vk.Buffer,
dst_buffer: vk.Buffer,
regions: []const vk.BufferCopy,
) void {
self.proxy.copyBuffer(src_buffer, dst_buffer, @intCast(regions.len), regions.ptr);
}
pub inline fn copyBufferToImage(
self: Self,
src_buffer: vk.Buffer,
dst_image: vk.Image,
dst_image_layout: vk.ImageLayout,
regions: []const vk.BufferImageCopy,
) void {
self.proxy.copyBufferToImage(src_buffer, dst_image, dst_image_layout, @intCast(regions.len), regions.ptr);
}
pub inline fn pipelineBarrier(self: Self, barrier: PipelineBarrier) void {
self.proxy.pipelineBarrier(
barrier.src_stage_mask,
barrier.dst_stage_mask,
barrier.dependency_flags,
@intCast(barrier.memory_barriers.len),
barrier.memory_barriers.ptr,
@intCast(barrier.buffer_memory_barriers.len),
barrier.buffer_memory_barriers.ptr,
@intCast(barrier.image_memory_barriers.len),
barrier.image_memory_barriers.ptr,
);
}
pub inline fn setScissor(self: Self, first_scissor: u32, scissors: []const vk.Rect2D) void {
self.proxy.setScissor(first_scissor, @intCast(scissors.len), scissors.ptr);
}
pub inline fn setViewport(self: Self, first_viewport: u32, viewports: []const vk.Viewport) void {
self.proxy.setViewport(first_viewport, @intCast(viewports.len), viewports.ptr);
}
};
}
inline fn resolveCommandPool(self: *const Engine, comptime queue_type: QueueType, comptime transient: Transient) vk.CommandPool {
return switch (queue_type) {
.graphics => switch (transient) {
.persistent => self.graphics_command_pool,
.transient => self.transient_graphics_command_pool,
},
.compute => switch (transient) {
.persistent => self.compute_command_pool,
.transient => self.transient_compute_command_pool,
},
.transfer => switch (transient) {
.persistent => self.transfer_command_pool,
.transient => self.transient_transfer_command_pool,
},
};
}
fn allocateCommandBuffer(engine: *const Engine, comptime queue_type: QueueType, comptime transient: Transient) !vk.CommandBuffer {
const command_pool = resolveCommandPool(engine, queue_type, transient);
var command_buffers: [1]vk.CommandBuffer = undefined;
try engine.device.allocateCommandBuffers(&.{
.command_pool = command_pool,
.level = .primary,
.command_buffer_count = command_buffers.len,
}, &command_buffers);
return command_buffers[0];
}
fn freeCommandBuffer(engine: *const Engine, comptime queue_type: QueueType, comptime transient: Transient, command_buffer: vk.CommandBuffer) void {
const command_pool = resolveCommandPool(engine, queue_type, transient);
const command_buffers = [_]vk.CommandBuffer{command_buffer};
engine.device.freeCommandBuffers(command_pool, command_buffers.len, &command_buffers);
}
fn submitCommandBuffer(
engine: *const Engine,
comptime 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);
}

View File

@@ -6,6 +6,7 @@ const glfw = @import("zglfw");
const vk = @import("vulkan");
const Queue = @import("Queue.zig");
const QueueType = @import("QueueType.zig").QueueType;
const VkAllocator = @import("VkAllocator.zig");
pub const Mode = union(enum) {
@@ -15,18 +16,6 @@ pub const Mode = union(enum) {
surface: vk.SurfaceKHR,
presentation_queue: Queue,
},
pub fn deinit(self: *Mode, instance: vk.InstanceProxy, vk_allocator: *VkAllocator) void {
std.log.debug("Deinitializing {*} with InstanceProxy@{{{X},{*}}} and {*}", .{ self, instance.handle, instance.wrapper, vk_allocator });
switch (self.*) {
.headless => {},
.surface => |x| {
instance.destroySurfaceKHR(x.surface, &vk_allocator.interface);
},
}
self.* = undefined;
}
};
pub const ModeTag = std.meta.Tag(Mode);
@@ -104,6 +93,9 @@ transfer_queue: Queue,
graphics_command_pool: vk.CommandPool,
compute_command_pool: vk.CommandPool,
transfer_command_pool: vk.CommandPool,
transient_graphics_command_pool: vk.CommandPool,
transient_compute_command_pool: vk.CommandPool,
transient_transfer_command_pool: vk.CommandPool,
prng_ptr: *std.Random.Pcg,
@@ -337,6 +329,18 @@ pub fn init(allocator: std.mem.Allocator, maybe_window: ?*glfw.Window) !Engine {
}, &vk_allocator.interface);
errdefer device.destroyCommandPool(transfer_command_pool, &vk_allocator.interface);
const transient_graphics_command_pool = try device.createCommandPool(&.{
.flags = .{ .transient_bit = true },
.queue_family_index = queue_allocations.graphics_queue.family,
}, &vk_allocator.interface);
errdefer device.destroyCommandPool(transient_graphics_command_pool, &vk_allocator.interface);
const transient_compute_command_pool = try device.createCommandPool(&.{
.flags = .{ .transient_bit = true },
.queue_family_index = queue_allocations.compute_queue.family,
}, &vk_allocator.interface);
errdefer device.destroyCommandPool(transient_compute_command_pool, &vk_allocator.interface);
const transient_transfer_command_pool = try device.createCommandPool(&.{
.flags = .{ .transient_bit = true },
.queue_family_index = queue_allocations.transfer_queue.family,
@@ -383,6 +387,9 @@ pub fn init(allocator: std.mem.Allocator, maybe_window: ?*glfw.Window) !Engine {
.graphics_command_pool = graphics_command_pool,
.compute_command_pool = compute_command_pool,
.transfer_command_pool = transfer_command_pool,
.transient_graphics_command_pool = transient_graphics_command_pool,
.transient_compute_command_pool = transient_compute_command_pool,
.transient_transfer_command_pool = transient_transfer_command_pool,
.prng_ptr = prng_ptr,
@@ -400,6 +407,9 @@ pub fn deinit(self: *Engine) void {
self.device.destroyCommandPool(self.graphics_command_pool, &self.vk_allocator.interface);
self.device.destroyCommandPool(self.compute_command_pool, &self.vk_allocator.interface);
self.device.destroyCommandPool(self.transfer_command_pool, &self.vk_allocator.interface);
self.device.destroyCommandPool(self.transient_graphics_command_pool, &self.vk_allocator.interface);
self.device.destroyCommandPool(self.transient_compute_command_pool, &self.vk_allocator.interface);
self.device.destroyCommandPool(self.transient_transfer_command_pool, &self.vk_allocator.interface);
self.device.destroyDevice(&self.vk_allocator.interface);
@@ -437,70 +447,6 @@ pub fn allocate(self: *const Engine, memory_requirements: vk.MemoryRequirements,
return error.NoSuitableMemoryType;
}
pub fn allocateTransientTransferCommandBuffer(self: *const Engine) !vk.CommandBufferProxy {
var command_buffer: vk.CommandBuffer = undefined;
try self.device.allocateCommandBuffers(&.{
.command_pool = self.transient_transfer_command_pool,
.level = .primary,
.command_buffer_count = 1,
}, @ptrCast(&command_buffer));
return .init(command_buffer, self.device.wrapper);
}
pub fn allocateTransferCommandBuffer(self: *const Engine) !vk.CommandBufferProxy {
var command_buffer: vk.CommandBuffer = undefined;
try self.device.allocateCommandBuffers(&.{
.command_pool = self.transfer_command_pool,
.level = .primary,
.command_buffer_count = 1,
}, @ptrCast(&command_buffer));
return .init(command_buffer, self.device.wrapper);
}
pub fn allocateGraphicsCommandBuffer(self: *const Engine) !vk.CommandBufferProxy {
var command_buffer: vk.CommandBuffer = undefined;
try self.device.allocateCommandBuffers(&.{
.command_pool = self.graphics_command_pool,
.level = .primary,
.command_buffer_count = 1,
}, @ptrCast(&command_buffer));
return .init(command_buffer, self.device.wrapper);
}
pub fn freeTransferCommandBuffer(self: *const Engine, command_buffer: vk.CommandBufferProxy) void {
self.device.freeCommandBuffers(self.transfer_command_pool, 1, @ptrCast(&command_buffer));
}
pub fn freeTransientTransferCommandBuffer(self: *const Engine, command_buffer: vk.CommandBufferProxy) void {
self.device.freeCommandBuffers(self.transient_transfer_command_pool, 1, @ptrCast(&command_buffer));
}
pub fn freeGraphicsCommandBuffer(self: *const Engine, command_buffer: vk.CommandBufferProxy) void {
self.device.freeCommandBuffers(self.graphics_command_pool, 1, @ptrCast(&command_buffer));
}
pub fn submitTransferCommandBuffer(self: *const Engine, command_buffer: vk.CommandBufferProxy, fence: vk.Fence) !void {
const submits = [_]vk.SubmitInfo{
.{
.command_buffer_count = 1,
.p_command_buffers = @ptrCast(&command_buffer.handle),
},
};
try self.device.queueSubmit(self.transfer_queue.handle, submits.len, &submits, fence);
}
pub fn submitGraphicsCommandBuffer(self: *const Engine, command_buffer: vk.CommandBufferProxy, fence: vk.Fence) !void {
const submits = [_]vk.SubmitInfo{
.{
.command_buffer_count = 1,
.p_command_buffers = @ptrCast(&command_buffer.handle),
},
};
try self.device.queueSubmit(self.graphics_queue.handle, submits.len, &submits, fence);
}
fn debugUtilsMessengerCallback(
severity: vk.DebugUtilsMessageSeverityFlagsEXT,
message_type: vk.DebugUtilsMessageTypeFlagsEXT,

View File

@@ -2,6 +2,7 @@ const std = @import("std");
const vk = @import("vulkan");
const CommandBuffer = @import("CommandBuffer.zig").CommandBuffer;
const Engine = @import("Engine.zig");
const StagingBuffer = @import("StagingBuffer.zig");
const TargetQueue = @import("TargetQueue.zig").TargetQueue;
@@ -146,22 +147,17 @@ pub fn GenericBuffer(comptime Header: type, comptime Element: type) type {
@memcpy(staging_memory[array_write_offset..write_size], std.mem.sliceAsBytes(write_info.elements));
staging_buffer.unmap(engine);
const command_buffer = try engine.allocateTransientTransferCommandBuffer();
defer engine.freeTransientTransferCommandBuffer(command_buffer);
var command_buffer: CommandBuffer(.transfer, .transient) = try .init(engine);
defer command_buffer.deinit(engine);
try command_buffer.beginCommandBuffer(&.{ .flags = .{ .one_time_submit_bit = true } });
command_buffer.copyBuffer(
staging_buffer.buffer,
self.buffer,
@intCast(regions.items.len),
regions.items.ptr,
);
try command_buffer.beginCommandBuffer(.{});
command_buffer.copyBuffer(staging_buffer.buffer, self.buffer, regions.items);
try command_buffer.endCommandBuffer();
const fence = try engine.createFence(.{});
defer engine.destroyFence(fence);
try engine.submitTransferCommandBuffer(command_buffer, fence);
try command_buffer.submit(engine, .{ .fence = fence });
try engine.waitForFence(fence);
}
@@ -172,14 +168,6 @@ pub fn GenericBuffer(comptime Header: type, comptime Element: type) type {
const data_size = std.math.cast(u32, data.len) orelse return error.Overflow;
std.debug.assert(data_offset + data_size <= size);
const regions = [_]vk.BufferCopy{
.{
.src_offset = 0,
.dst_offset = data_offset,
.size = data_size,
},
};
var staging_buffer = try StagingBuffer.init(engine, .{
.target_queue = self.target_queue,
.capacity = data_size,
@@ -190,22 +178,27 @@ pub fn GenericBuffer(comptime Header: type, comptime Element: type) type {
@memcpy(staging_memory, data);
staging_buffer.unmap(engine);
const command_buffer = try engine.allocateTransientTransferCommandBuffer();
defer engine.freeTransientTransferCommandBuffer(command_buffer);
const command_buffer: CommandBuffer(.tranfer, .transient) = try .init(engine);
defer command_buffer.deinit(engine);
try command_buffer.beginCommandBuffer(&.{ .flags = .{ .one_time_submit_bit = true } });
try command_buffer.beginCommandBuffer(.{});
command_buffer.copyBuffer(
staging_buffer.buffer,
self.buffer,
@intCast(regions.items.len),
regions.items.ptr,
&.{
.{
.src_offset = 0,
.dst_offset = data_offset,
.size = data_size,
},
},
);
try command_buffer.endCommandBuffer();
const fence = try engine.createFence(.{});
defer engine.destroyFence(fence);
try engine.submitTransferCommandBuffer(command_buffer, fence);
try command_buffer.submit(engine, .{ .fence = fence });
try engine.waitForFence(fence);
}
};

5
src/engine/QueueType.zig Normal file
View File

@@ -0,0 +1,5 @@
pub const QueueType = enum {
graphics,
compute,
transfer,
};

View File

@@ -3,9 +3,11 @@ const std = @import("std");
const vk = @import("vulkan");
const CommandBuffer = @import("CommandBuffer.zig").CommandBuffer;
const Engine = @import("Engine.zig");
const QSM = @import("QueueSharingMode.zig");
const Texture = @import("Texture.zig");
const WaitSemaphore = @import("WaitSemaphore.zig");
params: Params,
render_pass: vk.RenderPass,
@@ -117,7 +119,7 @@ pub fn deinit(self: *Swapchain, engine: *Engine) void {
const allocator = engine.vk_allocator.allocator;
for (self.swapchain_images) |swapchain_image| {
for (self.swapchain_images) |*swapchain_image| {
swapchain_image.deinit(engine);
}
allocator.free(self.swapchain_images);
@@ -178,7 +180,7 @@ pub fn recreate(self: *Swapchain, engine: *Engine) !void {
// to revert on error. Instead, we set the current swapchain and images to
// null and deinit the old swapchain and images.
for (old_swapchain_images) |swapchain_image| {
for (old_swapchain_images) |*swapchain_image| {
swapchain_image.deinit(engine);
}
allocator.free(self.swapchain_images);
@@ -219,7 +221,7 @@ pub fn recreate(self: *Swapchain, engine: *Engine) !void {
var swapchain_images: std.ArrayList(SwapchainImage) = try .initCapacity(allocator, images.len);
errdefer swapchain_images.deinit(allocator);
errdefer for (swapchain_images.items) |x| x.deinit(engine);
errdefer for (swapchain_images.items) |*x| x.deinit(engine);
for (images) |image| {
swapchain_images.appendAssumeCapacity(try SwapchainImage.init(engine, .{
@@ -234,7 +236,7 @@ pub fn recreate(self: *Swapchain, engine: *Engine) !void {
break :blk try swapchain_images.toOwnedSlice(allocator);
};
errdefer {
for (new_swapchain_images) |swapchain_image| {
for (new_swapchain_images) |*swapchain_image| {
swapchain_image.deinit(engine);
}
allocator.free(new_swapchain_images);
@@ -262,14 +264,9 @@ pub fn recreate(self: *Swapchain, engine: *Engine) !void {
self.semaphore_image_acquired = semaphore_image_acquired;
}
pub const WaitSemaphore = struct {
semaphore: vk.Semaphore,
stage_flags: vk.PipelineStageFlags = .{ .top_of_pipe_bit = true },
};
pub const PresentInfo = struct {
command_buffer: vk.CommandBuffer,
wait_semaphores: []const WaitSemaphore,
command_buffer: CommandBuffer(.graphics, .transient),
wait_semaphores: []const WaitSemaphore = &.{},
};
pub fn present(self: *Swapchain, engine: *Engine, present_info: PresentInfo) !PresentResult {
@@ -281,42 +278,29 @@ pub fn present(self: *Swapchain, engine: *Engine, present_info: PresentInfo) !Pr
const current_swapchain_image = &self.swapchain_images[self.image_index];
try engine.waitForFence(current_swapchain_image.fence);
if (current_swapchain_image.command_buffer != .null_handle) {
device.freeCommandBuffers(engine.graphics_command_pool, 1, @ptrCast(&current_swapchain_image.command_buffer));
current_swapchain_image.command_buffer = .null_handle;
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);
// --- SUBMIT COMMAND BUFFER -----------------------------------------------
var wait_semaphores = std.MultiArrayList(WaitSemaphore){};
var wait_semaphores: std.ArrayList(WaitSemaphore) = try .initCapacity(allocator, 1 + present_info.wait_semaphores.len);
defer wait_semaphores.deinit(allocator);
try wait_semaphores.ensureTotalCapacity(allocator, 1 + present_info.wait_semaphores.len);
wait_semaphores.appendAssumeCapacity(.{
.semaphore = current_swapchain_image.semaphore_image_acquired,
.stage_flags = .{ .top_of_pipe_bit = true },
});
for (present_info.wait_semaphores) |wait_semaphore| {
wait_semaphores.appendAssumeCapacity(wait_semaphore);
}
const signal_semaphores = [_]vk.Semaphore{
current_swapchain_image.semaphore_render_finished,
};
wait_semaphores.appendSliceAssumeCapacity(present_info.wait_semaphores);
current_swapchain_image.command_buffer = present_info.command_buffer;
try device.queueSubmit(engine.graphics_queue.handle, 1, &.{
.{
.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 = 1,
.p_command_buffers = @ptrCast(&present_info.command_buffer),
.signal_semaphore_count = signal_semaphores.len,
.p_signal_semaphores = &signal_semaphores,
},
}, current_swapchain_image.fence);
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,
});
// --- PRESENT CURRENT FRAME -----------------------------------------------
@@ -415,7 +399,7 @@ const SwapchainImage = struct {
semaphore_render_finished: vk.Semaphore,
fence: vk.Fence,
framebuffer: vk.Framebuffer,
command_buffer: vk.CommandBuffer,
command_buffer: ?CommandBuffer(.graphics, .transient),
const InitProps = struct {
image: vk.Image,
@@ -467,18 +451,23 @@ const SwapchainImage = struct {
.semaphore_render_finished = semaphore_render_finished,
.fence = fence,
.framebuffer = framebuffer,
.command_buffer = .null_handle,
.command_buffer = null,
};
}
fn deinit(self: SwapchainImage, engine: *Engine) void {
fn deinit(self: *SwapchainImage, engine: *Engine) void {
std.log.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);
self.* = undefined;
}
};

View File

@@ -3,6 +3,7 @@ const std = @import("std");
const vk = @import("vulkan");
const CommandBuffer = @import("CommandBuffer.zig").CommandBuffer;
const Engine = @import("Engine.zig");
const StagingBuffer = @import("StagingBuffer.zig");
const TargetQueue = @import("TargetQueue.zig").TargetQueue;
@@ -194,64 +195,53 @@ pub fn writeRaw(self: Texture, engine: *Engine, data: []const u8) !void {
// --- TRANSITION TO TRANSFER_DST_OPTIMAL AND COPY -----------------
const transfer_command_buffer = try engine.allocateTransientTransferCommandBuffer();
defer engine.freeTransientTransferCommandBuffer(transfer_command_buffer);
var transfer_command_buffer: CommandBuffer(.transfer, .transient) = try .init(engine);
defer transfer_command_buffer.deinit(engine);
try transfer_command_buffer.beginCommandBuffer(&.{ .flags = .{ .one_time_submit_bit = true } });
try transfer_command_buffer.beginCommandBuffer(.{});
const transfer_transition_barriers = [_]vk.ImageMemoryBarrier{
.{
.src_access_mask = .{},
.dst_access_mask = .{ .transfer_write_bit = true },
.old_layout = .undefined,
.new_layout = .transfer_dst_optimal,
.src_queue_family_index = vk.QUEUE_FAMILY_IGNORED,
.dst_queue_family_index = vk.QUEUE_FAMILY_IGNORED,
.image = self.image,
.subresource_range = .{
.aspect_mask = .{ .color_bit = true },
.base_mip_level = 0,
.level_count = 1,
.base_array_layer = 0,
.layer_count = 1,
transfer_command_buffer.pipelineBarrier(.{
.src_stage_mask = .{ .top_of_pipe_bit = true },
.dst_stage_mask = .{ .transfer_bit = true },
.image_memory_barriers = &.{
.{
.src_access_mask = .{},
.dst_access_mask = .{ .transfer_write_bit = true },
.old_layout = .undefined,
.new_layout = .transfer_dst_optimal,
.src_queue_family_index = vk.QUEUE_FAMILY_IGNORED,
.dst_queue_family_index = vk.QUEUE_FAMILY_IGNORED,
.image = self.image,
.subresource_range = .{
.aspect_mask = .{ .color_bit = true },
.base_mip_level = 0,
.level_count = 1,
.base_array_layer = 0,
.layer_count = 1,
},
},
},
};
transfer_command_buffer.pipelineBarrier(
.{ .top_of_pipe_bit = true },
.{ .transfer_bit = true },
.{},
0,
null,
0,
null,
transfer_transition_barriers.len,
&transfer_transition_barriers,
);
const regions = [_]vk.BufferImageCopy{
.{
.buffer_offset = 0,
.buffer_row_length = self.width,
.buffer_image_height = self.height,
.image_subresource = .{
.aspect_mask = .{ .color_bit = true },
.mip_level = 0,
.base_array_layer = 0,
.layer_count = 1,
},
.image_offset = .{ .x = 0, .y = 0, .z = 0 },
.image_extent = .{ .width = self.width, .height = self.height, .depth = 1 },
},
};
});
transfer_command_buffer.copyBufferToImage(
staging_buffer.buffer,
self.image,
.transfer_dst_optimal,
regions.len,
&regions,
&.{
.{
.buffer_offset = 0,
.buffer_row_length = self.width,
.buffer_image_height = self.height,
.image_subresource = .{
.aspect_mask = .{ .color_bit = true },
.mip_level = 0,
.base_array_layer = 0,
.layer_count = 1,
},
.image_offset = .{ .x = 0, .y = 0, .z = 0 },
.image_extent = .{ .width = self.width, .height = self.height, .depth = 1 },
},
},
);
try transfer_command_buffer.endCommandBuffer();
@@ -259,73 +249,48 @@ pub fn writeRaw(self: Texture, engine: *Engine, data: []const u8) !void {
const semaphore = try engine.createSemaphore();
defer engine.destroySemaphore(semaphore);
const transfer_submits = [_]vk.SubmitInfo{
.{
.command_buffer_count = 1,
.p_command_buffers = @ptrCast(&transfer_command_buffer.handle),
.signal_semaphore_count = 1,
.p_signal_semaphores = @ptrCast(&semaphore),
},
};
try engine.device.queueSubmit(engine.transfer_queue.handle, transfer_submits.len, &transfer_submits, .null_handle);
try transfer_command_buffer.submit(engine, .{
.signal_semaphores = &.{semaphore},
});
// --- TRANSITION TO SHADER_READ_ONLY_OPTIMAL ----------------------
const graphics_command_buffer = try engine.allocateGraphicsCommandBuffer();
defer engine.freeGraphicsCommandBuffer(graphics_command_buffer);
var graphics_command_buffer: CommandBuffer(.graphics, .transient) = try .init(engine);
defer graphics_command_buffer.deinit(engine);
try graphics_command_buffer.beginCommandBuffer(&.{ .flags = .{ .one_time_submit_bit = true } });
try graphics_command_buffer.beginCommandBuffer(.{});
const graphics_transition_barriers = [_]vk.ImageMemoryBarrier{
.{
.src_access_mask = .{ .transfer_write_bit = true },
.dst_access_mask = .{ .shader_read_bit = true },
.old_layout = .transfer_dst_optimal,
.new_layout = .shader_read_only_optimal,
.src_queue_family_index = vk.QUEUE_FAMILY_IGNORED,
.dst_queue_family_index = vk.QUEUE_FAMILY_IGNORED,
.image = self.image,
.subresource_range = .{
.aspect_mask = .{ .color_bit = true },
.base_mip_level = 0,
.level_count = 1,
.base_array_layer = 0,
.layer_count = 1,
graphics_command_buffer.pipelineBarrier(.{
.src_stage_mask = .{ .transfer_bit = true },
.dst_stage_mask = .{ .fragment_shader_bit = true },
.image_memory_barriers = &.{
.{
.src_access_mask = .{ .transfer_write_bit = true },
.dst_access_mask = .{ .shader_read_bit = true },
.old_layout = .transfer_dst_optimal,
.new_layout = .shader_read_only_optimal,
.src_queue_family_index = vk.QUEUE_FAMILY_IGNORED,
.dst_queue_family_index = vk.QUEUE_FAMILY_IGNORED,
.image = self.image,
.subresource_range = .{
.aspect_mask = .{ .color_bit = true },
.base_mip_level = 0,
.level_count = 1,
.base_array_layer = 0,
.layer_count = 1,
},
},
},
};
graphics_command_buffer.pipelineBarrier(
.{ .transfer_bit = true },
.{ .fragment_shader_bit = true },
.{},
0,
null,
0,
null,
graphics_transition_barriers.len,
&graphics_transition_barriers,
);
});
try graphics_command_buffer.endCommandBuffer();
const wait_stage_masks = [_]vk.PipelineStageFlags{
.{ .top_of_pipe_bit = true },
};
const graphics_submits = [_]vk.SubmitInfo{
.{
.command_buffer_count = 1,
.p_command_buffers = @ptrCast(&graphics_command_buffer.handle),
.wait_semaphore_count = 1,
.p_wait_semaphores = @ptrCast(&semaphore),
.p_wait_dst_stage_mask = &wait_stage_masks,
},
};
const fence = try engine.createFence(.{});
defer engine.destroyFence(fence);
try engine.device.queueSubmit(engine.graphics_queue.handle, graphics_submits.len, &graphics_submits, fence);
try graphics_command_buffer.submit(engine, .{
.wait_semaphores = &.{.{ .semaphore = semaphore }},
.fence = fence,
});
try engine.waitForFence(fence);
}

4
src/engine/Transient.zig Normal file
View File

@@ -0,0 +1,4 @@
pub const Transient = enum {
persistent,
transient,
};

View File

@@ -0,0 +1,4 @@
const vk = @import("vulkan");
semaphore: vk.Semaphore,
stage_flags: vk.PipelineStageFlags = .{ .top_of_pipe_bit = true },