From 1e02a5bc3efb8ebebbb9261115fad5403020282e Mon Sep 17 00:00:00 2001 From: Kbz-8 Date: Sat, 9 May 2026 23:52:06 +0200 Subject: [PATCH] fixing triggered assertion in blitter --- .gitea/workflows/Test.yml | 2 +- build.zig | 46 ++++++++++++++++++-- src/soft/SoftBuffer.zig | 2 +- src/soft/SoftCommandBuffer.zig | 47 ++++++++++++++++++--- src/soft/SoftImage.zig | 77 ++++++++++++++++++++++++---------- src/soft/device/blitter.zig | 18 ++++---- src/vulkan/VulkanAllocator.zig | 5 +-- src/vulkan/format.zig | 6 +-- 8 files changed, 156 insertions(+), 47 deletions(-) diff --git a/.gitea/workflows/Test.yml b/.gitea/workflows/Test.yml index 83a4d22..c2865c5 100644 --- a/.gitea/workflows/Test.yml +++ b/.gitea/workflows/Test.yml @@ -61,7 +61,7 @@ jobs: which deqp-runner && deqp-runner --version || echo "deqp-runner not found" - name: Run Vulkan CTS - run: zig build cts-soft --release=fast -Ddebug-allocator=true -- -j4 + run: zig build cts-soft --release=fast -- -j4 continue-on-error: true - name: Verify tests diff --git a/build.zig b/build.zig index eaddb92..90b9b1d 100644 --- a/build.zig +++ b/build.zig @@ -136,7 +136,9 @@ pub fn build(b: *std.Build) !void { (try addCTS(b, target, &impl, lib, .gdb)).dependOn(&lib_install.step); (try addCTS(b, target, &impl, lib, .valgrind)).dependOn(&lib_install.step); - (try addMultithreadedCTS(b, target, &impl, lib)).dependOn(&lib_install.step); + (try addMultithreadedCTS(b, target, &impl, lib, .normal)).dependOn(&lib_install.step); + (try addMultithreadedCTS(b, target, &impl, lib, .gdb)).dependOn(&lib_install.step); + (try addMultithreadedCTS(b, target, &impl, lib, .valgrind)).dependOn(&lib_install.step); const impl_autodoc_test = b.addObject(.{ .name = "lib", @@ -290,7 +292,7 @@ fn addCTS(b: *std.Build, target: std.Build.ResolvedTarget, impl: *const Implemen return &run.step; } -fn addMultithreadedCTS(b: *std.Build, target: std.Build.ResolvedTarget, impl: *const ImplementationDesc, impl_lib: *Step.Compile) !*Step { +fn addMultithreadedCTS(b: *std.Build, target: std.Build.ResolvedTarget, impl: *const ImplementationDesc, impl_lib: *Step.Compile, comptime mode: RunningMode) !*Step { const cts = b.dependency("cts_bin", .{}); const cts_exe_name = cts.path(b.fmt("deqp-vk-{s}", .{ @@ -321,9 +323,28 @@ fn addMultithreadedCTS(b: *std.Build, target: std.Build.ResolvedTarget, impl: *c const cts_exe_path = try cts_exe_name.getPath3(b, null).toString(b.allocator); - const run = b.addSystemCommand(&[_][]const u8{"deqp-runner"}); + const run = b.addSystemCommand(&[_][]const u8{switch (mode) { + .normal => cts_exe_path, + .gdb => "gdb", + .valgrind => "valgrind", + }}); run.step.dependOn(&impl_lib.step); + switch (mode) { + .gdb => { + run.addArg("--args"); + run.addArg(cts_exe_path); + }, + .valgrind => { + run.addArg("-s"); + run.addArg("--leak-check=full"); + run.addArg("--show-leak-kinds=all"); + run.addArg("--track-origins=yes"); + run.addArg(cts_exe_path); + }, + else => {}, + } + run.addArg("run"); run.addArg("--verbose"); run.addArg("--deqp"); @@ -340,7 +361,24 @@ fn addMultithreadedCTS(b: *std.Build, target: std.Build.ResolvedTarget, impl: *c run.addArg(b.fmt("--deqp-vk-library-path={s}", .{b.getInstallPath(.lib, impl_lib.out_lib_filename)})); run.addArg("--deqp-test-oom=disable"); - const run_step = b.step(b.fmt("cts-{s}", .{impl.name}), b.fmt("Run Vulkan conformance tests for libvulkan_{s} in a multithreaded environment", .{impl.name})); + const run_step = b.step( + b.fmt("cts-{s}{s}", .{ + impl.name, + switch (mode) { + .normal => "", + .gdb => "-gdb", + .valgrind => "-valgrind", + }, + }), + b.fmt("Run Vulkan conformance tests for libvulkan_{s}{s} in a multithreaded environment", .{ + impl.name, + switch (mode) { + .normal => "", + .gdb => " within GDB", + .valgrind => " within Valgrind", + }, + }), + ); run_step.dependOn(&run.step); return &run.step; diff --git a/src/soft/SoftBuffer.zig b/src/soft/SoftBuffer.zig index 944c1cf..73d3b15 100644 --- a/src/soft/SoftBuffer.zig +++ b/src/soft/SoftBuffer.zig @@ -94,7 +94,7 @@ pub inline fn mapToWithAddedOffset(self: *const Self, comptime T: type, offset: return self.mapToWithOffset(T, self.interface.offset + offset); } -pub inline fn mapAsSliceWithAddedOffset(self: *const Self, comptime T: type, size: usize, offset: usize) VkError![]T { +pub inline fn mapAsSliceWithAddedOffset(self: *const Self, comptime T: type, offset: usize, size: usize) VkError![]T { return self.mapAsSliceWithOffset(T, self.interface.offset + offset, size); } diff --git a/src/soft/SoftCommandBuffer.zig b/src/soft/SoftCommandBuffer.zig index f4d4a70..2a114da 100644 --- a/src/soft/SoftCommandBuffer.zig +++ b/src/soft/SoftCommandBuffer.zig @@ -142,20 +142,57 @@ pub fn beginRenderPass(interface: *Interface, render_pass: *base.RenderPass, fra for (impl.render_pass.interface.attachments, impl.framebuffer.interface.attachments, 0..) |desc, attachment, index| { const image: *SoftImage = @alignCast(@fieldParentPtr("interface", attachment.image)); - const clear_format = try image.getClearFormat(); + var clear_mask: vk.ImageAspectFlags = .{}; switch (desc.load_op) { - .clear => { + .clear => clear_mask = .{ .color_bit = true, .depth_bit = true }, + else => {}, + } + + switch (desc.stencil_load_op) { + .clear => clear_mask = .{ .stencil_bit = true }, + else => {}, + } + + clear_mask = clear_mask.intersect(base.format.toAspect(attachment.format)); + + if (clear_mask.toInt() != 0) { + if (clear_mask.color_bit) { try blitter.clear( (impl.clear_values orelse return VkError.Unknown)[index], - clear_format, + try image.getClearFormat(), image, attachment.format, attachment.subresource_range, null, ); - }, - else => {}, + } else { + var subresource_range = attachment.subresource_range; + + if (clear_mask.depth_bit) { + subresource_range.aspect_mask = .{ .depth_bit = true }; + try blitter.clear( + (impl.clear_values orelse return VkError.Unknown)[index], + .d32_sfloat, + image, + attachment.format, + subresource_range, + null, + ); + } + + if (clear_mask.stencil_bit) { + subresource_range.aspect_mask = .{ .stencil_bit = true }; + try blitter.clear( + (impl.clear_values orelse return VkError.Unknown)[index], + .s8_uint, + image, + attachment.format, + subresource_range, + null, + ); + } + } } } } diff --git a/src/soft/SoftImage.zig b/src/soft/SoftImage.zig index c5e3cb5..9b1bd23 100644 --- a/src/soft/SoftImage.zig +++ b/src/soft/SoftImage.zig @@ -148,18 +148,16 @@ pub fn copyToImageSingleAspect(self: *const Self, dst: *Self, region: vk.ImageCo .mip_level = region.src_subresource.mip_level, .array_layer = region.src_subresource.base_array_layer, }); - const src_size = try self.interface.getTotalSizeForAspect(region.src_subresource.aspect_mask) - src_texel_offset; - const src_memory = if (self.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv; - var src_map: []u8 = @as([*]u8, @ptrCast(try src_memory.map(self.interface.memory_offset + src_texel_offset, src_size)))[0..src_size]; + const src_size = try self.interface.getTotalSizeForAspect(region.src_subresource.aspect_mask); + var src_map = try self.mapAsSliceWithAddedOffset(u8, src_texel_offset, src_size); const dst_texel_offset = try dst.getTexelMemoryOffset(region.dst_offset, .{ .aspect_mask = region.dst_subresource.aspect_mask, .mip_level = region.dst_subresource.mip_level, .array_layer = region.dst_subresource.base_array_layer, }); - const dst_size = try dst.interface.getTotalSizeForAspect(region.dst_subresource.aspect_mask) - dst_texel_offset; - const dst_memory = if (dst.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv; - var dst_map: []u8 = @as([*]u8, @ptrCast(try dst_memory.map(dst.interface.memory_offset + dst_texel_offset, dst_size)))[0..dst_size]; + const dst_size = try dst.interface.getTotalSizeForAspect(region.dst_subresource.aspect_mask); + var dst_map = try dst.mapAsSliceWithAddedOffset(u8, dst_texel_offset, dst_size); for (0..layer_count) |_| { if (is_single_row) { @@ -216,8 +214,7 @@ pub fn copyToImageSingleAspect(self: *const Self, dst: *Self, region: vk.ImageCo pub fn copyToBuffer(self: *const Self, dst: *SoftBuffer, region: vk.BufferImageCopy) VkError!void { const dst_size = dst.interface.size - region.buffer_offset; const dst_offset = dst.interface.offset + region.buffer_offset; - const dst_memory = if (dst.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv; - const dst_map: []u8 = @as([*]u8, @ptrCast(try dst_memory.map(dst_offset, dst_size)))[0..dst_size]; + const dst_map = try dst.mapAsSliceWithOffset(u8, dst_offset, dst_size); try self.copy( null, dst_map, @@ -230,8 +227,7 @@ pub fn copyToBuffer(self: *const Self, dst: *SoftBuffer, region: vk.BufferImageC pub fn copyFromBuffer(self: *const Self, src: *const SoftBuffer, region: vk.BufferImageCopy) VkError!void { const src_size = src.interface.size - region.buffer_offset; const src_offset = src.interface.offset + region.buffer_offset; - const src_memory = if (src.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv; - const src_map: []u8 = @as([*]u8, @ptrCast(try src_memory.map(src_offset, src_size)))[0..src_size]; + const src_map = try src.mapAsSliceWithOffset(u8, src_offset, src_size); try self.copy( src_map, null, @@ -285,9 +281,8 @@ pub fn copy( .mip_level = image_subresource.mip_level, .array_layer = image_subresource.base_array_layer, }); - const image_size = try self.interface.getTotalSizeForAspect(image_subresource.aspect_mask) - image_texel_offset; - const image_memory = if (self.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv; - const image_map: []u8 = @as([*]u8, @ptrCast(try image_memory.map(self.interface.memory_offset + image_texel_offset, image_size)))[0..image_size]; + const image_size = try self.interface.getTotalSizeForAspect(image_subresource.aspect_mask); + const image_map = try self.mapAsSliceWithAddedOffset(u8, image_texel_offset, image_size); var src_memory = if (is_source) base_src_memory orelse return VkError.InvalidDeviceMemoryDrv else image_map; var dst_memory = if (is_source) image_map else base_dst_memory orelse return VkError.InvalidDeviceMemoryDrv; @@ -328,34 +323,30 @@ pub fn copy( } pub fn readFloat4(self: *Self, offset: vk.Offset3D, subresource: vk.ImageSubresource, format: vk.Format) VkError!F32x4 { - const memory = if (self.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv; const texel_size = base.format.texelSize(format); const texel_offset = try self.getTexelMemoryOffset(offset, subresource); - const map: []const u8 = @as([*]u8, @ptrCast(try memory.map(self.interface.memory_offset + texel_offset, texel_size)))[0..texel_size]; + const map = try self.mapAsSliceWithAddedOffset(u8, texel_offset, texel_size); return blitter.readFloat4(map, format); } pub fn readInt4(self: *Self, offset: vk.Offset3D, subresource: vk.ImageSubresource, format: vk.Format) VkError!U32x4 { - const memory = if (self.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv; const texel_size = base.format.texelSize(format); const texel_offset = try self.getTexelMemoryOffset(offset, subresource); - const map: []const u8 = @as([*]u8, @ptrCast(try memory.map(self.interface.memory_offset + texel_offset, texel_size)))[0..texel_size]; + const map = try self.mapAsSliceWithAddedOffset(u8, texel_offset, texel_size); return blitter.readInt4(map, format); } pub fn writeFloat4(self: *Self, offset: vk.Offset3D, subresource: vk.ImageSubresource, format: vk.Format, pixel: F32x4) VkError!void { - const memory = if (self.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv; const texel_size = base.format.texelSize(format); const texel_offset = try self.getTexelMemoryOffset(offset, subresource); - const map: []u8 = @as([*]u8, @ptrCast(try memory.map(self.interface.memory_offset + texel_offset, texel_size)))[0..texel_size]; + const map = try self.mapAsSliceWithAddedOffset(u8, texel_offset, texel_size); blitter.writeFloat4(pixel, map, format); } pub fn writeInt4(self: *Self, offset: vk.Offset3D, subresource: vk.ImageSubresource, format: vk.Format, pixel: U32x4) VkError!void { - const memory = if (self.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv; const texel_size = base.format.texelSize(format); const texel_offset = try self.getTexelMemoryOffset(offset, subresource); - const map: []u8 = @as([*]u8, @ptrCast(try memory.map(self.interface.memory_offset + texel_offset, texel_size)))[0..texel_size]; + const map = try self.mapAsSliceWithAddedOffset(u8, texel_offset, texel_size); blitter.writeInt4(pixel, map, format); } @@ -366,7 +357,7 @@ pub fn getTexelMemoryOffsetInSubresource(self: *const Self, offset: vk.Offset3D, } pub fn getTexelMemoryOffset(self: *const Self, offset: vk.Offset3D, subresource: vk.ImageSubresource) VkError!usize { - return self.getTexelMemoryOffsetInSubresource(offset, subresource) + try self.getSubresourceOffset(subresource.aspect_mask, subresource.mip_level, subresource.array_layer); + return try self.getSubresourceOffset(subresource.aspect_mask, subresource.mip_level, subresource.array_layer) + self.getTexelMemoryOffsetInSubresource(offset, subresource); } fn getSubresourceOffset(self: *const Self, aspect_mask: vk.ImageAspectFlags, mip_level: u32, layer: u32) VkError!usize { @@ -486,3 +477,45 @@ pub fn getRowPitchMemSizeForMipLevel(interface: *const Interface, aspect_mask: v const format = self.interface.formatFromAspect(aspect_mask); return base.format.pitchMemSize(format, mip_extent.width); } + +pub inline fn mapAs(self: *const Self, comptime T: type) VkError!*T { + return self.mapAsWithAddedOffset(T, 0); +} + +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 { + return self.mapAsSliceWithAddedOffset(T, 0, size); +} + +pub inline fn mapAsWithAddedOffset(self: *const Self, comptime T: type, offset: usize) 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 { + 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 { + return self.mapAsSliceWithOffset(T, self.interface.memory_offset + offset, size); +} + +pub fn mapAsWithOffset(self: *const Self, comptime T: type, offset: usize) VkError!*T { + const memory = if (self.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv; + const map = @as([*]u8, @ptrCast(@alignCast(try memory.map(offset, @sizeOf(T)))))[0..@sizeOf(T)]; + return @alignCast(std.mem.bytesAsValue(T, map)); +} + +pub fn mapToWithOffset(self: *const Self, comptime T: type, offset: usize) VkError!T { + const memory = if (self.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv; + const map = @as([*]u8, @ptrCast(@alignCast(try memory.map(offset, @sizeOf(T)))))[0..@sizeOf(T)]; + return std.mem.bytesToValue(T, map); +} + +pub fn mapAsSliceWithOffset(self: *const Self, comptime T: type, offset: usize, size: usize) VkError![]T { + const memory = if (self.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv; + const map = @as([*]u8, @ptrCast(@alignCast(try memory.map(offset, size))))[0..size]; + return @alignCast(std.mem.bytesAsSlice(T, map)); +} diff --git a/src/soft/device/blitter.zig b/src/soft/device/blitter.zig index 4e96865..2b3deb5 100644 --- a/src/soft/device/blitter.zig +++ b/src/soft/device/blitter.zig @@ -70,7 +70,6 @@ pub fn clear(pixel: vk.ClearValue, format: vk.Format, dst: *SoftImage, view_form clamped_pixel.color.float_32[3] = std.math.clamp(pixel.color.float_32[3], min_value, 1.0); } - // Stencil never requires clamping, so we can check for Depth only if (range.aspect_mask.depth_bit) { clamped_pixel.depth_stencil.depth = std.math.clamp(pixel.depth_stencil.depth, min_value, 1.0); } @@ -182,7 +181,7 @@ fn fastClear(clear_value: vk.ClearValue, clear_format: vk.Format, dst: *SoftImag (@as(u32, @intFromFloat(255.0 * b + 0.5))), .d32_sfloat => { std.debug.assert(clear_format == .d32_sfloat); - pack = @bitCast(d); // float reinterpreted as uint32 + pack = @bitCast(d); // f32 reinterpreted as u32 }, .s8_uint => { std.debug.assert(clear_format == .s8_uint); @@ -204,8 +203,6 @@ fn fastClear(clear_value: vk.ClearValue, clear_format: vk.Format, dst: *SoftImag .extent = .{ .width = 0, .height = 0 }, }; - const dst_memory = if (dst.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv; - while (subresource.mip_level <= last_mip_level) : (subresource.mip_level += 1) { const dst_slice_pitch_bytes = dst.interface.getSliceMemSizeForMipLevel(subresource.aspect_mask, subresource.mip_level); const dst_row_pitch_bytes = dst.interface.getRowPitchMemSizeForMipLevel(subresource.aspect_mask, subresource.mip_level); @@ -220,8 +217,8 @@ fn fastClear(clear_value: vk.ClearValue, clear_format: vk.Format, dst: *SoftImag while (subresource.array_layer <= last_layer) : (subresource.array_layer += 1) { for (0..@intCast(extent.depth)) |depth| { const dst_texel_offset = try dst.getTexelMemoryOffset(.{ .x = area.offset.x, .y = area.offset.y, .z = @intCast(depth) }, subresource); - const dst_size = try dst.interface.getTotalSizeForAspect(subresource.aspect_mask) - dst_texel_offset; - var dst_map: []u8 = @as([*]u8, @ptrCast(try dst_memory.map(dst.interface.memory_offset + dst_texel_offset, dst_size)))[0..dst_size]; + const dst_size = try dst.interface.getTotalSizeForAspect(subresource.aspect_mask); + var dst_map = try dst.mapAsSliceWithAddedOffset(u8, dst_texel_offset, dst_size); for (0..dst.interface.samples.toInt()) |_| { var dst_pixel = dst_map[0..]; @@ -584,6 +581,8 @@ pub fn readFloat4(map: []const u8, src_format: vk.Format) F32x4 { .r32g32b32a32_sfloat => c = std.mem.bytesToValue(F32x4, map), + .s8_uint => c[0] = @floatFromInt(map[0]), + else => base.unsupported("Blitter: read float from source format {any}", .{src_format}), } @@ -594,10 +593,12 @@ pub fn writeFloat4(color: F32x4, map: []u8, dst_format: vk.Format) void { switch (dst_format) { .r8_snorm, .r8_unorm, + .s8_uint, => map[0] = @intFromFloat(@round(color[0] * 255.0)), .r16_sint, .r16_uint, + .d16_unorm, => std.mem.bytesAsValue(u16, map).* = @intFromFloat(@round(color[0])), .r16_sfloat => std.mem.bytesAsValue(f16, map).* = @floatCast(color[0]), @@ -606,7 +607,9 @@ pub fn writeFloat4(color: F32x4, map: []u8, dst_format: vk.Format) void { .r32_uint, => std.mem.bytesAsValue(u32, map).* = @intFromFloat(@round(color[0])), - .r32_sfloat => std.mem.bytesAsValue(f32, map).* = color[0], + .r32_sfloat, + .d32_sfloat, + => std.mem.bytesAsValue(f32, map).* = color[0], .b8g8r8a8_srgb, .b8g8r8a8_unorm, @@ -644,6 +647,7 @@ pub fn readInt4(map: []const u8, src_format: vk.Format) U32x4 { switch (src_format) { .r8_sint, .r8_uint, + .s8_uint, => c[0] = map[0], .r16_sint, .r16_uint, diff --git a/src/vulkan/VulkanAllocator.zig b/src/vulkan/VulkanAllocator.zig index 2e520df..e524a1a 100644 --- a/src/vulkan/VulkanAllocator.zig +++ b/src/vulkan/VulkanAllocator.zig @@ -55,10 +55,7 @@ fn alloc(context: *anyopaque, len: usize, alignment: Alignment, ret_addr: usize) if (self.callbacks) |callbacks| { if (callbacks.pfn_allocation) |pfn_allocation| { - const ptr: ?[*]u8 = @ptrCast(pfn_allocation(self.callbacks.?.p_user_data, len, alignment.toByteUnits(), self.scope)); - //std.debug.print("test {*}\n", .{ptr}); - //std.debug.dumpCurrentStackTrace(.{}); - return ptr; + return @ptrCast(pfn_allocation(self.callbacks.?.p_user_data, len, alignment.toByteUnits(), self.scope)); } } diff --git a/src/vulkan/format.zig b/src/vulkan/format.zig index b14fd44..9b254e2 100644 --- a/src/vulkan/format.zig +++ b/src/vulkan/format.zig @@ -4,7 +4,7 @@ const lib = @import("lib.zig"); const zm = @import("zmath"); pub fn fromAspect(format: vk.Format, aspect: vk.ImageAspectFlags) vk.Format { - if (aspect.color_bit or (aspect.color_bit and aspect.stencil_bit)) { + if (aspect.color_bit or (aspect.depth_bit and aspect.stencil_bit)) { return format; } else if (aspect.depth_bit) { if (format == .d16_unorm or format == .d16_unorm_s8_uint) { @@ -20,7 +20,7 @@ pub fn fromAspect(format: vk.Format, aspect: vk.ImageAspectFlags) vk.Format { } } lib.unsupported("format {s}", .{@tagName(format)}); - return format; + return .undefined; } pub fn toAspect(format: vk.Format) vk.ImageAspectFlags { @@ -40,7 +40,7 @@ pub inline fn texelSize(format: vk.Format) usize { return lib.c.vkuFormatTexelBlockSize(@intCast(@intFromEnum(format))); } -pub inline fn supportsColorAttachemendBlend(format: vk.Format) bool { +pub fn supportsColorAttachemendBlend(format: vk.Format) bool { return switch (format) { // Vulkan 1.1 mandatory .r5g6b5_unorm_pack16,