almost finished update to Zig 0.16.0
Build / build (push) Successful in 2m18s
Test / build_and_test (push) Failing after 6m9s

This commit is contained in:
2026-04-18 02:26:29 +02:00
parent d5a520e261
commit e97ee8b23d
23 changed files with 355 additions and 466 deletions
+48 -31
View File
@@ -18,9 +18,14 @@ const ExecutionDevice = @import("device/Device.zig");
const Self = @This();
pub const Interface = base.CommandBuffer;
const Command = InterfaceFactory(.{
.execute = fn (*ExecutionDevice) VkError!void,
}, null);
const Command = struct {
const VTable = struct {
execute: *const fn (*anyopaque, *ExecutionDevice) VkError!void,
};
ptr: *anyopaque,
vtable: *const VTable,
};
interface: Interface,
@@ -79,10 +84,10 @@ pub fn execute(self: *Self, device: *ExecutionDevice) void {
defer self.interface.finish() catch {};
for (self.commands.items) |command| {
command.vtable.execute(command.ptr, device) catch |err| {
command.vtable.execute(@ptrCast(command.ptr), device) catch |err| {
base.errors.errorLoggerContext(err, "the software execution device");
if (@errorReturnTrace()) |trace| {
std.debug.dumpStackTrace(trace.*);
std.debug.dumpErrorReturnTrace(trace);
}
return; // Should we return or continue ? Maybe device lost ?
};
@@ -119,7 +124,8 @@ pub fn bindDescriptorSets(interface: *Interface, bind_point: vk.PipelineBindPoin
sets: [base.VULKAN_MAX_DESCRIPTOR_SETS]?*base.DescriptorSet,
dynamic_offsets: []const u32,
pub fn execute(impl: *const Impl, device: *ExecutionDevice) VkError!void {
pub fn execute(context: *anyopaque, device: *ExecutionDevice) VkError!void {
const impl: *Impl = @ptrCast(@alignCast(context));
for (impl.first_set.., impl.sets[0..]) |i, set| {
if (set == null)
break;
@@ -136,7 +142,7 @@ pub fn bindDescriptorSets(interface: *Interface, bind_point: vk.PipelineBindPoin
.sets = sets,
.dynamic_offsets = dynamic_offsets,
};
self.commands.append(allocator, Command.from(cmd)) catch return VkError.OutOfHostMemory;
self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory;
}
pub fn bindPipeline(interface: *Interface, bind_point: vk.PipelineBindPoint, pipeline: *base.Pipeline) VkError!void {
@@ -149,7 +155,8 @@ pub fn bindPipeline(interface: *Interface, bind_point: vk.PipelineBindPoint, pip
bind_point: vk.PipelineBindPoint,
pipeline: *SoftPipeline,
pub fn execute(impl: *const Impl, device: *ExecutionDevice) VkError!void {
pub fn execute(context: *anyopaque, device: *ExecutionDevice) VkError!void {
const impl: *Impl = @ptrCast(@alignCast(context));
device.pipeline_states[@intCast(@intFromEnum(impl.bind_point))].pipeline = impl.pipeline;
}
};
@@ -160,7 +167,7 @@ pub fn bindPipeline(interface: *Interface, bind_point: vk.PipelineBindPoint, pip
.bind_point = bind_point,
.pipeline = @alignCast(@fieldParentPtr("interface", pipeline)),
};
self.commands.append(allocator, Command.from(cmd)) catch return VkError.OutOfHostMemory;
self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory;
}
pub fn blitImage(interface: *Interface, src: *base.Image, _: vk.ImageLayout, dst: *base.Image, _: vk.ImageLayout, regions: []const vk.ImageBlit, filter: vk.Filter) VkError!void {
@@ -175,7 +182,8 @@ pub fn blitImage(interface: *Interface, src: *base.Image, _: vk.ImageLayout, dst
regions: []const vk.ImageBlit,
filter: vk.Filter,
pub fn execute(impl: *const Impl, device: *ExecutionDevice) VkError!void {
pub fn execute(context: *anyopaque, device: *ExecutionDevice) VkError!void {
const impl: *Impl = @ptrCast(@alignCast(context));
for (impl.regions[0..]) |region| {
try device.blitter.blitRegion(impl.src, impl.dst, region, impl.filter);
}
@@ -190,7 +198,7 @@ pub fn blitImage(interface: *Interface, src: *base.Image, _: vk.ImageLayout, dst
.regions = allocator.dupe(vk.ImageBlit, regions) catch return VkError.OutOfHostMemory, // Will be freed on cmdbuf reset or destroy
.filter = filter,
};
self.commands.append(allocator, Command.from(cmd)) catch return VkError.OutOfHostMemory;
self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory;
}
pub fn clearColorImage(interface: *Interface, image: *base.Image, _: vk.ImageLayout, color: *const vk.ClearColorValue, range: vk.ImageSubresourceRange) VkError!void {
@@ -204,7 +212,8 @@ pub fn clearColorImage(interface: *Interface, image: *base.Image, _: vk.ImageLay
clear_color: vk.ClearColorValue,
range: vk.ImageSubresourceRange,
pub fn execute(impl: *const Impl, device: *ExecutionDevice) VkError!void {
pub fn execute(context: *anyopaque, device: *ExecutionDevice) VkError!void {
const impl: *Impl = @ptrCast(@alignCast(context));
const clear_format = try impl.image.getClearFormat();
try device.blitter.clear(.{ .color = impl.clear_color }, clear_format, impl.image, impl.image.interface.format, impl.range, null);
}
@@ -217,7 +226,7 @@ pub fn clearColorImage(interface: *Interface, image: *base.Image, _: vk.ImageLay
.clear_color = color.*,
.range = range,
};
self.commands.append(allocator, Command.from(cmd)) catch return VkError.OutOfHostMemory;
self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory;
}
pub fn copyBuffer(interface: *Interface, src: *base.Buffer, dst: *base.Buffer, regions: []const vk.BufferCopy) VkError!void {
@@ -231,7 +240,8 @@ pub fn copyBuffer(interface: *Interface, src: *base.Buffer, dst: *base.Buffer, r
dst: *SoftBuffer,
regions: []const vk.BufferCopy,
pub fn execute(impl: *const Impl, _: *ExecutionDevice) VkError!void {
pub fn execute(context: *anyopaque, _: *ExecutionDevice) VkError!void {
const impl: *Impl = @ptrCast(@alignCast(context));
try impl.src.copyBuffer(impl.dst, impl.regions);
}
};
@@ -243,7 +253,7 @@ pub fn copyBuffer(interface: *Interface, src: *base.Buffer, dst: *base.Buffer, r
.dst = @alignCast(@fieldParentPtr("interface", dst)),
.regions = allocator.dupe(vk.BufferCopy, regions) catch return VkError.OutOfHostMemory, // Will be freed on cmdbuf reset or destroy
};
self.commands.append(allocator, Command.from(cmd)) catch return VkError.OutOfHostMemory;
self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory;
}
pub fn copyBufferToImage(interface: *Interface, src: *base.Buffer, dst: *base.Image, dst_layout: vk.ImageLayout, regions: []const vk.BufferImageCopy) VkError!void {
@@ -258,7 +268,8 @@ pub fn copyBufferToImage(interface: *Interface, src: *base.Buffer, dst: *base.Im
dst_layout: vk.ImageLayout,
regions: []const vk.BufferImageCopy,
pub fn execute(impl: *const Impl, _: *ExecutionDevice) VkError!void {
pub fn execute(context: *anyopaque, _: *ExecutionDevice) VkError!void {
const impl: *Impl = @ptrCast(@alignCast(context));
for (impl.regions[0..]) |region| {
try impl.dst.copyFromBuffer(impl.src, region);
}
@@ -273,7 +284,7 @@ pub fn copyBufferToImage(interface: *Interface, src: *base.Buffer, dst: *base.Im
.dst = @alignCast(@fieldParentPtr("interface", dst)),
.regions = allocator.dupe(vk.BufferImageCopy, regions) catch return VkError.OutOfHostMemory, // Will be freed on cmdbuf reset or destroy
};
self.commands.append(allocator, Command.from(cmd)) catch return VkError.OutOfHostMemory;
self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory;
}
pub fn copyImage(interface: *Interface, src: *base.Image, _: vk.ImageLayout, dst: *base.Image, _: vk.ImageLayout, regions: []const vk.ImageCopy) VkError!void {
@@ -287,7 +298,8 @@ pub fn copyImage(interface: *Interface, src: *base.Image, _: vk.ImageLayout, dst
dst: *SoftImage,
regions: []const vk.ImageCopy,
pub fn execute(impl: *const Impl, _: *ExecutionDevice) VkError!void {
pub fn execute(context: *anyopaque, _: *ExecutionDevice) VkError!void {
const impl: *Impl = @ptrCast(@alignCast(context));
for (impl.regions[0..]) |region| {
try impl.src.copyToImage(impl.dst, region);
}
@@ -301,7 +313,7 @@ pub fn copyImage(interface: *Interface, src: *base.Image, _: vk.ImageLayout, dst
.dst = @alignCast(@fieldParentPtr("interface", dst)),
.regions = allocator.dupe(vk.ImageCopy, regions) catch return VkError.OutOfHostMemory, // Will be freed on cmdbuf reset or destroy
};
self.commands.append(allocator, Command.from(cmd)) catch return VkError.OutOfHostMemory;
self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory;
}
pub fn copyImageToBuffer(interface: *Interface, src: *base.Image, src_layout: vk.ImageLayout, dst: *base.Buffer, regions: []const vk.BufferImageCopy) VkError!void {
@@ -316,7 +328,8 @@ pub fn copyImageToBuffer(interface: *Interface, src: *base.Image, src_layout: vk
dst: *SoftBuffer,
regions: []const vk.BufferImageCopy,
pub fn execute(impl: *const Impl, _: *ExecutionDevice) VkError!void {
pub fn execute(context: *anyopaque, _: *ExecutionDevice) VkError!void {
const impl: *Impl = @ptrCast(@alignCast(context));
for (impl.regions[0..]) |region| {
try impl.src.copyToBuffer(impl.dst, region);
}
@@ -331,7 +344,7 @@ pub fn copyImageToBuffer(interface: *Interface, src: *base.Image, src_layout: vk
.dst = @alignCast(@fieldParentPtr("interface", dst)),
.regions = allocator.dupe(vk.BufferImageCopy, regions) catch return VkError.OutOfHostMemory, // Will be freed on cmdbuf reset or destroy
};
self.commands.append(allocator, Command.from(cmd)) catch return VkError.OutOfHostMemory;
self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory;
}
pub fn dispatch(interface: *Interface, group_count_x: u32, group_count_y: u32, group_count_z: u32) VkError!void {
@@ -345,7 +358,8 @@ pub fn dispatch(interface: *Interface, group_count_x: u32, group_count_y: u32, g
group_count_y: u32,
group_count_z: u32,
pub fn execute(impl: *const Impl, device: *ExecutionDevice) VkError!void {
pub fn execute(context: *anyopaque, device: *ExecutionDevice) VkError!void {
const impl: *Impl = @ptrCast(@alignCast(context));
try device.compute_routines.dispatch(impl.group_count_x, impl.group_count_y, impl.group_count_z);
}
};
@@ -357,7 +371,7 @@ pub fn dispatch(interface: *Interface, group_count_x: u32, group_count_y: u32, g
.group_count_y = group_count_y,
.group_count_z = group_count_z,
};
self.commands.append(allocator, Command.from(cmd)) catch return VkError.OutOfHostMemory;
self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory;
}
pub fn dispatchIndirect(interface: *Interface, buffer: *base.Buffer, offset: vk.DeviceSize) VkError!void {
@@ -370,7 +384,8 @@ pub fn dispatchIndirect(interface: *Interface, buffer: *base.Buffer, offset: vk.
buffer: *SoftBuffer,
offset: vk.DeviceSize,
pub fn execute(impl: *const Impl, device: *ExecutionDevice) VkError!void {
pub fn execute(context: *anyopaque, device: *ExecutionDevice) VkError!void {
const impl: *Impl = @ptrCast(@alignCast(context));
const size = 3 * @sizeOf(u32);
const memory = if (impl.buffer.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv;
const map: []u32 = @as([*]u32, @ptrCast(@alignCast(try memory.map(impl.offset, size))))[0..3];
@@ -384,7 +399,7 @@ pub fn dispatchIndirect(interface: *Interface, buffer: *base.Buffer, offset: vk.
.buffer = @alignCast(@fieldParentPtr("interface", buffer)),
.offset = offset,
};
self.commands.append(allocator, Command.from(cmd)) catch return VkError.OutOfHostMemory;
self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory;
}
pub fn executeCommands(interface: *Interface, commands: *Interface) VkError!void {
@@ -396,7 +411,8 @@ pub fn executeCommands(interface: *Interface, commands: *Interface) VkError!void
cmd: *Self,
pub fn execute(impl: *const Impl, device: *ExecutionDevice) VkError!void {
pub fn execute(context: *anyopaque, device: *ExecutionDevice) VkError!void {
const impl: *Impl = @ptrCast(@alignCast(context));
impl.cmd.execute(device);
}
};
@@ -406,7 +422,7 @@ pub fn executeCommands(interface: *Interface, commands: *Interface) VkError!void
cmd.* = .{
.cmd = @alignCast(@fieldParentPtr("interface", commands)),
};
self.commands.append(allocator, Command.from(cmd)) catch return VkError.OutOfHostMemory;
self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory;
}
pub fn fillBuffer(interface: *Interface, buffer: *base.Buffer, offset: vk.DeviceSize, size: vk.DeviceSize, data: u32) VkError!void {
@@ -421,7 +437,8 @@ pub fn fillBuffer(interface: *Interface, buffer: *base.Buffer, offset: vk.Device
size: vk.DeviceSize,
data: u32,
pub fn execute(impl: *const Impl, _: *ExecutionDevice) VkError!void {
pub fn execute(context: *anyopaque, _: *ExecutionDevice) VkError!void {
const impl: *Impl = @ptrCast(@alignCast(context));
try impl.buffer.fillBuffer(impl.offset, impl.size, impl.data);
}
};
@@ -434,7 +451,7 @@ pub fn fillBuffer(interface: *Interface, buffer: *base.Buffer, offset: vk.Device
.size = size,
.data = data,
};
self.commands.append(allocator, Command.from(cmd)) catch return VkError.OutOfHostMemory;
self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory;
}
pub fn pipelineBarrier(interface: *Interface, src_stage: vk.PipelineStageFlags, dst_stage: vk.PipelineStageFlags, dependency: vk.DependencyFlags, memory_barriers: []const vk.MemoryBarrier, buffer_barriers: []const vk.BufferMemoryBarrier, image_barriers: []const vk.ImageMemoryBarrier) VkError!void {
@@ -444,7 +461,7 @@ pub fn pipelineBarrier(interface: *Interface, src_stage: vk.PipelineStageFlags,
const CommandImpl = struct {
const Impl = @This();
pub fn execute(_: *const Impl, _: *ExecutionDevice) VkError!void {
pub fn execute(_: *anyopaque, _: *ExecutionDevice) VkError!void {
// TODO: implement synchronization for rasterization stages
}
};
@@ -452,7 +469,7 @@ pub fn pipelineBarrier(interface: *Interface, src_stage: vk.PipelineStageFlags,
const cmd = allocator.create(CommandImpl) catch return VkError.OutOfHostMemory;
errdefer allocator.destroy(cmd);
cmd.* = .{};
self.commands.append(allocator, Command.from(cmd)) catch return VkError.OutOfHostMemory;
self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory;
_ = src_stage;
_ = dst_stage;
+3 -10
View File
@@ -18,6 +18,7 @@ pub const SoftEvent = @import("SoftEvent.zig");
pub const SoftFence = @import("SoftFence.zig");
pub const SoftFramebuffer = @import("SoftFramebuffer.zig");
pub const SoftImage = @import("SoftImage.zig");
pub const SoftInstance = @import("SoftInstance.zig");
pub const SoftImageView = @import("SoftImageView.zig");
pub const SoftPipeline = @import("SoftPipeline.zig");
pub const SoftPipelineCache = @import("SoftPipelineCache.zig");
@@ -42,13 +43,12 @@ const DeviceAllocator = struct {
interface: Interface,
device_allocator: if (config.debug_allocator) std.heap.DebugAllocator(.{}) else DeviceAllocator,
workers: std.Thread.Pool,
pub fn create(physical_device: *base.PhysicalDevice, allocator: std.mem.Allocator, info: *const vk.DeviceCreateInfo) VkError!*Self {
pub fn create(instance: *base.Instance, physical_device: *base.PhysicalDevice, allocator: std.mem.Allocator, info: *const vk.DeviceCreateInfo) VkError!*Self {
const self = allocator.create(Self) catch return VkError.OutOfHostMemory;
errdefer allocator.destroy(self);
var interface = try Interface.init(allocator, physical_device, info);
var interface = try Interface.init(allocator, instance, physical_device, info);
interface.vtable = &.{
.createQueue = SoftQueue.create,
@@ -82,12 +82,6 @@ pub fn create(physical_device: *base.PhysicalDevice, allocator: std.mem.Allocato
self.* = .{
.interface = interface,
.device_allocator = if (config.debug_allocator) .init else .{},
.workers = undefined,
};
self.workers.init(.{ .allocator = self.device_allocator.allocator() }) catch |err| return switch (err) {
SpawnError.OutOfMemory, SpawnError.LockedMemoryLimitExceeded => VkError.OutOfDeviceMemory,
else => VkError.Unknown,
};
try self.interface.createQueues(allocator, info);
@@ -96,7 +90,6 @@ pub fn create(physical_device: *base.PhysicalDevice, allocator: std.mem.Allocato
pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
self.workers.deinit();
if (config.debug_allocator) {
// All device memory allocations should've been freed by now
+24 -17
View File
@@ -9,8 +9,8 @@ const Self = @This();
pub const Interface = base.Event;
interface: Interface,
mutex: std.Thread.Mutex,
condition: std.Thread.Condition,
mutex: std.Io.Mutex,
condition: std.Io.Condition,
is_signaled: bool,
pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const vk.EventCreateInfo) VkError!*Self {
@@ -29,8 +29,8 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v
self.* = .{
.interface = interface,
.mutex = std.Thread.Mutex{},
.condition = std.Thread.Condition{},
.mutex = .init,
.condition = .init,
.is_signaled = false,
};
return self;
@@ -43,9 +43,10 @@ pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void {
pub fn getStatus(interface: *Interface) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const io = interface.owner.io();
self.mutex.lock();
defer self.mutex.unlock();
self.mutex.lock(io) catch return VkError.DeviceLost;
defer self.mutex.unlock(io);
if (!self.is_signaled) {
return VkError.EventReset;
@@ -54,35 +55,41 @@ pub fn getStatus(interface: *Interface) VkError!void {
pub fn reset(interface: *Interface) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const io = interface.owner.io();
self.mutex.lock();
defer self.mutex.unlock();
self.mutex.lock(io) catch return VkError.DeviceLost;
defer self.mutex.unlock(io);
self.is_signaled = false;
}
pub fn signal(interface: *Interface) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const io = interface.owner.io();
self.mutex.lock();
defer self.mutex.unlock();
self.mutex.lock(io) catch return VkError.DeviceLost;
defer self.mutex.unlock(io);
self.is_signaled = true;
self.condition.broadcast();
self.condition.broadcast(io);
}
pub fn wait(interface: *Interface, timeout: u64) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const io = interface.owner.io();
self.mutex.lock();
defer self.mutex.unlock();
self.mutex.lock(io) catch return VkError.DeviceLost;
defer self.mutex.unlock(io);
if (self.is_signaled) return;
if (timeout == 0) return VkError.Timeout;
if (timeout == std.math.maxInt(@TypeOf(timeout))) {
self.condition.wait(&self.mutex);
} else {
self.condition.timedWait(&self.mutex, timeout) catch return VkError.Timeout;
if (timeout != std.math.maxInt(@TypeOf(timeout))) {
const duration: std.Io.Clock.Duration = .{
.raw = .fromNanoseconds(@intCast(timeout)),
.clock = .cpu_process,
};
duration.sleep(io) catch return VkError.DeviceLost;
}
self.condition.wait(io, &self.mutex) catch return VkError.DeviceLost;
}
+18 -13
View File
@@ -9,8 +9,8 @@ const Self = @This();
pub const Interface = base.Fence;
interface: Interface,
mutex: std.Thread.Mutex,
condition: std.Thread.Condition,
mutex: std.Io.Mutex,
condition: std.Io.Condition,
is_signaled: bool,
pub fn create(device: *Device, allocator: std.mem.Allocator, info: *const vk.FenceCreateInfo) VkError!*Self {
@@ -29,8 +29,8 @@ pub fn create(device: *Device, allocator: std.mem.Allocator, info: *const vk.Fen
self.* = .{
.interface = interface,
.mutex = std.Thread.Mutex{},
.condition = std.Thread.Condition{},
.mutex = .init,
.condition = .init,
.is_signaled = info.flags.signaled_bit,
};
return self;
@@ -55,26 +55,31 @@ pub fn reset(interface: *Interface) VkError!void {
pub fn signal(interface: *Interface) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const io = interface.owner.io();
self.mutex.lock();
defer self.mutex.unlock();
self.mutex.lock(io) catch return VkError.DeviceLost;
defer self.mutex.unlock(io);
self.is_signaled = true;
self.condition.broadcast();
self.condition.broadcast(io);
}
pub fn wait(interface: *Interface, timeout: u64) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const io = interface.owner.io();
self.mutex.lock();
defer self.mutex.unlock();
self.mutex.lock(io) catch return VkError.DeviceLost;
defer self.mutex.unlock(io);
if (self.is_signaled) return;
if (timeout == 0) return VkError.Timeout;
if (timeout == std.math.maxInt(@TypeOf(timeout))) {
self.condition.wait(&self.mutex);
} else {
self.condition.timedWait(&self.mutex, timeout) catch return VkError.Timeout;
if (timeout != std.math.maxInt(@TypeOf(timeout))) {
const duration: std.Io.Clock.Duration = .{
.raw = .fromNanoseconds(@intCast(timeout)),
.clock = .cpu_process,
};
duration.sleep(io) catch return VkError.DeviceLost;
}
self.condition.wait(io, &self.mutex) catch return VkError.DeviceLost;
}
+12
View File
@@ -11,6 +11,8 @@ const Self = @This();
pub const Interface = base.Instance;
interface: Interface,
threaded: std.Io.Threaded,
allocator: std.mem.Allocator,
fn castExtension(comptime ext: vk.ApiInfo) vk.ExtensionProperties {
var props: vk.ExtensionProperties = .{
@@ -29,6 +31,9 @@ pub fn create(allocator: std.mem.Allocator, infos: *const vk.InstanceCreateInfo)
const self = allocator.create(Self) catch return VkError.OutOfHostMemory;
errdefer allocator.destroy(self);
self.allocator = std.heap.smp_allocator;
self.threaded = std.Io.Threaded.init(self.allocator, .{});
self.interface = try base.Instance.init(allocator, infos);
self.interface.dispatch_table = &.{
.destroy = destroy,
@@ -36,12 +41,14 @@ pub fn create(allocator: std.mem.Allocator, infos: *const vk.InstanceCreateInfo)
self.interface.vtable = &.{
.requestPhysicalDevices = requestPhysicalDevices,
.releasePhysicalDevices = releasePhysicalDevices,
.io = io,
};
return &self.interface;
}
fn destroy(interface: *Interface, allocator: std.mem.Allocator) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
self.threaded.deinit();
allocator.destroy(self);
}
@@ -60,3 +67,8 @@ fn releasePhysicalDevices(interface: *Interface, allocator: std.mem.Allocator) V
interface.physical_devices.deinit(allocator);
interface.physical_devices = .empty;
}
fn io(interface: *Interface) std.Io {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
return self.threaded.io();
}
+2 -2
View File
@@ -17,7 +17,7 @@ var device_name = [_]u8{0} ** vk.MAX_PHYSICAL_DEVICE_NAME_SIZE;
interface: Interface,
pub fn create(allocator: std.mem.Allocator, instance: *const base.Instance) VkError!*Self {
pub fn create(allocator: std.mem.Allocator, instance: *base.Instance) VkError!*Self {
const command_allocator = VulkanAllocator.from(allocator).cloneWithScope(.command).allocator();
const self = allocator.create(Self) catch return VkError.OutOfHostMemory;
@@ -224,7 +224,7 @@ pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) VkError!void
}
pub fn createDevice(interface: *Interface, allocator: std.mem.Allocator, infos: *const vk.DeviceCreateInfo) VkError!*base.Device {
const device = try SoftDevice.create(interface, allocator, infos);
const device = try SoftDevice.create(interface.instance, interface, allocator, infos);
return &device.interface;
}
+22 -3
View File
@@ -10,6 +10,7 @@ const NonDispatchable = base.NonDispatchable;
const ShaderModule = base.ShaderModule;
const SoftDevice = @import("SoftDevice.zig");
const SoftInstance = @import("SoftInstance.zig");
const SoftShaderModule = @import("SoftShaderModule.zig");
const Self = @This();
@@ -49,6 +50,16 @@ pub fn createCompute(device: *base.Device, allocator: std.mem.Allocator, cache:
const device_allocator = soft_device.device_allocator.allocator();
const instance: *SoftInstance = @alignCast(@fieldParentPtr("interface", device.instance));
const runtimes_count = switch (instance.threaded.async_limit) {
.nothing => 1,
.unlimited => std.Thread.getCpuCount() catch 1, // If we cannot get the CPU count, fallback on single runtime
else => |count| blk: {
const cpu_count: usize = std.Thread.getCpuCount() catch break :blk @intFromEnum(count);
break :blk if (@intFromEnum(count) >= cpu_count) cpu_count else @intFromEnum(count);
},
};
self.* = .{
.interface = interface,
.stages = std.EnumMap(Stages, Shader).init(.{
@@ -57,7 +68,7 @@ pub fn createCompute(device: *base.Device, allocator: std.mem.Allocator, cache:
soft_module.ref();
shader.module = soft_module;
const runtimes = device_allocator.alloc(spv.Runtime, soft_device.workers.getIdCount()) catch return VkError.OutOfHostMemory;
const runtimes = device_allocator.alloc(spv.Runtime, runtimes_count) catch return VkError.OutOfHostMemory;
errdefer {
for (runtimes) |*runtime| {
runtime.deinit(device_allocator);
@@ -103,9 +114,17 @@ pub fn createGraphics(device: *base.Device, allocator: std.mem.Allocator, cache:
.destroy = destroy,
};
const soft_device: *SoftDevice = @alignCast(@fieldParentPtr("interface", device));
const instance: *SoftInstance = @alignCast(@fieldParentPtr("interface", device.instance));
const runtimes_count = switch (instance.threaded.async_limit) {
.nothing => 1,
.unlimited => std.Thread.getCpuCount() catch 1, // If we cannot get the CPU count, fallback on single runtime
else => |count| blk: {
const cpu_count: usize = std.Thread.getCpuCount() catch break :blk @intFromEnum(count);
break :blk if (@intFromEnum(count) >= cpu_count) cpu_count else @intFromEnum(count);
},
};
const runtimes = allocator.alloc(spv.Runtime, soft_device.workers.getIdCount()) catch return VkError.OutOfHostMemory;
const runtimes = allocator.alloc(spv.Runtime, runtimes_count) catch return VkError.OutOfHostMemory;
errdefer allocator.free(runtimes);
//for (runtimes) |*runtime| {
+14 -9
View File
@@ -17,7 +17,7 @@ const Self = @This();
pub const Interface = base.Queue;
interface: Interface,
lock: std.Thread.RwLock,
lock: std.Io.RwLock,
pub fn create(allocator: std.mem.Allocator, device: *base.Device, index: u32, family_index: u32, flags: vk.DeviceQueueCreateFlags) VkError!*Interface {
const self = allocator.create(Self) catch return VkError.OutOfHostMemory;
@@ -33,7 +33,7 @@ pub fn create(allocator: std.mem.Allocator, device: *base.Device, index: u32, fa
self.* = .{
.interface = interface,
.lock = .{},
.lock = .init,
};
return &self.interface;
}
@@ -56,10 +56,11 @@ pub fn submit(interface: *Interface, infos: []Interface.SubmitInfo, p_fence: ?*b
const soft_device: *SoftDevice = @alignCast(@fieldParentPtr("interface", interface.owner));
const allocator = soft_device.device_allocator.allocator();
const io = soft_device.interface.io();
// Lock here to avoid acquiring it in `waitIdle` before runners start
self.lock.lockShared();
defer self.lock.unlockShared();
self.lock.lockShared(io) catch return VkError.DeviceLost;
defer self.lock.unlockShared(io);
for (infos) |info| {
// Cloning info to keep them alive until command execution ends
@@ -68,19 +69,23 @@ pub fn submit(interface: *Interface, infos: []Interface.SubmitInfo, p_fence: ?*b
};
const runners_counter = allocator.create(RefCounter) catch return VkError.OutOfDeviceMemory;
runners_counter.* = .init;
soft_device.workers.spawn(Self.taskRunner, .{ self, cloned_info, p_fence, runners_counter }) catch return VkError.Unknown;
_ = soft_device.interface.io().async(Self.taskRunner, .{ self, cloned_info, p_fence, runners_counter });
}
}
pub fn waitIdle(interface: *Interface) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
self.lock.lock();
defer self.lock.unlock();
const io = interface.owner.io();
self.lock.lock(io) catch return VkError.DeviceLost;
defer self.lock.unlock(io);
}
fn taskRunner(self: *Self, info: Interface.SubmitInfo, p_fence: ?*base.Fence, runners_counter: *RefCounter) void {
self.lock.lockShared();
defer self.lock.unlockShared();
const io = self.interface.owner.io();
self.lock.lockShared(io) catch return;
defer self.lock.unlockShared(io);
runners_counter.ref();
defer {
+3 -2
View File
@@ -32,13 +32,14 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v
self.* = .{
.interface = interface,
.module = spv.Module.init(allocator, code, .{
.use_simd_vectors_specializations = !std.process.hasEnvVarConstant(lib.NO_SHADER_SIMD_ENV_NAME),
//.use_simd_vectors_specializations = !std.process.hasEnvVarConstant(lib.NO_SHADER_SIMD_ENV_NAME),
.use_simd_vectors_specializations = true,
}) catch |err| switch (err) {
spv.Module.ModuleError.OutOfMemory => return VkError.OutOfHostMemory,
else => {
std.log.scoped(.@"SPIR-V module").err("module creation catched a '{s}'", .{@errorName(err)});
if (@errorReturnTrace()) |trace| {
std.debug.dumpStackTrace(trace.*);
std.debug.dumpErrorReturnTrace(trace);
}
return VkError.ValidationFailed;
},
+46 -41
View File
@@ -40,8 +40,10 @@ pub fn init(device: *SoftDevice, state: *PipelineState) Self {
.state = state,
.batch_size = 0,
.invocation_index = .init(0),
.early_dump = std.process.parseEnvVarInt(lib.DUMP_EARLY_RESULT_TABLE_ENV_NAME, u32, 10) catch null,
.final_dump = std.process.parseEnvVarInt(lib.DUMP_FINAL_RESULT_TABLE_ENV_NAME, u32, 10) catch null,
//.early_dump = std.process.parseEnvVarInt(lib.DUMP_EARLY_RESULT_TABLE_ENV_NAME, u32, 10) catch null,
//.final_dump = std.process.parseEnvVarInt(lib.DUMP_FINAL_RESULT_TABLE_ENV_NAME, u32, 10) catch null,
.early_dump = null,
.final_dump = null,
};
}
@@ -61,46 +63,46 @@ pub fn dispatch(self: *Self, group_count_x: u32, group_count_y: u32, group_count
self.invocation_index.store(0, .monotonic);
var wg: std.Thread.WaitGroup = .{};
var wg: std.Io.Group = .init;
for (0..@min(self.batch_size, group_count)) |batch_id| {
if (std.process.hasEnvVarConstant(lib.SINGLE_THREAD_COMPUTE_EXECUTION_ENV_NAME)) {
@branchHint(.cold); // Should only be reached for debugging
//if (std.process.hasEnvVarConstant(lib.SINGLE_THREAD_COMPUTE_EXECUTION_ENV_NAME)) {
// @branchHint(.cold); // Should only be reached for debugging
runWrapper(
RunData{
.self = self,
.batch_id = batch_id,
.group_count = group_count,
.group_count_x = @as(usize, @intCast(group_count_x)),
.group_count_y = @as(usize, @intCast(group_count_y)),
.group_count_z = @as(usize, @intCast(group_count_z)),
.invocations_per_workgroup = invocations_per_workgroup,
.pipeline = pipeline,
},
);
} else {
self.device.workers.spawnWg(&wg, runWrapper, .{
RunData{
.self = self,
.batch_id = batch_id,
.group_count = group_count,
.group_count_x = @as(usize, @intCast(group_count_x)),
.group_count_y = @as(usize, @intCast(group_count_y)),
.group_count_z = @as(usize, @intCast(group_count_z)),
.invocations_per_workgroup = invocations_per_workgroup,
.pipeline = pipeline,
},
});
}
// runWrapper(
// RunData{
// .self = self,
// .batch_id = batch_id,
// .group_count = group_count,
// .group_count_x = @as(usize, @intCast(group_count_x)),
// .group_count_y = @as(usize, @intCast(group_count_y)),
// .group_count_z = @as(usize, @intCast(group_count_z)),
// .invocations_per_workgroup = invocations_per_workgroup,
// .pipeline = pipeline,
// },
// );
//} else {
wg.async(self.device.interface.io(), runWrapper, .{
RunData{
.self = self,
.batch_id = batch_id,
.group_count = group_count,
.group_count_x = @as(usize, @intCast(group_count_x)),
.group_count_y = @as(usize, @intCast(group_count_y)),
.group_count_z = @as(usize, @intCast(group_count_z)),
.invocations_per_workgroup = invocations_per_workgroup,
.pipeline = pipeline,
},
});
//}
}
self.device.workers.waitAndWork(&wg);
wg.await(self.device.interface.io()) catch return VkError.DeviceLost;
}
fn runWrapper(data: RunData) void {
@call(.always_inline, run, .{data}) catch |err| {
std.log.scoped(.@"SPIR-V runtime").err("SPIR-V runtime catched a '{s}'", .{@errorName(err)});
if (@errorReturnTrace()) |trace| {
std.debug.dumpStackTrace(trace.*);
std.debug.dumpErrorReturnTrace(trace);
}
};
}
@@ -171,14 +173,17 @@ inline fn run(data: RunData) !void {
inline fn dumpResultsTable(allocator: std.mem.Allocator, rt: *spv.Runtime, is_early: bool) !void {
@branchHint(.cold);
const file = try std.fs.cwd().createFile(
std.fmt.comptimePrint("{s}_compute_result_table_dump.txt", .{if (is_early) "early" else "final"}),
.{ .truncate = true },
);
defer file.close();
var buffer = [_]u8{0} ** 1024;
var writer = file.writer(buffer[0..]);
try rt.dumpResultsTable(allocator, &writer.interface);
_ = allocator;
_ = rt;
_ = is_early;
//const file = try std.fs.cwd().createFile(
// std.fmt.comptimePrint("{s}_compute_result_table_dump.txt", .{if (is_early) "early" else "final"}),
// .{ .truncate = true },
//);
//defer file.close();
//var buffer = [_]u8{0} ** 1024;
//var writer = file.writer(buffer[0..]);
//try rt.dumpResultsTable(allocator, &writer.interface);
}
fn writeDescriptorSets(self: *Self, rt: *spv.Runtime) !void {