diff --git a/src/software/SoftBuffer.zig b/src/software/SoftBuffer.zig index 34b1cf4..bc3ab54 100644 --- a/src/software/SoftBuffer.zig +++ b/src/software/SoftBuffer.zig @@ -80,35 +80,35 @@ pub inline fn mapTo(self: *const Self, comptime T: type) VkError!T { return self.mapToWithAddedOffset(T, 0); } -pub inline fn mapAsSlice(self: *const Self, comptime T: type, size: usize) VkError![]T { +pub inline fn mapAsSlice(self: *const Self, comptime T: type, size: vk.DeviceSize) VkError![]T { return self.mapAsSliceWithAddedOffset(T, 0, size); } -pub inline fn mapAsWithAddedOffset(self: *const Self, comptime T: type, offset: usize) VkError!*T { +pub inline fn mapAsWithAddedOffset(self: *const Self, comptime T: type, offset: vk.DeviceSize) VkError!*T { return self.mapAsWithOffset(T, self.interface.offset + offset); } -pub inline fn mapToWithAddedOffset(self: *const Self, comptime T: type, offset: usize) VkError!T { +pub inline fn mapToWithAddedOffset(self: *const Self, comptime T: type, offset: vk.DeviceSize) VkError!T { return self.mapToWithOffset(T, self.interface.offset + offset); } -pub inline fn mapAsSliceWithAddedOffset(self: *const Self, comptime T: type, offset: usize, size: usize) VkError![]T { +pub inline fn mapAsSliceWithAddedOffset(self: *const Self, comptime T: type, offset: vk.DeviceSize, size: vk.DeviceSize) VkError![]T { return self.mapAsSliceWithOffset(T, self.interface.offset + offset, size); } -pub fn mapAsWithOffset(self: *const Self, comptime T: type, offset: usize) VkError!*T { +pub fn mapAsWithOffset(self: *const Self, comptime T: type, offset: vk.DeviceSize) VkError!*T { const memory = if (self.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv; const map = try memory.map(offset, @sizeOf(T)); return @alignCast(std.mem.bytesAsValue(T, map)); } -pub fn mapToWithOffset(self: *const Self, comptime T: type, offset: usize) VkError!T { +pub fn mapToWithOffset(self: *const Self, comptime T: type, offset: vk.DeviceSize) VkError!T { const memory = if (self.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv; const map = try memory.map(offset, @sizeOf(T)); return std.mem.bytesToValue(T, map); } -pub fn mapAsSliceWithOffset(self: *const Self, comptime T: type, offset: usize, size: usize) VkError![]T { +pub fn mapAsSliceWithOffset(self: *const Self, comptime T: type, offset: vk.DeviceSize, size: vk.DeviceSize) VkError![]T { const memory = if (self.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv; const map = try memory.map(offset, size); return @alignCast(std.mem.bytesAsSlice(T, map)); diff --git a/src/software/SoftCommandBuffer.zig b/src/software/SoftCommandBuffer.zig index ce979b7..dbf980e 100644 --- a/src/software/SoftCommandBuffer.zig +++ b/src/software/SoftCommandBuffer.zig @@ -128,6 +128,7 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v self.* = .{ .interface = interface, + // SAFETY: the command allocator is initialized after the object is constructed .command_allocator = undefined, .commands = .empty, }; diff --git a/src/software/SoftDeviceMemory.zig b/src/software/SoftDeviceMemory.zig index db000ce..217ddbf 100644 --- a/src/software/SoftDeviceMemory.zig +++ b/src/software/SoftDeviceMemory.zig @@ -25,9 +25,10 @@ pub fn create(device: *SoftDevice, allocator: std.mem.Allocator, size: vk.Device .invalidateRange = invalidateRange, }; + const allocation_size = std.math.cast(usize, size) orelse return VkError.OutOfDeviceMemory; self.* = .{ .interface = interface, - .data = device.interface.device_allocator.allocator().alloc(u8, size) catch return VkError.OutOfDeviceMemory, + .data = device.interface.device_allocator.allocator().alloc(u8, allocation_size) catch return VkError.OutOfDeviceMemory, }; return self; } @@ -54,10 +55,18 @@ pub fn invalidateRange(interface: *Interface, offset: vk.DeviceSize, size: vk.De pub fn map(interface: *Interface, offset: vk.DeviceSize, size: vk.DeviceSize) VkError![]u8 { const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); - if (offset >= self.data.len or (size != vk.WHOLE_SIZE and offset + size > self.data.len)) { + const map_offset = std.math.cast(usize, offset) orelse return VkError.MemoryMapFailed; + if (map_offset >= self.data.len) { return VkError.MemoryMapFailed; } - return if (size == vk.WHOLE_SIZE) self.data[offset..] else self.data[offset..(offset + size)]; + const map_size = if (size == vk.WHOLE_SIZE) + self.data.len - map_offset + else + std.math.cast(usize, size) orelse return VkError.MemoryMapFailed; + if (map_size > self.data.len - map_offset) { + return VkError.MemoryMapFailed; + } + return self.data[map_offset..][0..map_size]; } pub fn unmap(_: *Interface) void { diff --git a/src/software/SoftImage.zig b/src/software/SoftImage.zig index 881926b..b5e2b24 100644 --- a/src/software/SoftImage.zig +++ b/src/software/SoftImage.zig @@ -569,35 +569,35 @@ pub inline fn mapTo(self: *const Self, comptime T: type) VkError!T { return self.mapToWithAddedOffset(T, 0); } -pub inline fn mapAsSlice(self: *const Self, comptime T: type, size: usize) VkError![]T { +pub inline fn mapAsSlice(self: *const Self, comptime T: type, size: vk.DeviceSize) VkError![]T { return self.mapAsSliceWithAddedOffset(T, 0, size); } -pub inline fn mapAsWithAddedOffset(self: *const Self, comptime T: type, offset: usize) VkError!*T { +pub inline fn mapAsWithAddedOffset(self: *const Self, comptime T: type, offset: vk.DeviceSize) VkError!*T { return self.mapAsWithOffset(T, self.interface.memory_offset + offset); } -pub inline fn mapToWithAddedOffset(self: *const Self, comptime T: type, offset: usize) VkError!T { +pub inline fn mapToWithAddedOffset(self: *const Self, comptime T: type, offset: vk.DeviceSize) VkError!T { return self.mapToWithOffset(T, self.interface.memory_offset + offset); } -pub inline fn mapAsSliceWithAddedOffset(self: *const Self, comptime T: type, offset: usize, size: usize) VkError![]T { +pub inline fn mapAsSliceWithAddedOffset(self: *const Self, comptime T: type, offset: vk.DeviceSize, size: vk.DeviceSize) VkError![]T { return self.mapAsSliceWithOffset(T, self.interface.memory_offset + offset, size); } -pub fn mapAsWithOffset(self: *const Self, comptime T: type, offset: usize) VkError!*T { +pub fn mapAsWithOffset(self: *const Self, comptime T: type, offset: vk.DeviceSize) VkError!*T { const memory = if (self.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv; const map = try memory.map(offset, @sizeOf(T)); return @alignCast(std.mem.bytesAsValue(T, map)); } -pub fn mapToWithOffset(self: *const Self, comptime T: type, offset: usize) VkError!T { +pub fn mapToWithOffset(self: *const Self, comptime T: type, offset: vk.DeviceSize) VkError!T { const memory = if (self.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv; const map = try memory.map(offset, @sizeOf(T)); return std.mem.bytesToValue(T, map); } -pub fn mapAsSliceWithOffset(self: *const Self, comptime T: type, offset: usize, size: usize) VkError![]T { +pub fn mapAsSliceWithOffset(self: *const Self, comptime T: type, offset: vk.DeviceSize, size: vk.DeviceSize) VkError![]T { const memory = if (self.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv; const map = try memory.map(offset, size); return @alignCast(std.mem.bytesAsSlice(T, map)); diff --git a/src/software/device/Renderer.zig b/src/software/device/Renderer.zig index e2ecd5f..dcfd143 100644 --- a/src/software/device/Renderer.zig +++ b/src/software/device/Renderer.zig @@ -23,6 +23,7 @@ const F32x4 = zm.F32x4; const Self = @This(); const @"1GiB" = 1_073_741_824; +const draw_allocator_limit: usize = @intCast(@min(@as(u64, 4) * @"1GiB", @as(u64, std.math.maxInt(usize)))); pub const VertexBuffer = struct { buffer: *const SoftBuffer, @@ -196,12 +197,12 @@ pub fn resetInputAttachmentSnapshots(self: *Self) void { } pub fn draw(self: *Self, vertex_count: usize, instance_count: usize, first_vertex: usize, first_instance: usize) VkError!void { - var bounded_allocator: BoundedAllocator = .init(self.device.interface.device_allocator.allocator(), 4 * @"1GiB"); + var bounded_allocator: BoundedAllocator = .init(self.device.interface.device_allocator.allocator(), draw_allocator_limit); try self.drawCall(&bounded_allocator, vertex_count, instance_count, first_vertex, first_instance, null, null); } pub fn drawIndexed(self: *Self, index_count: usize, instance_count: usize, first_index: usize, first_instance: usize, vertex_offset: i32) VkError!void { - var bounded_allocator: BoundedAllocator = .init(self.device.interface.device_allocator.allocator(), 4 * @"1GiB"); + var bounded_allocator: BoundedAllocator = .init(self.device.interface.device_allocator.allocator(), draw_allocator_limit); const allocator = bounded_allocator.allocator(); const indexed_draw = try self.readIndexBuffer(allocator, index_count, first_index, vertex_offset); diff --git a/src/software/device/blitter.zig b/src/software/device/blitter.zig index 33b6362..c9c7a08 100644 --- a/src/software/device/blitter.zig +++ b/src/software/device/blitter.zig @@ -794,7 +794,12 @@ pub fn readFloat4(map: []const u8, src_format: vk.Format) F32x4 { c[3] = normalizedI16(std.mem.bytesToValue(u16, map[6..])); }, - .r16g16b16a16_sfloat => c = std.mem.bytesToValue(@Vector(4, f16), map), + .r16g16b16a16_sfloat => { + c[0] = std.mem.bytesToValue(f16, map[0..]); + c[1] = std.mem.bytesToValue(f16, map[2..]); + c[2] = std.mem.bytesToValue(f16, map[4..]); + c[3] = std.mem.bytesToValue(f16, map[6..]); + }, .r32g32b32a32_sfloat => c = std.mem.bytesToValue(F32x4, map), diff --git a/src/vulkan/NonDispatchable.zig b/src/vulkan/NonDispatchable.zig index b993840..14f9205 100644 --- a/src/vulkan/NonDispatchable.zig +++ b/src/vulkan/NonDispatchable.zig @@ -44,7 +44,8 @@ pub fn NonDispatchable(comptime T: type) type { if (handle == 0) { return VkError.InvalidHandleDrv; } - const non_dispatchable: *Self = @ptrFromInt(handle); + const address = std.math.cast(usize, handle) orelse return VkError.InvalidHandleDrv; + const non_dispatchable: *Self = @ptrFromInt(address); if (non_dispatchable.object_type != T.ObjectType) { return VkError.InvalidHandleDrv; } diff --git a/src/vulkan/QueryPool.zig b/src/vulkan/QueryPool.zig index 189384c..3dbdf5e 100644 --- a/src/vulkan/QueryPool.zig +++ b/src/vulkan/QueryPool.zig @@ -14,8 +14,51 @@ queries: []Query, vtable: *const VTable, +/// 32 bits platforms only supportss atomics up to 32 bits, +/// so serialize 64-bit query access with a 32-bit lock. +const LockedU64 = struct { + mutex: std.atomic.Value(u32) = std.atomic.Value(u32).init(0), + raw: u64, + + fn init(value: u64) LockedU64 { + return .{ .raw = value }; + } + + fn lock(self: *LockedU64) void { + while (self.mutex.cmpxchgWeak(0, 1, .acquire, .monotonic) != null) { + std.atomic.spinLoopHint(); + } + } + + fn unlock(self: *LockedU64) void { + self.mutex.store(0, .release); + } + + fn load(self: *LockedU64, comptime _: std.builtin.AtomicOrder) u64 { + self.lock(); + defer self.unlock(); + return self.raw; + } + + fn store(self: *LockedU64, value: u64, comptime _: std.builtin.AtomicOrder) void { + self.lock(); + defer self.unlock(); + self.raw = value; + } + + fn fetchAdd(self: *LockedU64, value: u64, comptime _: std.builtin.AtomicOrder) u64 { + self.lock(); + defer self.unlock(); + const previous = self.raw; + self.raw +%= value; + return previous; + } +}; + +const QueryValue = if (@bitSizeOf(usize) >= 64) std.atomic.Value(u64) else LockedU64; + const Query = struct { - value: std.atomic.Value(u64) = std.atomic.Value(u64).init(0), + value: QueryValue = QueryValue.init(0), available: bool = false, active: bool = false, }; diff --git a/src/vulkan/lib_vulkan.zig b/src/vulkan/lib_vulkan.zig index be96754..13386ed 100644 --- a/src/vulkan/lib_vulkan.zig +++ b/src/vulkan/lib_vulkan.zig @@ -1860,7 +1860,8 @@ pub export fn apeCmdBindIndexBuffer(p_cmd: vk.CommandBuffer, p_buffer: vk.Buffer const cmd = Dispatchable(CommandBuffer).fromHandleObject(p_cmd) catch |err| return errorLogger(err); const buffer = NonDispatchable(Buffer).fromHandleObject(p_buffer) catch |err| return errorLogger(err); - cmd.bindIndexBuffer(buffer, offset, index_type) catch |err| return errorLogger(err); + const host_offset = std.math.cast(usize, offset) orelse return errorLogger(VkError.ValidationFailed); + cmd.bindIndexBuffer(buffer, host_offset, index_type) catch |err| return errorLogger(err); } pub export fn apeCmdBindPipeline(p_cmd: vk.CommandBuffer, bind_point: vk.PipelineBindPoint, p_pipeline: vk.Pipeline) callconv(vk.vulkan_call_conv) void { @@ -1879,7 +1880,8 @@ pub export fn apeCmdBindVertexBuffers(p_cmd: vk.CommandBuffer, first: u32, count const cmd = Dispatchable(CommandBuffer).fromHandleObject(p_cmd) catch |err| return errorLogger(err); for (p_buffers, offsets, 0..count) |p_buffer, offset, i| { const buffer = NonDispatchable(Buffer).fromHandleObject(p_buffer) catch |err| return errorLogger(err); - cmd.bindVertexBuffer(first + i, buffer, offset) catch |err| return errorLogger(err); + const host_offset = std.math.cast(usize, offset) orelse return errorLogger(VkError.ValidationFailed); + cmd.bindVertexBuffer(first + i, buffer, host_offset) catch |err| return errorLogger(err); } } @@ -2035,7 +2037,8 @@ pub export fn apeCmdDrawIndexedIndirect(p_cmd: vk.CommandBuffer, p_buffer: vk.Bu const cmd = Dispatchable(CommandBuffer).fromHandleObject(p_cmd) catch |err| return errorLogger(err); const buffer = NonDispatchable(Buffer).fromHandleObject(p_buffer) catch |err| return errorLogger(err); - cmd.drawIndexedIndirect(buffer, offset, count, stride) catch |err| return errorLogger(err); + const host_offset = std.math.cast(usize, offset) orelse return errorLogger(VkError.ValidationFailed); + cmd.drawIndexedIndirect(buffer, host_offset, count, stride) catch |err| return errorLogger(err); } pub export fn apeCmdDrawIndirect(p_cmd: vk.CommandBuffer, p_buffer: vk.Buffer, offset: vk.DeviceSize, count: u32, stride: u32) callconv(vk.vulkan_call_conv) void { @@ -2044,7 +2047,8 @@ pub export fn apeCmdDrawIndirect(p_cmd: vk.CommandBuffer, p_buffer: vk.Buffer, o const cmd = Dispatchable(CommandBuffer).fromHandleObject(p_cmd) catch |err| return errorLogger(err); const buffer = NonDispatchable(Buffer).fromHandleObject(p_buffer) catch |err| return errorLogger(err); - cmd.drawIndirect(buffer, offset, count, stride) catch |err| return errorLogger(err); + const host_offset = std.math.cast(usize, offset) orelse return errorLogger(VkError.ValidationFailed); + cmd.drawIndirect(buffer, host_offset, count, stride) catch |err| return errorLogger(err); } pub export fn apeCmdEndQuery(p_cmd: vk.CommandBuffer, p_pool: vk.QueryPool, query: u32) callconv(vk.vulkan_call_conv) void { @@ -2265,8 +2269,9 @@ pub export fn apeCmdUpdateBuffer(p_cmd: vk.CommandBuffer, p_buffer: vk.Buffer, o const cmd = Dispatchable(CommandBuffer).fromHandleObject(p_cmd) catch |err| return errorLogger(err); const buffer = NonDispatchable(Buffer).fromHandleObject(p_buffer) catch |err| return errorLogger(err); + const data_size = std.math.cast(usize, size) orelse return errorLogger(VkError.ValidationFailed); const data_bytes: [*]const u8 = @ptrCast(data); - cmd.updateBuffer(buffer, offset, data_bytes[0..size]) catch |err| return errorLogger(err); + cmd.updateBuffer(buffer, offset, data_bytes[0..data_size]) catch |err| return errorLogger(err); } pub export fn apeCmdWaitEvents(