From e9454ddd9a41b40a1178d35249d09349da25136b Mon Sep 17 00:00:00 2001 From: Kbz-8 Date: Sun, 10 May 2026 12:28:53 +0200 Subject: [PATCH] adding bounded arena allocator to renderer --- .gitea/workflows/Test.yml | 2 +- src/soft/device/BoundedArenaAllocator.zig | 73 +++++++++++++++++++++++ src/soft/device/Renderer.zig | 7 ++- src/soft/device/blitter.zig | 8 +-- src/soft/device/rasterizer.zig | 2 - src/vulkan/SpinMutex.zig | 21 +++++++ src/vulkan/error_set.zig | 1 - src/vulkan/fallback_host_allocator.zig | 24 +------- src/vulkan/lib.zig | 1 + 9 files changed, 107 insertions(+), 32 deletions(-) create mode 100644 src/soft/device/BoundedArenaAllocator.zig create mode 100644 src/vulkan/SpinMutex.zig diff --git a/.gitea/workflows/Test.yml b/.gitea/workflows/Test.yml index 287299a..ffef072 100644 --- a/.gitea/workflows/Test.yml +++ b/.gitea/workflows/Test.yml @@ -58,7 +58,7 @@ jobs: which deqp-runner && deqp-runner --version || echo "deqp-runner not found" - name: Run Vulkan CTS - run: zig build cts-soft --release=fast -Dlogs="standard" -- -j4 + run: zig build cts-soft --release=fast -- -j3 continue-on-error: true - name: Verify tests diff --git a/src/soft/device/BoundedArenaAllocator.zig b/src/soft/device/BoundedArenaAllocator.zig new file mode 100644 index 0000000..4c74c67 --- /dev/null +++ b/src/soft/device/BoundedArenaAllocator.zig @@ -0,0 +1,73 @@ +const std = @import("std"); +const base = @import("base"); + +const Self = @This(); + +const Allocator = std.mem.Allocator; +const Alignment = std.mem.Alignment; + +mutex: base.SpinMutex, +arena: std.heap.ArenaAllocator, +bound: usize, + +pub fn init(child_allocator: Allocator, bound: usize) Self { + return .{ + .mutex = .{}, + .arena = .init(child_allocator), + .bound = bound, + }; +} + +pub fn deinit(self: *Self) void { + self.arena.deinit(); +} + +pub fn allocator(self: *const Self) Allocator { + return .{ + .ptr = @ptrCast(@constCast(self)), // Ugly const cast for convenience + .vtable = &.{ + .alloc = alloc, + .resize = resize, + .remap = remap, + .free = free, + }, + }; +} + +pub inline fn queryCapacity(self: *Self) usize { + return self.arena.queryCapacity(); +} + +fn alloc(context: *anyopaque, len: usize, alignment: Alignment, ret_addr: usize) ?[*]u8 { + const self: *Self = @ptrCast(@alignCast(context)); + self.mutex.lock(); + defer self.mutex.unlock(); + if (self.arena.queryCapacity() >= self.bound) + return null; + return self.arena.allocator().rawAlloc(len, alignment, ret_addr); +} + +fn resize(context: *anyopaque, ptr: []u8, alignment: Alignment, new_len: usize, ret_addr: usize) bool { + const self: *Self = @ptrCast(@alignCast(context)); + self.mutex.lock(); + defer self.mutex.unlock(); + if (self.arena.queryCapacity() >= self.bound) + return false; + return self.arena.allocator().rawResize(ptr, alignment, new_len, ret_addr); +} + +fn remap(context: *anyopaque, ptr: []u8, alignment: Alignment, new_len: usize, ret_addr: usize) ?[*]u8 { + const self: *Self = @ptrCast(@alignCast(context)); + self.mutex.lock(); + defer self.mutex.unlock(); + if (self.arena.queryCapacity() >= self.bound) + return null; + return self.arena.allocator().rawRemap(ptr, alignment, new_len, ret_addr); +} + +fn free(context: *anyopaque, ptr: []u8, alignment: Alignment, ret_addr: usize) void { + const self: *Self = @ptrCast(@alignCast(context)); + self.mutex.lock(); + defer self.mutex.unlock(); + return self.arena.allocator().rawFree(ptr, alignment, ret_addr); +} diff --git a/src/soft/device/Renderer.zig b/src/soft/device/Renderer.zig index 09c5771..8689ab7 100644 --- a/src/soft/device/Renderer.zig +++ b/src/soft/device/Renderer.zig @@ -8,6 +8,7 @@ const spv = @import("spv"); pub const F32x4 = zm.F32x4; const PipelineState = @import("Device.zig").PipelineState; +const BoundedArenaAllocator = @import("BoundedArenaAllocator.zig"); const SoftBuffer = @import("../SoftBuffer.zig"); const SoftDescriptorSet = @import("../SoftDescriptorSet.zig"); @@ -26,6 +27,8 @@ const VkError = base.VkError; const Self = @This(); +const @"1GiB" = 1_073_741_824; + pub const VertexBuffer = struct { buffer: *const SoftBuffer, offset: usize, @@ -100,7 +103,7 @@ pub fn init(device: *SoftDevice, state: *PipelineState) Self { pub fn draw(self: *Self, vertex_count: usize, instance_count: usize, first_vertex: usize, first_instance: usize) VkError!void { const io = self.device.interface.io(); - var arena: std.heap.ArenaAllocator = .init(self.device.device_allocator.allocator()); + var arena: BoundedArenaAllocator = .init(self.device.device_allocator.allocator(), @"1GiB"); defer arena.deinit(); const allocator = arena.allocator(); @@ -131,7 +134,7 @@ pub fn draw(self: *Self, vertex_count: usize, instance_count: usize, first_verte pub fn drawIndexed(self: *Self, index_count: usize, instance_count: usize, first_index: usize, first_instance: usize, vertex_offset: i32) VkError!void { const io = self.device.interface.io(); - var arena: std.heap.ArenaAllocator = .init(self.device.device_allocator.allocator()); + var arena: BoundedArenaAllocator = .init(self.device.device_allocator.allocator(), @"1GiB"); defer arena.deinit(); const allocator = arena.allocator(); diff --git a/src/soft/device/blitter.zig b/src/soft/device/blitter.zig index cbf80d1..2b3deb5 100644 --- a/src/soft/device/blitter.zig +++ b/src/soft/device/blitter.zig @@ -583,7 +583,7 @@ pub fn readFloat4(map: []const u8, src_format: vk.Format) F32x4 { .s8_uint => c[0] = @floatFromInt(map[0]), - else => {}, //base.unsupported("Blitter: read float from source format {any}", .{src_format}), + else => base.unsupported("Blitter: read float from source format {any}", .{src_format}), } return c; @@ -637,7 +637,7 @@ pub fn writeFloat4(color: F32x4, map: []u8, dst_format: vk.Format) void { .r32g32b32a32_sfloat => std.mem.bytesAsValue(F32x4, map).* = color, - else => {}, //base.unsupported("Blitter: write float to destination format {any}", .{dst_format}), + else => base.unsupported("Blitter: write float to destination format {any}", .{dst_format}), } } @@ -678,7 +678,7 @@ pub fn readInt4(map: []const u8, src_format: vk.Format) U32x4 { .r32g32b32a32_uint, => c = std.mem.bytesToValue(U32x4, map), - else => {}, //base.unsupported("Blitter: read int from source format {any}", .{src_format}), + else => base.unsupported("Blitter: read int from source format {any}", .{src_format}), } return c; @@ -720,6 +720,6 @@ pub fn writeInt4(color: U32x4, map: []u8, dst_format: vk.Format) void { .r32g32b32a32_uint, => std.mem.bytesAsValue(U32x4, map).* = color, - else => {}, //base.unsupported("Blitter: write int to destination format {any}", .{dst_format}), + else => base.unsupported("Blitter: write int to destination format {any}", .{dst_format}), } } diff --git a/src/soft/device/rasterizer.zig b/src/soft/device/rasterizer.zig index 5f41ac2..3f0fbb0 100644 --- a/src/soft/device/rasterizer.zig +++ b/src/soft/device/rasterizer.zig @@ -167,8 +167,6 @@ pub fn drawTriangleFilled(allocator: std.mem.Allocator, fragments: *std.ArrayLis .color = zm.f32x4(1.0, 1.0, 1.0, 1.0), .inputs = try interpolateVertexOutputs(allocator, v0, v1, v2, b0, b1, b2), }) catch return VkError.OutOfDeviceMemory; - if (fragments.items.len > 64_000) - return VkError.MemoryFootprintTooBigDrv; } } } diff --git a/src/vulkan/SpinMutex.zig b/src/vulkan/SpinMutex.zig new file mode 100644 index 0000000..cfe64c1 --- /dev/null +++ b/src/vulkan/SpinMutex.zig @@ -0,0 +1,21 @@ +//! Atomic based spin mutex +const std = @import("std"); + +mutex: std.atomic.Mutex = .unlocked, + +pub fn lock(self: *@This()) void { + if (self.mutex.tryLock()) { + @branchHint(.likely); + return; + } + + while (true) { + if (self.mutex.tryLock()) { + return; + } + } +} + +pub fn unlock(self: *@This()) void { + self.mutex.unlock(); +} diff --git a/src/vulkan/error_set.zig b/src/vulkan/error_set.zig index afeb4e0..5b2beb6 100644 --- a/src/vulkan/error_set.zig +++ b/src/vulkan/error_set.zig @@ -55,7 +55,6 @@ pub const VkError = error{ InvalidHandleDrv, InvalidPipelineDrv, InvalidDeviceMemoryDrv, - MemoryFootprintTooBigDrv, }; pub inline fn errorLogger(err: VkError) void { diff --git a/src/vulkan/fallback_host_allocator.zig b/src/vulkan/fallback_host_allocator.zig index deb7df5..9e0f488 100644 --- a/src/vulkan/fallback_host_allocator.zig +++ b/src/vulkan/fallback_host_allocator.zig @@ -4,29 +4,9 @@ const builtin = @import("builtin"); const Allocator = std.mem.Allocator; const Alignment = std.mem.Alignment; -/// Atomic based spin mutex -const AtomicMutex = struct { - mutex: std.atomic.Mutex = .unlocked, +const SpinMutex = @import("SpinMutex.zig"); - fn lock(self: *@This()) void { - if (self.mutex.tryLock()) { - @branchHint(.likely); - return; - } - - while (true) { - if (self.mutex.tryLock()) { - return; - } - } - } - - fn unlock(self: *@This()) void { - self.mutex.unlock(); - } -}; - -var mutex: AtomicMutex = .{}; +var mutex: SpinMutex = .{}; var child_allocator: std.mem.Allocator = if (builtin.link_libc) std.heap.c_allocator else std.heap.smp_allocator; pub const fallback_host_allocator: Allocator = .{ diff --git a/src/vulkan/lib.zig b/src/vulkan/lib.zig index f03dc84..2159b0f 100644 --- a/src/vulkan/lib.zig +++ b/src/vulkan/lib.zig @@ -19,6 +19,7 @@ pub const NonDispatchable = @import("NonDispatchable.zig").NonDispatchable; pub const VkError = errors.VkError; pub const VulkanAllocator = @import("VulkanAllocator.zig"); pub const RefCounter = @import("RefCounter.zig"); +pub const SpinMutex = @import("SpinMutex.zig"); pub const CommandBuffer = @import("CommandBuffer.zig"); pub const Device = @import("Device.zig");