adding descriptor bindings
Some checks failed
Build / build (push) Successful in 1m5s
Test / build_and_test (push) Failing after 3h8m2s

This commit is contained in:
2026-02-21 03:37:08 +01:00
parent a95e57bd5e
commit d97533082d
12 changed files with 217 additions and 129 deletions

View File

@@ -22,6 +22,7 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v
interface.dispatch_table = &.{
.begin = begin,
.bindDescriptorSets = bindDescriptorSets,
.bindPipeline = bindPipeline,
.clearColorImage = clearColorImage,
.copyBuffer = copyBuffer,
@@ -65,6 +66,15 @@ pub fn reset(interface: *Interface, flags: vk.CommandBufferResetFlags) VkError!v
// Commands ====================================================================================================
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 {
// No-op
_ = interface;
_ = bind_point;
_ = first_set;
_ = sets;
_ = dynamic_offsets;
}
pub fn bindPipeline(interface: *Interface, bind_point: vk.PipelineBindPoint, pipeline: *base.Pipeline) VkError!void {
_ = interface;
_ = pipeline;

View File

@@ -52,7 +52,7 @@ pub fn create(allocator: std.mem.Allocator, instance: *const base.Instance) VkEr
.max_sampler_allocation_count = 4096,
.buffer_image_granularity = 131072,
.sparse_address_space_size = 0,
.max_bound_descriptor_sets = 4,
.max_bound_descriptor_sets = base.VULKAN_MAX_DESCRIPTOR_SETS,
.max_per_stage_descriptor_samplers = 16,
.max_per_stage_descriptor_uniform_buffers = 12,
.max_per_stage_descriptor_storage_buffers = 4,

View File

@@ -6,11 +6,19 @@ const spv = @import("spv");
const VkError = base.VkError;
const Device = base.Device;
const NonDispatchable = base.NonDispatchable;
const ShaderModule = base.ShaderModule;
const SoftDevice = @import("SoftDevice.zig");
const SoftShaderModule = @import("SoftShaderModule.zig");
const Self = @This();
pub const Interface = base.Pipeline;
interface: Interface,
runtimes: []spv.Runtime,
pub fn createCompute(device: *base.Device, allocator: std.mem.Allocator, cache: ?*base.PipelineCache, info: *const vk.ComputePipelineCreateInfo) VkError!*Self {
const self = allocator.create(Self) catch return VkError.OutOfHostMemory;
errdefer allocator.destroy(self);
@@ -21,8 +29,23 @@ pub fn createCompute(device: *base.Device, allocator: std.mem.Allocator, cache:
.destroy = destroy,
};
const soft_device: *SoftDevice = @alignCast(@fieldParentPtr("interface", device));
const module = try NonDispatchable(ShaderModule).fromHandleObject(info.stage.module);
const soft_module: *SoftShaderModule = @alignCast(@fieldParentPtr("interface", module));
const runtimes = allocator.alloc(spv.Runtime, soft_device.workers.getIdCount()) catch return VkError.OutOfHostMemory;
errdefer allocator.free(runtimes);
for (runtimes) |*runtime| {
runtime.* = spv.Runtime.init(allocator, &soft_module.module) catch |err| {
std.log.scoped(.SpvRuntimeInit).err("SPIR-V Runtime failed to initialize, {s}", .{@errorName(err)});
return VkError.Unknown;
};
}
self.* = .{
.interface = interface,
.runtimes = runtimes,
};
return self;
}
@@ -37,13 +60,30 @@ pub fn createGraphics(device: *base.Device, allocator: std.mem.Allocator, cache:
.destroy = destroy,
};
const soft_device: *SoftDevice = @alignCast(@fieldParentPtr("interface", device));
const runtimes = allocator.alloc(spv.Runtime, soft_device.workers.getIdCount()) catch return VkError.OutOfHostMemory;
errdefer allocator.free(runtimes);
//for (runtimes) |*runtime| {
// runtime.* = spv.Runtime.init() catch |err| {
// std.log.scoped(.SpvRuntimeInit).err("SPIR-V Runtime failed to initialize, {s}", .{@errorName(err)});
// return VkError.Unknown;
// };
//}
self.* = .{
.interface = interface,
.runtimes = runtimes,
};
return self;
}
pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
for (self.runtimes) |*runtime| {
runtime.deinit(allocator);
}
allocator.free(self.runtimes);
allocator.destroy(self);
}

View File

@@ -61,7 +61,7 @@ pub fn submit(interface: *Interface, infos: []Interface.SubmitInfo, p_fence: ?*b
defer self.lock.unlockShared();
for (infos) |info| {
// Cloning info to keep them alive until commands dispatch end
// Cloning info to keep them alive until command execution ends
const cloned_info: Interface.SubmitInfo = .{
.command_buffers = info.command_buffers.clone(allocator) catch return VkError.OutOfDeviceMemory,
};
@@ -100,10 +100,10 @@ fn taskRunner(self: *Self, info: Interface.SubmitInfo, p_fence: ?*base.Fence, ru
var device = Device.init(soft_device);
defer device.deinit();
loop: for (info.command_buffers.items) |command_buffer| {
command_buffer.submit() catch continue :loop;
for (info.command_buffers.items) |command_buffer| {
command_buffer.submit() catch continue;
for (command_buffer.commands.items) |command| {
device.dispatch(&command) catch |err| base.errors.errorLoggerContext(err, "the software command dispatcher");
device.execute(&command) catch |err| base.errors.errorLoggerContext(err, "the software command dispatcher");
}
}

View File

@@ -3,6 +3,8 @@ const vk = @import("vulkan");
const base = @import("base");
const spv = @import("spv");
const PipelineState = @import("PipelineState.zig");
const SoftDevice = @import("../SoftDevice.zig");
const SoftPipeline = @import("../SoftPipeline.zig");
@@ -11,19 +13,15 @@ const VkError = base.VkError;
const Self = @This();
device: *SoftDevice,
pipeline: ?*SoftPipeline,
state: *PipelineState,
pub fn init(device: *SoftDevice) Self {
pub fn init(device: *SoftDevice, state: *PipelineState) Self {
return .{
.device = device,
.pipeline = null,
.state = state,
};
}
pub fn destroy(self: *Self) void {
_ = self;
}
pub fn bindPipeline(self: *Self, pipeline: *SoftPipeline) void {
self.pipeline = pipeline;
}

View File

@@ -2,11 +2,14 @@ const std = @import("std");
const vk = @import("vulkan");
const base = @import("base");
const copy_routines = @import("copy_routines.zig");
const SoftDescriptorSet = @import("../SoftDescriptorSet.zig");
const SoftDevice = @import("../SoftDevice.zig");
const SoftImage = @import("../SoftImage.zig");
const SoftPipeline = @import("../SoftPipeline.zig");
const ComputeRoutines = @import("ComputeRoutines.zig");
const PipelineState = @import("PipelineState.zig");
const cmd = base.commands;
const VkError = base.VkError;
@@ -15,108 +18,43 @@ const Self = @This();
compute_routine: ComputeRoutines,
/// .graphics = 0
/// .compute = 1
pipeline_states: [2]PipelineState,
pub fn init(device: *SoftDevice) Self {
return .{
.compute_routine = .init(device),
var self: Self = undefined;
for (self.pipeline_states[0..]) |*state| {
state.* = .{
.pipeline = null,
.sets = [_]?*SoftDescriptorSet{null} ** base.VULKAN_MAX_DESCRIPTOR_SETS,
};
}
self.compute_routine = .init(device, &self.pipeline_states[@intFromEnum(vk.PipelineBindPoint.compute)]);
return self;
}
pub fn deinit(self: *Self) void {
self.compute_routine.destroy();
}
pub fn dispatch(self: *Self, command: *const cmd.Command) VkError!void {
pub fn execute(self: *Self, command: *const cmd.Command) VkError!void {
switch (command.*) {
.BindPipeline => |data| {
if (data.bind_point == .compute) {
self.compute_routine.bindPipeline(@alignCast(@fieldParentPtr("interface", data.pipeline)));
} else {
// TODO
.BindDescriptorSets => |data| {
for (data.first_set.., data.sets[0..]) |i, set| {
if (set == null) break;
self.pipeline_states[@intCast(@intFromEnum(data.bind_point))].sets[i] = @alignCast(@fieldParentPtr("interface", set.?));
}
},
.ClearColorImage => |data| try clearColorImage(&data),
.CopyBuffer => |data| try copyBuffer(&data),
.CopyImage => |data| try copyImage(&data),
.CopyImageToBuffer => |data| try copyImageToBuffer(&data),
.FillBuffer => |data| try fillBuffer(&data),
.BindPipeline => |data| self.pipeline_states[@intCast(@intFromEnum(data.bind_point))].pipeline = @alignCast(@fieldParentPtr("interface", data.pipeline)),
.ClearColorImage => |data| try copy_routines.clearColorImage(&data),
.CopyBuffer => |data| try copy_routines.copyBuffer(&data),
.CopyImage => |data| try copy_routines.copyImage(&data),
.CopyImageToBuffer => |data| try copy_routines.copyImageToBuffer(&data),
.FillBuffer => |data| try copy_routines.fillBuffer(&data),
else => {},
}
}
fn clearColorImage(data: *const cmd.CommandClearColorImage) VkError!void {
const soft_image: *SoftImage = @alignCast(@fieldParentPtr("interface", data.image));
soft_image.clearRange(data.clear_color, data.range);
}
fn copyBuffer(data: *const cmd.CommandCopyBuffer) VkError!void {
for (data.regions) |region| {
const src_memory = if (data.src.memory) |memory| memory else return VkError.ValidationFailed;
const dst_memory = if (data.dst.memory) |memory| memory else return VkError.ValidationFailed;
const src_map: []u8 = @as([*]u8, @ptrCast(try src_memory.map(region.src_offset, region.size)))[0..region.size];
const dst_map: []u8 = @as([*]u8, @ptrCast(try dst_memory.map(region.dst_offset, region.size)))[0..region.size];
@memcpy(dst_map, src_map);
src_memory.unmap();
dst_memory.unmap();
}
}
fn copyImage(data: *const cmd.CommandCopyImage) VkError!void {
_ = data;
std.log.scoped(.commandExecutor).warn("FIXME: implement image to image copy", .{});
}
fn copyImageToBuffer(data: *const cmd.CommandCopyImageToBuffer) VkError!void {
for (data.regions) |region| {
const src_memory = if (data.src.memory) |memory| memory else return VkError.ValidationFailed;
const dst_memory = if (data.dst.memory) |memory| memory else return VkError.ValidationFailed;
const pixel_size: u32 = @intCast(data.src.getPixelSize());
const image_row_pitch: u32 = data.src.extent.width * pixel_size;
const image_size: u32 = @intCast(data.src.getTotalSize());
const buffer_row_length: u32 = if (region.buffer_row_length != 0) region.buffer_row_length else region.image_extent.width;
const buffer_row_pitch: u32 = buffer_row_length * pixel_size;
const buffer_size: u32 = buffer_row_pitch * region.image_extent.height * region.image_extent.depth;
const src_map: []u8 = @as([*]u8, @ptrCast(try src_memory.map(0, image_size)))[0..image_size];
const dst_map: []u8 = @as([*]u8, @ptrCast(try dst_memory.map(region.buffer_offset, buffer_size)))[0..buffer_size];
const row_size = region.image_extent.width * pixel_size;
for (0..data.src.extent.depth) |z| {
for (0..data.src.extent.height) |y| {
const z_as_u32: u32 = @intCast(z);
const y_as_u32: u32 = @intCast(y);
const src_offset = ((@as(u32, @intCast(region.image_offset.z)) + z_as_u32) * data.src.extent.height + @as(u32, @intCast(region.image_offset.y)) + y_as_u32) * image_row_pitch + @as(u32, @intCast(region.image_offset.x)) * pixel_size;
const dst_offset = (z_as_u32 * buffer_row_length * region.image_extent.height + y_as_u32 * buffer_row_length) * pixel_size;
const src_slice = src_map[src_offset..(src_offset + row_size)];
const dst_slice = dst_map[dst_offset..(dst_offset + row_size)];
@memcpy(dst_slice, src_slice);
}
}
src_memory.unmap();
dst_memory.unmap();
}
}
fn fillBuffer(data: *const cmd.CommandFillBuffer) VkError!void {
const memory = if (data.buffer.memory) |memory| memory else return VkError.ValidationFailed;
var memory_map: []u32 = @as([*]u32, @ptrCast(@alignCast(try memory.map(data.offset, data.size))))[0..data.size];
var bytes = if (data.size == vk.WHOLE_SIZE) memory.size - data.offset else data.size;
var i: usize = 0;
while (bytes >= 4) : ({
bytes -= 4;
i += 1;
}) {
memory_map[i] = data.data;
}
memory.unmap();
}

9
src/soft/device/PipelineState.zig git.filemode.normal_file
View File

@@ -0,0 +1,9 @@
const std = @import("std");
const vk = @import("vulkan");
const base = @import("base");
const SoftDescriptorSet = @import("../SoftDescriptorSet.zig");
const SoftPipeline = @import("../SoftPipeline.zig");
pipeline: ?*SoftPipeline,
sets: [base.VULKAN_MAX_DESCRIPTOR_SETS]?*SoftDescriptorSet,

86
src/soft/device/copy_routines.zig git.filemode.normal_file
View File

@@ -0,0 +1,86 @@
const std = @import("std");
const vk = @import("vulkan");
const base = @import("base");
const cmd = base.commands;
const VkError = base.VkError;
const SoftImage = @import("../SoftImage.zig");
pub fn clearColorImage(data: *const cmd.CommandClearColorImage) VkError!void {
const soft_image: *SoftImage = @alignCast(@fieldParentPtr("interface", data.image));
soft_image.clearRange(data.clear_color, data.range);
}
pub fn copyBuffer(data: *const cmd.CommandCopyBuffer) VkError!void {
for (data.regions) |region| {
const src_memory = if (data.src.memory) |memory| memory else return VkError.ValidationFailed;
const dst_memory = if (data.dst.memory) |memory| memory else return VkError.ValidationFailed;
const src_map: []u8 = @as([*]u8, @ptrCast(try src_memory.map(region.src_offset, region.size)))[0..region.size];
const dst_map: []u8 = @as([*]u8, @ptrCast(try dst_memory.map(region.dst_offset, region.size)))[0..region.size];
@memcpy(dst_map, src_map);
src_memory.unmap();
dst_memory.unmap();
}
}
pub fn copyImage(data: *const cmd.CommandCopyImage) VkError!void {
_ = data;
std.log.scoped(.commandExecutor).warn("FIXME: implement image to image copy", .{});
}
pub fn copyImageToBuffer(data: *const cmd.CommandCopyImageToBuffer) VkError!void {
for (data.regions) |region| {
const src_memory = if (data.src.memory) |memory| memory else return VkError.ValidationFailed;
const dst_memory = if (data.dst.memory) |memory| memory else return VkError.ValidationFailed;
const pixel_size: u32 = @intCast(data.src.getPixelSize());
const image_row_pitch: u32 = data.src.extent.width * pixel_size;
const image_size: u32 = @intCast(data.src.getTotalSize());
const buffer_row_length: u32 = if (region.buffer_row_length != 0) region.buffer_row_length else region.image_extent.width;
const buffer_row_pitch: u32 = buffer_row_length * pixel_size;
const buffer_size: u32 = buffer_row_pitch * region.image_extent.height * region.image_extent.depth;
const src_map: []u8 = @as([*]u8, @ptrCast(try src_memory.map(0, image_size)))[0..image_size];
const dst_map: []u8 = @as([*]u8, @ptrCast(try dst_memory.map(region.buffer_offset, buffer_size)))[0..buffer_size];
const row_size = region.image_extent.width * pixel_size;
for (0..data.src.extent.depth) |z| {
for (0..data.src.extent.height) |y| {
const z_as_u32: u32 = @intCast(z);
const y_as_u32: u32 = @intCast(y);
const src_offset = ((@as(u32, @intCast(region.image_offset.z)) + z_as_u32) * data.src.extent.height + @as(u32, @intCast(region.image_offset.y)) + y_as_u32) * image_row_pitch + @as(u32, @intCast(region.image_offset.x)) * pixel_size;
const dst_offset = (z_as_u32 * buffer_row_length * region.image_extent.height + y_as_u32 * buffer_row_length) * pixel_size;
const src_slice = src_map[src_offset..(src_offset + row_size)];
const dst_slice = dst_map[dst_offset..(dst_offset + row_size)];
@memcpy(dst_slice, src_slice);
}
}
src_memory.unmap();
dst_memory.unmap();
}
}
pub fn fillBuffer(data: *const cmd.CommandFillBuffer) VkError!void {
const memory = if (data.buffer.memory) |memory| memory else return VkError.ValidationFailed;
var memory_map: []u32 = @as([*]u32, @ptrCast(@alignCast(try memory.map(data.offset, data.size))))[0..data.size];
var bytes = if (data.size == vk.WHOLE_SIZE) memory.size - data.offset else data.size;
var i: usize = 0;
while (bytes >= 4) : ({
bytes -= 4;
i += 1;
}) {
memory_map[i] = data.data;
}
memory.unmap();
}

View File

@@ -1,5 +1,6 @@
const std = @import("std");
const vk = @import("vulkan");
const lib = @import("lib.zig");
const cmd = @import("commands.zig");
@@ -14,6 +15,7 @@ const CommandPool = @import("CommandPool.zig");
const Event = @import("Event.zig");
const Image = @import("Image.zig");
const Pipeline = @import("Pipeline.zig");
const DescriptorSet = @import("DescriptorSet.zig");
const COMMAND_BUFFER_BASE_CAPACITY = 256;
@@ -40,6 +42,7 @@ vtable: *const VTable,
dispatch_table: *const DispatchTable,
pub const DispatchTable = struct {
bindDescriptorSets: *const fn (*Self, vk.PipelineBindPoint, u32, [lib.VULKAN_MAX_DESCRIPTOR_SETS]?*DescriptorSet, []const u32) VkError!void,
bindPipeline: *const fn (*Self, vk.PipelineBindPoint, *Pipeline) VkError!void,
begin: *const fn (*Self, *const vk.CommandBufferBeginInfo) VkError!void,
clearColorImage: *const fn (*Self, *Image, vk.ImageLayout, *const vk.ClearColorValue, vk.ImageSubresourceRange) VkError!void,
@@ -135,6 +138,23 @@ fn cleanCommandList(self: *Self) void {
// Commands ====================================================================================================
pub inline fn bindDescriptorSets(self: *Self, bind_point: vk.PipelineBindPoint, first_set: u32, sets: []const vk.DescriptorSet, dynamic_offsets: []const u32) VkError!void {
const allocator = self.host_allocator.allocator();
var inner_sets = [_]?*DescriptorSet{null} ** lib.VULKAN_MAX_DESCRIPTOR_SETS;
for (sets, inner_sets[0..sets.len]) |set, *inner_set| {
inner_set.* = try NonDispatchable(DescriptorSet).fromHandleObject(set);
}
try self.dispatch_table.bindDescriptorSets(self, bind_point, first_set, inner_sets, dynamic_offsets);
self.commands.append(allocator, .{ .BindDescriptorSets = .{
.bind_point = bind_point,
.first_set = first_set,
.sets = inner_sets,
.dynamic_offsets = dynamic_offsets,
} }) catch return VkError.OutOfHostMemory;
}
pub inline fn bindPipeline(self: *Self, bind_point: vk.PipelineBindPoint, pipeline: *Pipeline) VkError!void {
const allocator = self.host_allocator.allocator();
try self.dispatch_table.bindPipeline(self, bind_point, pipeline);

View File

@@ -1,24 +1,18 @@
const std = @import("std");
const vk = @import("vulkan");
const lib = @import("lib.zig");
const Buffer = @import("Buffer.zig");
const Image = @import("Image.zig");
const Pipeline = @import("Pipeline.zig");
const DescriptorSet = @import("DescriptorSet.zig");
pub const CommandType = enum {
BindPipeline,
BindVertexBuffer,
ClearColorImage,
CopyBuffer,
CopyImage,
CopyImageToBuffer,
Draw,
DrawIndexed,
DrawIndexedIndirect,
DrawIndirect,
FillBuffer,
pub const CommandBindDescriptorSets = struct {
bind_point: vk.PipelineBindPoint,
first_set: u32,
sets: [lib.VULKAN_MAX_DESCRIPTOR_SETS]?*DescriptorSet,
dynamic_offsets: []const u32,
};
pub const CommandBindPipeline = struct {
bind_point: vk.PipelineBindPoint,
pipeline: *Pipeline,
@@ -84,7 +78,8 @@ pub const CommandFillBuffer = struct {
data: u32,
};
pub const Command = union(CommandType) {
pub const Command = union(enum) {
BindDescriptorSets: CommandBindDescriptorSets,
BindPipeline: CommandBindPipeline,
BindVertexBuffer: CommandBindVertexBuffer,
ClearColorImage: CommandClearColorImage,

View File

@@ -56,7 +56,7 @@ pub const DRIVER_NAME = "Unnamed Stroll Driver";
pub const VULKAN_VERSION = vk.makeApiVersion(0, 1, 0, 0);
/// Maximum number of descriptor sets per pipeline
pub const VULKAN_MAX_DESCRIPTOR_SETS = 32;
pub const VULKAN_MAX_DESCRIPTOR_SETS = 4;
/// The number of push constant ranges is effectively bounded
/// by the number of possible shader stages. Not the number of stages that can

View File

@@ -1674,17 +1674,9 @@ pub export fn strollCmdBindDescriptorSets(
defer entryPointEndLogTrace();
const cmd = Dispatchable(CommandBuffer).fromHandleObject(p_cmd) catch |err| return errorLogger(err);
cmd.bindDescriptorSets(bind_point, first, sets[0..count], dynamic_offsets[0..dynamic_offset_count]) catch |err| return errorLogger(err);
notImplementedWarning();
_ = cmd;
_ = bind_point;
_ = layout;
_ = first;
_ = count;
_ = sets;
_ = dynamic_offsets;
_ = dynamic_offset_count;
}
pub export fn strollCmdBindIndexBuffer(p_cmd: vk.CommandBuffer, p_buffer: vk.Buffer, offset: vk.DeviceSize, index_type: vk.IndexType) callconv(vk.vulkan_call_conv) void {