//! A thin wrapper around `@import("vk").CommandBufferProxy` that ziggifies //! arguments by turning pointer-length pairs into slices and integrates with //! different queues from `Engine` struct. //! //! The wrapper retains information about the queue type, such that it can //! submit to the correct queue preallocated in the `Engine` struct when calling //! `CommandBuffer.submit`. const CommandBuffer = @This(); const std = @import("std"); const ctx = @import("../AppContext.zig"); const vk = @import("vulkan"); const Engine = @import("Engine.zig"); const QueueType = @import("QueueType.zig").QueueType; proxy: vk.CommandBufferProxy, queue_type: QueueType, pub fn init(queue_type: QueueType) !CommandBuffer { const engine = ctx.engine; const handle = try engine.allocateCommandBuffer(.{ .queue_type = queue_type }); return .{ .proxy = .init(handle, engine.device.wrapper), .queue_type = queue_type, }; } pub fn deinit(self: *CommandBuffer) void { const engine = ctx.engine; engine.freeCommandBuffer(.{ .queue_type = self.queue_type, .command_buffer = self.proxy.handle, }); self.* = undefined; } pub fn submit(self: CommandBuffer, submit_info: Engine.SubmitInfo) !void { const engine = ctx.engine; try engine.queueSubmit(self.queue_type, self.proxy.handle, submit_info); } // --- VULKAN WRAPPERS --------------------------------------------------------- fn ToVkType(comptime T: type) type { if (!std.meta.hasFn(T, "toVk")) { @compileError("Type " ++ T ++ " has no toVk method."); } const to_vk_type_info = @typeInfo(@TypeOf(@field(T, "toVk"))); return to_vk_type_info.@"fn".return_type.?; } fn toVkOptional(comptime T: type, maybe_value: ?T) !?*const ToVkType(T) { const allocator_frame = ctx.allocator_frame; if (maybe_value) |value| { const ptr = try allocator_frame.create(ToVkType(T)); if (std.meta.hasFn(T, "toVk")) { ptr.* = value.toVk(); } return ptr; } else { return null; } } fn toVkMulti(comptime T: type, values: []const T) ![*]const ToVkType(T) { const allocator_frame = ctx.allocator_frame; const slice = try allocator_frame.alloc(ToVkType(T), values.len); for (slice, values) |*y, x| { y.* = x.toVk(); } return slice.ptr; } 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 RenderingAttachmentInfo = struct { image_view: vk.ImageView = .null_handle, image_layout: vk.ImageLayout, resolve_mode: vk.ResolveModeFlags = .{}, resolve_image_view: vk.ImageView = .null_handle, resolve_image_layout: vk.ImageLayout = .undefined, load_op: vk.AttachmentLoadOp, store_op: vk.AttachmentStoreOp, clear_value: vk.ClearValue, pub fn toVk(self: RenderingAttachmentInfo) vk.RenderingAttachmentInfo { return .{ .image_view = self.image_view, .image_layout = self.image_layout, .resolve_mode = self.resolve_mode, .resolve_image_view = self.resolve_image_view, .resolve_image_layout = self.resolve_image_layout, .load_op = self.load_op, .store_op = self.store_op, .clear_value = self.clear_value, }; } }; pub const RenderingInfo = struct { flags: vk.RenderingFlags = .{}, render_area: vk.Rect2D, layer_count: u32, view_mask: u32, color_attachments: []const RenderingAttachmentInfo = &.{}, depth_attachment: ?RenderingAttachmentInfo = null, stencil_attachment: ?RenderingAttachmentInfo = null, pub fn toVk(self: RenderingInfo) !vk.RenderingInfo { return .{ .flags = self.flags, .render_area = self.render_area, .layer_count = self.layer_count, .view_mask = self.view_mask, .color_attachment_count = @intCast(self.color_attachments.len), .p_color_attachments = try toVkMulti(RenderingAttachmentInfo, self.color_attachments), .p_depth_attachment = try toVkOptional(RenderingAttachmentInfo, self.depth_attachment), .p_stencil_attachment = try toVkOptional(RenderingAttachmentInfo, self.stencil_attachment), }; } }; pub const VertexBufferBinding = struct { buffer: vk.Buffer, offset: usize, pub fn transpose(vertex_buffer_bindings: []const VertexBufferBinding) !struct { []const vk.Buffer, []const usize } { const allocator_frame = ctx.allocator_frame; var t: std.MultiArrayList(VertexBufferBinding) = .empty; try t.ensureTotalCapacity(allocator_frame, vertex_buffer_bindings.len); for (vertex_buffer_bindings) |binding| { t.appendAssumeCapacity(binding); } return .{ t.items(.buffer), t.items(.offset), }; } }; pub fn beginCommandBuffer(self: CommandBuffer) !void { try self.proxy.beginCommandBuffer(&.{ .flags = .{ .one_time_submit_bit = true, }, }); } pub fn beginRendering(self: CommandBuffer, rendering_info: RenderingInfo) !void { const rendering_info_vk = try rendering_info.toVk(); self.proxy.beginRendering(&rendering_info_vk); } pub fn bindDescriptorSet( self: CommandBuffer, pipeline_bind_point: vk.PipelineBindPoint, layout: vk.PipelineLayout, set: u32, descriptor_set: vk.DescriptorSet, dynamic_offset: ?u32, ) void { self.bindDescriptorSets( pipeline_bind_point, layout, set, &.{descriptor_set}, if (dynamic_offset) |x| &.{x} else &.{}, ); } pub fn bindDescriptorSets( self: CommandBuffer, 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, descriptor_sets, dynamic_offsets, ); } pub fn bindIndexBuffer(self: CommandBuffer, buffer: vk.Buffer, offset: u64, index_type: vk.IndexType) void { self.proxy.bindIndexBuffer(buffer, offset, index_type); } pub fn bindPipeline(self: CommandBuffer, pipeline_bind_point: vk.PipelineBindPoint, pipeline: vk.Pipeline) void { self.proxy.bindPipeline(pipeline_bind_point, pipeline); } pub fn bindVertexBuffer(self: CommandBuffer, index: u32, binding: VertexBufferBinding) !void { self.proxy.bindVertexBuffers(index, &.{binding.buffer}, &.{binding.offset}); } pub fn bindVertexBuffers(self: CommandBuffer, first_binding: u32, bindings: []const VertexBufferBinding) !void { const buffers, const offsets = try VertexBufferBinding.transpose(bindings); self.proxy.bindVertexBuffers(first_binding, buffers, offsets); } pub fn copyBuffer( self: CommandBuffer, src_buffer: vk.Buffer, dst_buffer: vk.Buffer, regions: []const vk.BufferCopy, ) void { self.proxy.copyBuffer(src_buffer, dst_buffer, regions); } pub fn copyBufferToImage( self: CommandBuffer, 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, regions); } pub fn drawIndexed(self: CommandBuffer, 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 fn endCommandBuffer(self: CommandBuffer) !void { try self.proxy.endCommandBuffer(); } pub fn endRendering(self: CommandBuffer) void { self.proxy.endRendering(); } pub fn pipelineBarrier(self: CommandBuffer, barrier: PipelineBarrier) void { self.proxy.pipelineBarrier( barrier.src_stage_mask, barrier.dst_stage_mask, barrier.dependency_flags, barrier.memory_barriers, barrier.buffer_memory_barriers, barrier.image_memory_barriers, ); } pub fn setScissor(self: CommandBuffer, index: u32, scissor: vk.Rect2D) void { self.proxy.setScissor(index, &.{scissor}); } pub fn setScissors(self: CommandBuffer, first_scissor: u32, scissors: []const vk.Rect2D) void { self.proxy.setScissor(first_scissor, scissors); } pub fn setViewport(self: CommandBuffer, index: u32, viewports: vk.Viewport) void { self.proxy.setViewport(index, &.{viewports}); } pub fn setViewports(self: CommandBuffer, first_viewport: u32, viewports: []const vk.Viewport) void { self.proxy.setViewport(first_viewport, viewports); }