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

@@ -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);
}