Files
VulkanDriver/src/intel/FlintCommandBuffer.zig
T
kbz_8 04ff263b40
Mirror Gitea refs to GitHub / mirror (push) Successful in 18s
Test / build_and_test (push) Successful in 2m12s
Build / build (push) Successful in 3m21s
[Flint] adding compute resource layout lowering
2026-08-14 20:41:31 +02:00

534 lines
19 KiB
Zig

const std = @import("std");
const vk = @import("vulkan");
const base = @import("base");
const kmd = @import("kmd.zig");
const VkError = base.VkError;
const FlintDevice = @import("FlintDevice.zig");
const FlintDescriptorSet = @import("FlintDescriptorSet.zig");
const FlintPipeline = @import("FlintPipeline.zig");
const MemoryRange = @import("MemoryRange.zig");
const copy = @import("copy_commands.zig");
const blitter = @import("blitter.zig");
const Self = @This();
pub const Interface = base.CommandBuffer;
interface: Interface,
batch: std.ArrayList(u32),
relocations: std.ArrayList(kmd.Relocation),
bound_compute_pipeline: ?*FlintPipeline,
bound_compute_descriptor_sets: [base.vulkan_max_descriptor_sets]?*FlintDescriptorSet,
pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const vk.CommandBufferAllocateInfo) VkError!*Self {
const self = allocator.create(Self) catch return VkError.OutOfHostMemory;
errdefer allocator.destroy(self);
var interface = try Interface.init(device, allocator, info);
interface.vtable = &.{ .destroy = destroy };
interface.dispatch_table = &.{
.begin = begin,
.beginQuery = beginQuery,
.beginRenderPass = beginRenderPass,
.bindDescriptorSets = bindDescriptorSets,
.bindPipeline = bindPipeline,
.bindIndexBuffer = bindIndexBuffer,
.bindVertexBuffer = bindVertexBuffer,
.blitImage = blitImage,
.clearAttachment = clearAttachment,
.clearColorImage = clearColorImage,
.clearDepthStencilImage = clearDepthStencilImage,
.copyBuffer = copyBuffer,
.copyBufferToImage = copyBufferToImage,
.copyImage = copyImage,
.copyImageToBuffer = copyImageToBuffer,
.copyQueryPoolResults = copyQueryPoolResults,
.dispatch = dispatch,
.dispatchBase = dispatchBase,
.dispatchIndirect = dispatchIndirect,
.draw = draw,
.drawIndexed = drawIndexed,
.drawIndexedIndirect = drawIndexedIndirect,
.drawIndirect = drawIndirect,
.end = end,
.endQuery = endQuery,
.endRenderPass = endRenderPass,
.executeCommands = executeCommands,
.fillBuffer = fillBuffer,
.nextSubpass = nextSubpass,
.pipelineBarrier = pipelineBarrier,
.pushConstants = pushConstants,
.reset = reset,
.resetQueryPool = resetQueryPool,
.resetEvent = resetEvent,
.resolveImage = resolveImage,
.setEvent = setEvent,
.setBlendConstants = setBlendConstants,
.setDepthBias = setDepthBias,
.setDepthBounds = setDepthBounds,
.setDeviceMask = setDeviceMask,
.setLineWidth = setLineWidth,
.setScissor = setScissor,
.setStencilCompareMask = setStencilCompareMask,
.setStencilReference = setStencilReference,
.setStencilWriteMask = setStencilWriteMask,
.setViewport = setViewport,
.updateBuffer = updateBuffer,
.waitEvent = waitEvent,
.writeTimestamp = writeTimestamp,
};
self.* = .{
.interface = interface,
.batch = .empty,
.relocations = .empty,
.bound_compute_pipeline = null,
.bound_compute_descriptor_sets = @splat(null),
};
return self;
}
pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const command_allocator = self.interface.host_allocator.allocator();
self.batch.deinit(command_allocator);
self.relocations.deinit(command_allocator);
allocator.destroy(self);
}
pub fn submitGpuBatch(self: *Self, syncs: []const kmd.SyncDependency) VkError!void {
try self.interface.submit();
defer self.interface.finish() catch @panic("Caught an error while handling an error");
// Empty command buffers still need a no-op submission to carry queue synchronization.
const device: *FlintDevice = @alignCast(@fieldParentPtr("interface", self.interface.owner));
const allocator = self.interface.host_allocator.allocator();
try device.kmd.submitBatch(self.interface.owner.io(), allocator, self.batch.items, self.relocations.items, syncs);
}
pub fn begin(interface: *Interface, info: *const vk.CommandBufferBeginInfo) VkError!void {
_ = interface;
_ = info;
}
pub fn end(interface: *Interface) VkError!void {
_ = interface;
}
pub fn reset(interface: *Interface, flags: vk.CommandBufferResetFlags) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
if (flags.release_resources_bit) {
const command_allocator = self.interface.host_allocator.allocator();
self.batch.clearAndFree(command_allocator);
self.relocations.clearAndFree(command_allocator);
} else {
self.batch.clearRetainingCapacity();
self.relocations.clearRetainingCapacity();
}
self.bound_compute_pipeline = null;
self.bound_compute_descriptor_sets = @splat(null);
}
pub fn emit(self: *Self, dword: u32) VkError!void {
self.batch.append(self.interface.host_allocator.allocator(), dword) catch return VkError.OutOfHostMemory;
}
pub fn emitRelocatedAddress(self: *Self, range: MemoryRange, read: bool, write: bool) VkError!void {
const address_offset = self.batch.items.len * @sizeOf(u32);
try self.emit(@intCast(range.offset));
try self.emit(0);
self.relocations.append(self.interface.host_allocator.allocator(), .{
.target_handle = try range.memory.allocation.handle(),
.offset = @intCast(address_offset),
.delta = @intCast(range.offset),
.read = read,
.write = write,
}) catch return VkError.OutOfHostMemory;
}
pub fn beginQuery(interface: *Interface, pool: *base.QueryPool, query: u32, flags: vk.QueryControlFlags) VkError!void {
_ = interface;
_ = flags;
try pool.begin(query);
}
pub fn endQuery(interface: *Interface, pool: *base.QueryPool, query: u32) VkError!void {
_ = interface;
try pool.end(query);
}
pub fn resetQueryPool(interface: *Interface, pool: *base.QueryPool, first: u32, count: u32) VkError!void {
_ = interface;
try pool.reset(first, count);
}
pub fn beginRenderPass(interface: *Interface, render_pass: *base.RenderPass, framebuffer: *base.Framebuffer, render_area: vk.Rect2D, clear_values: ?[]const vk.ClearValue) VkError!void {
_ = interface;
_ = render_pass;
_ = framebuffer;
_ = render_area;
_ = clear_values;
}
pub fn bindDescriptorSets(interface: *Interface, bind_point: vk.PipelineBindPoint, first_set: u32, sets: [base.vulkan_max_descriptor_sets]?*base.DescriptorSet, dynamic_offsets: []const u32) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
if (bind_point != .compute)
return;
if (dynamic_offsets.len != 0)
return VkError.FeatureNotPresent;
if (first_set >= base.vulkan_max_descriptor_sets)
return VkError.ValidationFailed;
for (sets, 0..) |set, index| {
const base_set = set orelse break;
const destination = first_set + index;
if (destination >= base.vulkan_max_descriptor_sets)
return VkError.ValidationFailed;
self.bound_compute_descriptor_sets[destination] = @alignCast(@fieldParentPtr("interface", base_set));
}
}
pub fn bindPipeline(interface: *Interface, bind_point: vk.PipelineBindPoint, pipeline: *base.Pipeline) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
if (bind_point != .compute)
return;
const flint_pipeline: *FlintPipeline = @alignCast(@fieldParentPtr("interface", pipeline));
self.bound_compute_pipeline = flint_pipeline;
}
pub fn bindIndexBuffer(interface: *Interface, buffer: *base.Buffer, offset: usize, index_type: vk.IndexType) VkError!void {
_ = interface;
_ = buffer;
_ = offset;
_ = index_type;
}
pub fn bindVertexBuffer(interface: *Interface, index: usize, buffer: *base.Buffer, offset: usize) VkError!void {
_ = interface;
_ = index;
_ = buffer;
_ = offset;
}
pub fn blitImage(interface: *Interface, src: *base.Image, src_layout: vk.ImageLayout, dst: *base.Image, dst_layout: vk.ImageLayout, regions: []const vk.ImageBlit, filter: vk.Filter) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
_ = src_layout;
_ = dst_layout;
if (filter != .nearest) return VkError.FeatureNotPresent;
for (regions) |region|
try blitter.blitImageRegion(self, src, dst, region);
}
pub fn clearAttachment(interface: *Interface, attachment: vk.ClearAttachment, rect: vk.ClearRect) VkError!void {
_ = interface;
_ = attachment;
_ = rect;
}
pub fn clearColorImage(interface: *Interface, image: *base.Image, layout: vk.ImageLayout, color: *const vk.ClearColorValue, range: vk.ImageSubresourceRange) VkError!void {
_ = interface;
_ = image;
_ = layout;
_ = color;
_ = range;
}
pub fn clearDepthStencilImage(interface: *Interface, image: *base.Image, layout: vk.ImageLayout, value: *const vk.ClearDepthStencilValue, range: vk.ImageSubresourceRange) VkError!void {
_ = interface;
_ = image;
_ = layout;
_ = value;
_ = range;
}
pub fn copyBuffer(interface: *Interface, src: *base.Buffer, dst: *base.Buffer, regions: []const vk.BufferCopy) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
for (regions) |region| {
const src_range = try copy.copyRangeFromRegion(src, region.src_offset, region.size);
const dst_range = try copy.copyRangeFromRegion(dst, region.dst_offset, region.size);
try copy.emitLinearCopy(self, src_range, dst_range);
}
}
pub fn copyBufferToImage(interface: *Interface, src: *base.Buffer, dst: *base.Image, dst_layout: vk.ImageLayout, regions: []const vk.BufferImageCopy) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
_ = dst_layout;
for (regions) |region|
try copy.copyBufferImage(self, src, dst, region, true);
}
pub fn copyImage(interface: *Interface, src: *base.Image, src_layout: vk.ImageLayout, dst: *base.Image, dst_layout: vk.ImageLayout, regions: []const vk.ImageCopy) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
_ = src_layout;
_ = dst_layout;
for (regions) |region|
try copy.copyImage(self, src, dst, region);
}
pub fn copyImageToBuffer(interface: *Interface, src: *base.Image, src_layout: vk.ImageLayout, dst: *base.Buffer, regions: []const vk.BufferImageCopy) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
_ = src_layout;
for (regions) |region|
try copy.copyBufferImage(self, dst, src, region, false);
}
pub fn copyQueryPoolResults(interface: *Interface, pool: *base.QueryPool, first: u32, count: u32, dst: *base.Buffer, offset: vk.DeviceSize, stride: vk.DeviceSize, flags: vk.QueryResultFlags) VkError!void {
_ = interface;
_ = pool;
_ = first;
_ = count;
_ = dst;
_ = offset;
_ = stride;
_ = flags;
}
pub fn dispatch(interface: *Interface, group_count_x: u32, group_count_y: u32, group_count_z: u32) VkError!void {
try dispatchBase(interface, 0, 0, 0, group_count_x, group_count_y, group_count_z);
}
pub fn dispatchBase(interface: *Interface, base_group_x: u32, base_group_y: u32, base_group_z: u32, group_count_x: u32, group_count_y: u32, group_count_z: u32) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
if (group_count_x == 0 or group_count_y == 0 or group_count_z == 0)
return;
inline for ([_]struct { u32, u32 }{
.{ base_group_x, group_count_x },
.{ base_group_y, group_count_y },
.{ base_group_z, group_count_z },
}) |dimension| {
const group_end = std.math.add(u32, dimension[0], dimension[1]) catch return VkError.ValidationFailed;
if (group_end > 65535)
return VkError.ValidationFailed;
}
const pipeline = self.bound_compute_pipeline orelse return VkError.ValidationFailed;
const artifact = pipeline.computeArtifact() orelse return VkError.FeatureNotPresent;
for (artifact.resources.bindings) |resource| {
if (resource.set >= base.vulkan_max_descriptor_sets)
return VkError.ValidationFailed;
const descriptor_set = self.bound_compute_descriptor_sets[resource.set] orelse return VkError.ValidationFailed;
const expected_layout = pipeline.interface.layout.set_layouts[resource.set] orelse return VkError.ValidationFailed;
if (descriptor_set.interface.layout != expected_layout)
return VkError.ValidationFailed;
const descriptor = try descriptor_set.getBuffer(resource.binding, 0);
const buffer = descriptor.buffer orelse return VkError.ValidationFailed;
if (!buffer.usage.storage_buffer_bit or buffer.memory == null)
return VkError.ValidationFailed;
}
}
pub fn setDeviceMask(interface: *Interface, device_mask: u32) VkError!void {
_ = interface;
_ = device_mask;
}
pub fn dispatchIndirect(interface: *Interface, buffer: *base.Buffer, offset: vk.DeviceSize) VkError!void {
_ = interface;
_ = buffer;
_ = offset;
}
pub fn draw(interface: *Interface, vertex_count: usize, instance_count: usize, first_vertex: usize, first_instance: usize) VkError!void {
_ = interface;
_ = vertex_count;
_ = instance_count;
_ = first_vertex;
_ = first_instance;
}
pub fn drawIndexed(interface: *Interface, index_count: usize, instance_count: usize, first_index: usize, vertex_offset: i32, first_instance: usize) VkError!void {
_ = interface;
_ = index_count;
_ = instance_count;
_ = first_index;
_ = vertex_offset;
_ = first_instance;
}
pub fn drawIndexedIndirect(interface: *Interface, buffer: *base.Buffer, offset: usize, count: usize, stride: usize) VkError!void {
_ = interface;
_ = buffer;
_ = offset;
_ = count;
_ = stride;
}
pub fn drawIndirect(interface: *Interface, buffer: *base.Buffer, offset: usize, count: usize, stride: usize) VkError!void {
_ = interface;
_ = buffer;
_ = offset;
_ = count;
_ = stride;
}
pub fn endRenderPass(interface: *Interface) VkError!void {
_ = interface;
}
pub fn executeCommands(interface: *Interface, commands: *Interface) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const secondary: *Self = @alignCast(@fieldParentPtr("interface", commands));
const allocator = self.interface.host_allocator.allocator();
const relocation_offset = self.batch.items.len * @sizeOf(u32);
self.batch.appendSlice(allocator, secondary.batch.items) catch return VkError.OutOfHostMemory;
for (secondary.relocations.items) |relocation| {
self.relocations.append(allocator, .{
.target_handle = relocation.target_handle,
.offset = relocation.offset + relocation_offset,
.delta = relocation.delta,
.read = relocation.read,
.write = relocation.write,
}) catch return VkError.OutOfHostMemory;
}
}
pub fn fillBuffer(interface: *Interface, buffer: *base.Buffer, offset: vk.DeviceSize, size: vk.DeviceSize, data: u32) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const dst_range = try copy.fillRange(buffer, offset, size);
var filled: vk.DeviceSize = 0;
while (filled < dst_range.size) {
const dst_chunk: MemoryRange = .{ .memory = dst_range.memory, .offset = dst_range.offset + filled, .size = @sizeOf(u32) };
try self.emit(kmd.mi_store_data_imm_dword);
try self.emitRelocatedAddress(dst_chunk, false, true);
try self.emit(data);
filled += @sizeOf(u32);
}
}
pub fn updateBuffer(interface: *Interface, buffer: *base.Buffer, offset: vk.DeviceSize, data: []const u8) VkError!void {
_ = interface;
_ = buffer;
_ = offset;
_ = data;
}
pub fn nextSubpass(interface: *Interface, contents: vk.SubpassContents) VkError!void {
_ = interface;
_ = contents;
}
pub fn pipelineBarrier(interface: *Interface, src_stage: vk.PipelineStageFlags, dst_stage: vk.PipelineStageFlags, dependency: vk.DependencyFlags, memory: []const vk.MemoryBarrier, buffers: []const vk.BufferMemoryBarrier, images: []const vk.ImageMemoryBarrier) VkError!void {
_ = interface;
_ = src_stage;
_ = dst_stage;
_ = dependency;
_ = memory;
_ = buffers;
_ = images;
}
pub fn pushConstants(interface: *Interface, stages: vk.ShaderStageFlags, offset: u32, blob: []const u8) VkError!void {
_ = interface;
_ = stages;
_ = offset;
_ = blob;
}
pub fn resetEvent(interface: *Interface, event: *base.Event, stage: vk.PipelineStageFlags) VkError!void {
_ = interface;
_ = stage;
try event.reset();
}
pub fn resolveImage(interface: *Interface, src: *base.Image, src_layout: vk.ImageLayout, dst: *base.Image, dst_layout: vk.ImageLayout, region: vk.ImageResolve) VkError!void {
_ = interface;
_ = src;
_ = src_layout;
_ = dst;
_ = dst_layout;
_ = region;
}
pub fn setEvent(interface: *Interface, event: *base.Event, stage: vk.PipelineStageFlags) VkError!void {
_ = interface;
_ = stage;
try event.signal();
}
pub fn setScissor(interface: *Interface, first: u32, scissor: []const vk.Rect2D) VkError!void {
_ = interface;
_ = first;
_ = scissor;
}
pub fn setViewport(interface: *Interface, first: u32, viewports: []const vk.Viewport) VkError!void {
_ = interface;
_ = first;
_ = viewports;
}
pub fn setBlendConstants(interface: *Interface, constants: [4]f32) VkError!void {
_ = interface;
_ = constants;
}
pub fn setDepthBias(interface: *Interface, constant_factor: f32, clamp: f32, slope_factor: f32) VkError!void {
_ = interface;
_ = constant_factor;
_ = clamp;
_ = slope_factor;
}
pub fn setDepthBounds(interface: *Interface, min: f32, max: f32) VkError!void {
_ = interface;
_ = min;
_ = max;
}
pub fn setLineWidth(interface: *Interface, width: f32) VkError!void {
_ = interface;
_ = width;
}
pub fn setStencilCompareMask(interface: *Interface, face_mask: vk.StencilFaceFlags, compare_mask: u32) VkError!void {
_ = interface;
_ = face_mask;
_ = compare_mask;
}
pub fn setStencilReference(interface: *Interface, face_mask: vk.StencilFaceFlags, reference: u32) VkError!void {
_ = interface;
_ = face_mask;
_ = reference;
}
pub fn setStencilWriteMask(interface: *Interface, face_mask: vk.StencilFaceFlags, write_mask: u32) VkError!void {
_ = interface;
_ = face_mask;
_ = write_mask;
}
pub fn waitEvent(interface: *Interface, event: *base.Event, src_stage: vk.PipelineStageFlags, dst_stage: vk.PipelineStageFlags, memory_barriers: []const vk.MemoryBarrier, buffer_barriers: []const vk.BufferMemoryBarrier, image_barriers: []const vk.ImageMemoryBarrier) VkError!void {
_ = interface;
_ = event;
_ = src_stage;
_ = dst_stage;
_ = memory_barriers;
_ = buffer_barriers;
_ = image_barriers;
}
pub fn writeTimestamp(interface: *Interface, stage: vk.PipelineStageFlags, pool: *base.QueryPool, query: u32) VkError!void {
_ = interface;
_ = stage;
try pool.writeTimestamp(query, 0);
}