From 5b182580a72ec9ea086d7dc9f5c4bd7ebc6b0099 Mon Sep 17 00:00:00 2001 From: Kbz-8 Date: Wed, 1 Jul 2026 13:56:47 +0200 Subject: [PATCH] fixing wsi crashes --- build.zig | 3 +- build.zig.zon | 4 +- src/software/device/BoundedAllocator.zig | 67 ++++++++++++++++++----- src/software/device/Device.zig | 5 +- src/software/device/fragment.zig | 9 +++ src/software/device/rasterizer/common.zig | 27 ++------- src/vulkan/PhysicalDevice.zig | 16 ++++-- src/vulkan/Queue.zig | 4 ++ src/vulkan/lib_vulkan.zig | 8 ++- src/vulkan/wsi/SurfaceKHR.zig | 1 + src/vulkan/wsi/SwapchainKHR.zig | 21 +++++++ 11 files changed, 118 insertions(+), 47 deletions(-) diff --git a/build.zig b/build.zig index e011eb9..95623fe 100644 --- a/build.zig +++ b/build.zig @@ -432,7 +432,8 @@ fn addMultithreadedCTS(b: *std.Build, target: std.Build.ResolvedTarget, impl: *c } run.addArg("run"); - run.addArg("--verbose"); + run.addArg("--timeout"); + run.addArg("300"); run.addArg("--deqp"); run.addArg(cts_exe_path); run.addArg("--caselist"); diff --git a/build.zig.zon b/build.zig.zon index 7725ec1..2c33720 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -32,8 +32,8 @@ // Soft dependencies .SPIRV_Interpreter = .{ - .url = "git+https://github.com/Kbz-8/SPIRV-Interpreter#18f910f38a6a9c8a00294947ac545bbffdc55477", - .hash = "SPIRV_Interpreter-0.0.1-ajmpn9wmCQAuq_IL_yvnqUJgHnWyGAtYomv0x8HjTaRh", + .url = "git+https://github.com/Kbz-8/SPIRV-Interpreter#9b6f409bffdbb9105d32d73fa3c3025134b0e7f0", + .hash = "SPIRV_Interpreter-0.0.1-ajmpn_U2CQArCKl6BQ5HxvEE9bslrF21gzZVufvTHkJe", .lazy = true, }, //.SPIRV_Interpreter = .{ diff --git a/src/software/device/BoundedAllocator.zig b/src/software/device/BoundedAllocator.zig index a72408f..09fdac6 100644 --- a/src/software/device/BoundedAllocator.zig +++ b/src/software/device/BoundedAllocator.zig @@ -42,36 +42,75 @@ pub inline fn queryPeakFootprint(self: *Self) usize { return self.peak_concurrent_bytes_allocated.load(.monotonic); } +fn reserve(self: *Self, len: usize) bool { + while (true) { + const current = self.current_bytes_allocated.load(.monotonic); + if (len > self.bound -| current) + return false; + const new_current = current + len; + if (self.current_bytes_allocated.cmpxchgWeak(current, new_current, .monotonic, .monotonic) == null) { + _ = self.total_bytes_allocated.fetchAdd(len, .monotonic); + while (true) { + const peak = self.peak_concurrent_bytes_allocated.load(.monotonic); + if (new_current <= peak) + break; + if (self.peak_concurrent_bytes_allocated.cmpxchgWeak(peak, new_current, .monotonic, .monotonic) == null) + break; + } + return true; + } + } +} + +fn release(self: *Self, len: usize) void { + while (true) { + const current = self.current_bytes_allocated.load(.monotonic); + const new_current = current -| len; + if (self.current_bytes_allocated.cmpxchgWeak(current, new_current, .monotonic, .monotonic) == null) + return; + } +} + fn alloc(context: *anyopaque, len: usize, alignment: Alignment, ret_addr: usize) ?[*]u8 { const self: *Self = @ptrCast(@alignCast(context)); - if (self.current_bytes_allocated.fetchAdd(len, .monotonic) >= self.bound) + if (!self.reserve(len)) return null; - _ = self.total_bytes_allocated.fetchAdd(len, .monotonic); - if (self.current_bytes_allocated.load(.monotonic) > self.peak_concurrent_bytes_allocated.load(.monotonic)) - self.peak_concurrent_bytes_allocated.store(self.current_bytes_allocated.load(.monotonic), .monotonic); - return self.child_allocator.rawAlloc(len, alignment, ret_addr); + return self.child_allocator.rawAlloc(len, alignment, ret_addr) orelse { + self.release(len); + return null; + }; } fn resize(context: *anyopaque, ptr: []u8, alignment: Alignment, new_len: usize, ret_addr: usize) bool { const self: *Self = @ptrCast(@alignCast(context)); - _ = self.current_bytes_allocated.fetchSub(ptr.len, .monotonic); - if (self.current_bytes_allocated.fetchAdd(new_len, .monotonic) >= self.bound) + if (new_len > ptr.len) { + const delta = new_len - ptr.len; + if (!self.reserve(delta)) + return false; + if (self.child_allocator.rawResize(ptr, alignment, new_len, ret_addr)) + return true; + self.release(delta); return false; - _ = self.total_bytes_allocated.fetchAdd(new_len, .monotonic); + } return self.child_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.current_bytes_allocated.fetchSub(ptr.len, .monotonic); - if (self.current_bytes_allocated.fetchAdd(new_len, .monotonic) >= self.bound) - return null; - _ = self.total_bytes_allocated.fetchAdd(new_len, .monotonic); - return self.child_allocator.rawRemap(ptr, alignment, new_len, ret_addr); + if (new_len > ptr.len) { + const delta = new_len - ptr.len; + if (!self.reserve(delta)) + return null; + return self.child_allocator.rawRemap(ptr, alignment, new_len, ret_addr) orelse { + self.release(delta); + return null; + }; + } + return self.child_allocator.rawRemap(ptr, alignment, new_len, ret_addr) orelse null; } fn free(context: *anyopaque, ptr: []u8, alignment: Alignment, ret_addr: usize) void { const self: *Self = @ptrCast(@alignCast(context)); - _ = self.current_bytes_allocated.fetchSub(ptr.len, .monotonic); + self.release(ptr.len); return self.child_allocator.rawFree(ptr, alignment, ret_addr); } diff --git a/src/software/device/Device.zig b/src/software/device/Device.zig index b498e3d..cb9a3f5 100644 --- a/src/software/device/Device.zig +++ b/src/software/device/Device.zig @@ -118,11 +118,10 @@ fn writeDescriptorSet(rt: *spv.Runtime, payload: DescriptorPayload, set: u32, bi for (rt.mod.bindings.items) |entry| { if (entry.set != set or entry.binding != binding) continue; - - const variable = switch (rt.results[entry.result].variant orelse continue) { + const variable = if (rt.results[entry.result].variant) |*variant| switch (variant.*) { .Variable => |*variable| variable, else => continue, - }; + } else continue; writeDescriptorValue(&variable.value, payload, descriptor_index) catch |err| switch (err) { spv.Runtime.RuntimeError.InvalidValueType, diff --git a/src/software/device/fragment.zig b/src/software/device/fragment.zig index c01ae98..0771d5f 100644 --- a/src/software/device/fragment.zig +++ b/src/software/device/fragment.zig @@ -82,6 +82,11 @@ pub fn shaderInvocation( SpvRuntimeError.NotFound => {}, else => return err, }; + const helper_invocation = false; + rt.writeBuiltIn(allocator, std.mem.asBytes(&helper_invocation), .HelperInvocation) catch |err| switch (err) { + SpvRuntimeError.NotFound => {}, + else => return err, + }; const SoftPipeline = @import("../SoftPipeline.zig"); const previous_fragment_coord = SoftPipeline.current_fragment_coord; @@ -157,6 +162,10 @@ pub fn shaderInvocation( }, else => return err, }; + if (rt.helper_invocation) { + try rt.flushDescriptorSets(allocator); + return SpvRuntimeError.Killed; + } var outputs = std.mem.zeroes([spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(zm.F32x4)]u8); diff --git a/src/software/device/rasterizer/common.zig b/src/software/device/rasterizer/common.zig index a66c50f..40e1f89 100644 --- a/src/software/device/rasterizer/common.zig +++ b/src/software/device/rasterizer/common.zig @@ -216,20 +216,13 @@ pub fn depthTestSampleAndUpdate( z: f32, state: ?vk.PipelineDepthStencilStateCreateInfo, ) VkError!bool { + const depth_state = state orelse return true; const depth_offset = targetSampleOffset(depth.*, x, y, sample_index) orelse return false; depth.mutex.lock(io) catch return VkError.DeviceLost; defer depth.mutex.unlock(io); - if (state) |depth_state| { - return depthTestAndUpdateAtOffset(depth, depth_offset, z, depth_state); - } - - const depth_value = blitter.readFloat4(depth.base[depth_offset..], depth.format); - if (z >= depth_value[0]) - return false; - blitter.writeFloat4(zm.f32x4s(z), depth.base[depth_offset..], depth.format); - return true; + return depthTestAndUpdateAtOffset(depth, depth_offset, z, depth_state); } fn depthTestAndUpdateAtOffset(depth: *RenderTargetAccess, offset: usize, z: f32, state: vk.PipelineDepthStencilStateCreateInfo) bool { @@ -589,24 +582,16 @@ pub fn writeToTargets( // After work depth test to avoid overwritten depth pixels during fragment invocations. var depth_passed: ?bool = null; - if (!depth_already_applied and depth_attachment_access != null) { + if (!depth_already_applied and depth_attachment_access != null and depth_stencil_state != null) { const depth = depth_attachment_access.?; const depth_offset = targetSampleOffset(depth.*, x, y, sample_index) orelse continue; depth.mutex.lock(io) catch return VkError.DeviceLost; defer depth.mutex.unlock(io); - if (depth_stencil_state) |state| { - depth_passed = depthTestAndUpdateAtOffset(depth, depth_offset, z, state); - if (!depth_passed.? and stencil_state == null) - continue; - } else { - const depth_value = blitter.readFloat4(depth.base[depth_offset..], depth.format); - if (z >= depth_value[0]) - continue; - blitter.writeFloat4(zm.f32x4s(z), depth.base[depth_offset..], depth.format); - depth_passed = true; - } + depth_passed = depthTestAndUpdateAtOffset(depth, depth_offset, z, depth_stencil_state.?); + if (!depth_passed.? and stencil_state == null) + continue; } if (stencil_attachment_access) |stencil| { diff --git a/src/vulkan/PhysicalDevice.zig b/src/vulkan/PhysicalDevice.zig index 737cfae..f63ff6a 100644 --- a/src/vulkan/PhysicalDevice.zig +++ b/src/vulkan/PhysicalDevice.zig @@ -164,21 +164,29 @@ pub fn getSurfaceCapabilitiesKHR(_: *Self, surface: *SurfaceKHR, capabilities: * pub fn getSurfaceFormatsKHR(_: *Self, _: *SurfaceKHR, count: *u32, p_formats: ?[*]vk.SurfaceFormatKHR) VkError!void { const surface_formats = SurfaceKHR.getFormats(); - count.* = surface_formats.len; if (p_formats) |formats| { - for (formats[0..], surface_formats[0..]) |*format, surface_format| { + const write_count = @min(count.*, surface_formats.len); + for (formats[0..write_count], surface_formats[0..write_count]) |*format, surface_format| { format.* = surface_format; } + count.* = @intCast(write_count); + if (write_count < surface_formats.len) return VkError.Incomplete; + } else { + count.* = @intCast(surface_formats.len); } } pub fn getSurfacePresentModesKHR(_: *Self, _: *SurfaceKHR, count: *u32, p_modes: ?[*]vk.PresentModeKHR) VkError!void { const surface_modes = SurfaceKHR.getPresentModes(); - count.* = surface_modes.len; if (p_modes) |modes| { - for (modes[0..], surface_modes[0..]) |*mode, surface_mode| { + const write_count = @min(count.*, surface_modes.len); + for (modes[0..write_count], surface_modes[0..write_count]) |*mode, surface_mode| { mode.* = surface_mode; } + count.* = @intCast(write_count); + if (write_count < surface_modes.len) return VkError.Incomplete; + } else { + count.* = @intCast(surface_modes.len); } } diff --git a/src/vulkan/Queue.zig b/src/vulkan/Queue.zig index 11621f0..45b40c8 100644 --- a/src/vulkan/Queue.zig +++ b/src/vulkan/Queue.zig @@ -149,7 +149,11 @@ pub fn presentKHR(_: *Self, info: *const vk.PresentInfoKHR) VkError!void { } else cmd_err = err; + continue; }; + if (info.p_results) |results| { + results[i] = .success; + } } if (cmd_err) |err| diff --git a/src/vulkan/lib_vulkan.zig b/src/vulkan/lib_vulkan.zig index 7ba1e73..a2abf7d 100644 --- a/src/vulkan/lib_vulkan.zig +++ b/src/vulkan/lib_vulkan.zig @@ -2458,11 +2458,15 @@ pub export fn apeGetSwapchainImagesKHR(p_device: vk.Device, p_swapchain: vk.Swap Dispatchable(Device).checkHandleValidity(p_device) catch |err| return toVkResult(err); const swapchain = NonDispatchable(SwapchainKHR).fromHandleObject(p_swapchain) catch |err| return toVkResult(err); - count.* = @intCast(swapchain.images.len); if (p_images) |images| { - for (images[0..], swapchain.images[0..]) |*image, *swapchain_image| { + const write_count = @min(count.*, swapchain.images.len); + for (images[0..write_count], swapchain.images[0..write_count]) |*image, *swapchain_image| { image.* = swapchain_image.non_dispatchable_image.toVkHandle(vk.Image); } + count.* = @intCast(write_count); + if (write_count < swapchain.images.len) return .incomplete; + } else { + count.* = @intCast(swapchain.images.len); } return .success; diff --git a/src/vulkan/wsi/SurfaceKHR.zig b/src/vulkan/wsi/SurfaceKHR.zig index f29fb9a..4b83cc2 100644 --- a/src/vulkan/wsi/SurfaceKHR.zig +++ b/src/vulkan/wsi/SurfaceKHR.zig @@ -17,6 +17,7 @@ const formats = [_]vk.SurfaceFormatKHR{ }; const present_modes = [_]vk.PresentModeKHR{ + .fifo_khr, .immediate_khr, }; diff --git a/src/vulkan/wsi/SwapchainKHR.zig b/src/vulkan/wsi/SwapchainKHR.zig index 8f588e9..b66919e 100644 --- a/src/vulkan/wsi/SwapchainKHR.zig +++ b/src/vulkan/wsi/SwapchainKHR.zig @@ -18,7 +18,19 @@ owner: *Device, surface: ?*SurfaceKHR, images: []PresentImage, +fn validateImageAllocationSize(info: *const vk.SwapchainCreateInfoKHR) VkError!void { + if (info.image_extent.width == 0 or info.image_extent.height == 0 or info.image_array_layers == 0) + return VkError.OutOfDeviceMemory; + + const texel_size = lib.format.texelSize(info.image_format); + var size = std.math.mul(usize, texel_size, info.image_extent.width) catch return VkError.OutOfDeviceMemory; + size = std.math.mul(usize, size, info.image_extent.height) catch return VkError.OutOfDeviceMemory; + _ = std.math.mul(usize, size, info.image_array_layers) catch return VkError.OutOfDeviceMemory; +} + pub fn create(device: *Device, allocator: std.mem.Allocator, surface: *SurfaceKHR, info: *const vk.SwapchainCreateInfoKHR) VkError!*Self { + try validateImageAllocationSize(info); + const self = allocator.create(Self) catch return VkError.OutOfHostMemory; errdefer allocator.destroy(self); @@ -27,6 +39,14 @@ pub fn create(device: *Device, allocator: std.mem.Allocator, surface: *SurfaceKH allocator.free(images); } + var initialized_image_count: usize = 0; + errdefer { + for (images[0..initialized_image_count]) |*image| { + surface.detachImage(allocator, image) catch {}; + image.deinit(allocator); + } + } + for (images) |*image| { image.* = try .init(device, allocator, &.{ .format = info.image_format, @@ -46,6 +66,7 @@ pub fn create(device: *Device, allocator: std.mem.Allocator, surface: *SurfaceKH .queue_family_index_count = info.queue_family_index_count, .initial_layout = .general, }); + initialized_image_count += 1; try surface.attachImage(allocator, image); }