From d4ac14ae92d7bde48c259d830454f101b9919876 Mon Sep 17 00:00:00 2001 From: Kbz-8 Date: Thu, 2 Jul 2026 21:00:56 +0200 Subject: [PATCH] fixing vertex outputs --- build.zig | 4 +- build.zig.zon | 4 +- src/intel/FlintInstance.zig | 15 ++- src/intel/FlintPhysicalDevice.zig | 4 +- src/phi/PhiDevice.zig | 8 -- src/software/SoftCommandBuffer.zig | 2 +- src/software/SoftDevice.zig | 18 ---- src/software/SoftDeviceMemory.zig | 5 +- src/software/SoftPipeline.zig | 39 ++++++-- src/software/SoftPipelineCache.zig | 3 +- src/software/SoftQueue.zig | 6 +- src/software/SoftSampler.zig | 1 - src/software/SoftShaderModule.zig | 6 +- src/software/device/ComputeDispatcher.zig | 23 +++-- src/software/device/Renderer.zig | 9 +- src/software/device/fragment.zig | 74 ++++++++++++++- src/software/device/rasterizer.zig | 2 +- src/software/device/rasterizer/common.zig | 3 +- .../device/rasterizer/edge_function.zig | 2 +- src/software/device/vertex_dispatcher.zig | 93 +++++++++++++++++-- src/vulkan/Device.zig | 21 +++++ src/vulkan/Pipeline.zig | 2 + 22 files changed, 259 insertions(+), 85 deletions(-) diff --git a/build.zig b/build.zig index a110653..26c3444 100644 --- a/build.zig +++ b/build.zig @@ -88,10 +88,12 @@ pub fn build(b: *std.Build) !void { const drm = b.dependency("drm", .{}).module("drm"); const logs_option: LogType = b.option(LogType, "logs", "Driver logs") orelse .none; + const debug_allocator_option = b.option(bool, "device-debug-allocator", "Debug device allocator") orelse false; const options = b.addOptions(); options.addOption(std.SemanticVersion, "driver_version", driver_version); options.addOption(LogType, "logs", logs_option); + options.addOption(bool, "device_debug_allocator", debug_allocator_option); base_mod.addImport("vulkan", vulkan); base_mod.addImport("zmath", zmath); @@ -274,14 +276,12 @@ fn customSoft( fn optionsSoft(b: *std.Build, options: *Step.Options) !void { const single_threaded_option = b.option(bool, "soft-single-threaded", "Single threaded runtime mode") orelse false; - const debug_allocator_option = b.option(bool, "soft-debug-allocator", "Debug device allocator") orelse false; const shaders_simd_option = b.option(bool, "soft-shader-simd", "Shaders SIMD acceleration") orelse true; const compute_dump_early_results_table_option = b.option(u32, "soft-compute-dump-early-results-table", "Dump compute shaders results table before invocation"); const compute_dump_final_results_table_option = b.option(u32, "soft-compute-dump-final-results-table", "Dump compute shaders results table after invocation"); const approxiamte_rgb_option = b.option(bool, "soft-approximates-rgb", "Approximate sRGB <-> RGB conversions") orelse true; options.addOption(bool, "soft_single_threaded", single_threaded_option); - options.addOption(bool, "soft_debug_allocator", debug_allocator_option); options.addOption(bool, "soft_shaders_simd", shaders_simd_option); options.addOption(?u32, "soft_compute_dump_early_results_table", compute_dump_early_results_table_option); options.addOption(?u32, "soft_compute_dump_final_results_table", compute_dump_final_results_table_option); diff --git a/build.zig.zon b/build.zig.zon index 311fdb2..ba99c27 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#445bb93296bd6da4a74aa40152aaa779170887cf", - .hash = "SPIRV_Interpreter-0.0.1-ajmpn113CQDBhtMXf88no5gcMJhlricW5G_rWc7AvRS5", + .url = "git+https://github.com/Kbz-8/SPIRV-Interpreter#53da7f93154086574bf41a13130299d1bc6df670", + .hash = "SPIRV_Interpreter-0.0.1-ajmpn95-CQCUUA6EfT9id_Y50N7kjcKftj0VrGLyi751", .lazy = true, }, //.SPIRV_Interpreter = .{ diff --git a/src/intel/FlintInstance.zig b/src/intel/FlintInstance.zig index 09f8fea..9a101b2 100644 --- a/src/intel/FlintInstance.zig +++ b/src/intel/FlintInstance.zig @@ -76,7 +76,20 @@ fn requestPhysicalDevices(interface: *Interface, allocator: std.mem.Allocator, d drm_device.device_info.pci.vendor_id != lib.INTEL_PCI_VENDOR_ID) continue; - const physical_device = try FlintPhysicalDevice.create(allocator, interface, &drm_device); + const version = device.getVersion(io_var, allocator) catch continue; + defer version.deinit(allocator); + + const kmd_type: lib.KmdType = if (std.mem.eql(u8, version.name, "i915")) + .I915 + else if (std.mem.eql(u8, version.name, "xe")) + .Xe + else + .Invalid; + + if (kmd_type == .Invalid) + continue; + + const physical_device = try FlintPhysicalDevice.create(allocator, interface, &drm_device, kmd_type); errdefer physical_device.interface.release(allocator) catch {}; const dispatchable = try Dispatchable(base.PhysicalDevice).wrap(allocator, &physical_device.interface); diff --git a/src/intel/FlintPhysicalDevice.zig b/src/intel/FlintPhysicalDevice.zig index 612686f..0e03651 100644 --- a/src/intel/FlintPhysicalDevice.zig +++ b/src/intel/FlintPhysicalDevice.zig @@ -30,8 +30,9 @@ pub const EXTENSIONS = [_]vk.ExtensionProperties{ }; interface: Interface, +kmd_type: lib.KmdType, -pub fn create(allocator: std.mem.Allocator, instance: *base.Instance, drm_device: *const base.drm.Device) VkError!*Self { +pub fn create(allocator: std.mem.Allocator, instance: *base.Instance, drm_device: *const base.drm.Device, kmd_type: lib.KmdType) VkError!*Self { const self = allocator.create(Self) catch return VkError.OutOfHostMemory; errdefer allocator.destroy(self); @@ -223,6 +224,7 @@ pub fn create(allocator: std.mem.Allocator, instance: *base.Instance, drm_device self.* = .{ .interface = interface, + .kmd_type = kmd_type, }; return self; diff --git a/src/phi/PhiDevice.zig b/src/phi/PhiDevice.zig index fa116c4..2221286 100644 --- a/src/phi/PhiDevice.zig +++ b/src/phi/PhiDevice.zig @@ -31,14 +31,7 @@ const VkError = base.VkError; const Self = @This(); pub const Interface = base.Device; -const DeviceAllocator = struct { - pub inline fn allocator(_: @This()) std.mem.Allocator { - return base.fallback_host_allocator; - } -}; - interface: Interface, -allocator: DeviceAllocator, 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; @@ -80,7 +73,6 @@ pub fn create(instance: *base.Instance, physical_device: *base.PhysicalDevice, a self.* = .{ .interface = interface, - .allocator = .{}, }; try self.interface.createQueues(allocator, info); diff --git a/src/software/SoftCommandBuffer.zig b/src/software/SoftCommandBuffer.zig index 4bd8d05..ce17372 100644 --- a/src/software/SoftCommandBuffer.zig +++ b/src/software/SoftCommandBuffer.zig @@ -195,7 +195,7 @@ pub fn beginQuery(interface: *Interface, pool: *base.QueryPool, query: u32, _: v if (active.pool == impl.pool and active.query == impl.query) return; } - device.active_occlusion_queries.append(device.renderer.device.device_allocator.allocator(), .{ + device.active_occlusion_queries.append(device.renderer.device.interface.device_allocator.allocator(), .{ .pool = impl.pool, .query = impl.query, }) catch return VkError.OutOfDeviceMemory; diff --git a/src/software/SoftDevice.zig b/src/software/SoftDevice.zig index 9d62097..a8599dc 100644 --- a/src/software/SoftDevice.zig +++ b/src/software/SoftDevice.zig @@ -35,14 +35,7 @@ pub const Interface = base.Device; const SpawnError = std.Thread.SpawnError; -const DeviceAllocator = struct { - pub inline fn allocator(_: @This()) std.mem.Allocator { - return base.fallback_host_allocator; - } -}; - interface: Interface, -device_allocator: if (config.soft_debug_allocator) std.heap.DebugAllocator(.{}) else DeviceAllocator, 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; @@ -91,7 +84,6 @@ pub fn create(instance: *base.Instance, physical_device: *base.PhysicalDevice, a self.* = .{ .interface = interface, - .device_allocator = if (config.soft_debug_allocator) .init else .{}, }; initialized = true; @@ -102,16 +94,6 @@ pub fn create(instance: *base.Instance, physical_device: *base.PhysicalDevice, a pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) VkError!void { const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); - if (config.soft_debug_allocator) { - // All device memory allocations should've been freed by now - const leaks = self.device_allocator.detectLeaks(); - if (leaks != 0) - std.log.scoped(.vkDestroyDevice).err("Device was destroyed leaking {d:.3}KB", .{@as(f32, @floatFromInt(leaks)) / 1000}) - else - std.log.scoped(.vkDestroyDevice).debug("No device memory leaks detected", .{}); - self.device_allocator.deinitWithoutLeakChecks(); - } - allocator.destroy(self); } diff --git a/src/software/SoftDeviceMemory.zig b/src/software/SoftDeviceMemory.zig index b809e3f..9d15268 100644 --- a/src/software/SoftDeviceMemory.zig +++ b/src/software/SoftDeviceMemory.zig @@ -28,15 +28,14 @@ pub fn create(device: *SoftDevice, allocator: std.mem.Allocator, size: vk.Device self.* = .{ .interface = interface, - .data = device.device_allocator.allocator().alloc(u8, size) catch return VkError.OutOfDeviceMemory, + .data = device.interface.device_allocator.allocator().alloc(u8, size) catch return VkError.OutOfDeviceMemory, }; return self; } pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void { const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); - const soft_device: *SoftDevice = @alignCast(@fieldParentPtr("interface", interface.owner)); - soft_device.device_allocator.allocator().free(self.data); + interface.owner.device_allocator.allocator().free(self.data); allocator.destroy(self); } diff --git a/src/software/SoftPipeline.zig b/src/software/SoftPipeline.zig index b389c48..30d2394 100644 --- a/src/software/SoftPipeline.zig +++ b/src/software/SoftPipeline.zig @@ -79,11 +79,10 @@ pub fn createCompute(device: *base.Device, allocator: std.mem.Allocator, cache: .destroy = destroy, }; - const soft_device: *SoftDevice = @alignCast(@fieldParentPtr("interface", device)); const module = try NonDispatchable(ShaderModule).fromHandleObject(info.stage.module); const soft_module: *SoftShaderModule = @alignCast(@fieldParentPtr("interface", module)); - const device_allocator = soft_device.device_allocator.allocator(); + const device_allocator = device.device_allocator.allocator(); const soft_cache: ?*SoftPipelineCache = if (cache) |pipeline_cache| @alignCast(@fieldParentPtr("interface", pipeline_cache)) else @@ -123,8 +122,7 @@ pub fn createGraphics(device: *base.Device, allocator: std.mem.Allocator, cache: .destroy = destroy, }; - const soft_device: *SoftDevice = @alignCast(@fieldParentPtr("interface", device)); - const device_allocator = soft_device.device_allocator.allocator(); + const device_allocator = device.device_allocator.allocator(); const soft_cache: ?*SoftPipelineCache = if (cache) |pipeline_cache| @alignCast(@fieldParentPtr("interface", pipeline_cache)) else @@ -185,8 +183,7 @@ pub fn createGraphics(device: *base.Device, allocator: std.mem.Allocator, cache: pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void { const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); - const soft_device: *SoftDevice = @alignCast(@fieldParentPtr("interface", interface.owner)); - const device_allocator = soft_device.device_allocator.allocator(); + const device_allocator = interface.owner.device_allocator.allocator(); var it = self.stages.iterator(); while (it.next()) |entry| { @@ -486,10 +483,22 @@ fn readImageFloat4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32, .@"2d", .@"2d_array", .cube, .cube_array => .{ .x = x, .y = y, .z = 0 }, else => .{ .x = x, .y = y, .z = z }, }; + const sample_count = image.interface.samples.toInt(); + const sample_index: u32 = if (sample_count > 1) blk: { + if (image_view.interface.view_type == .@"2d_array" or image_view.interface.view_type == .cube_array) + break :blk @intCast(@mod(z, @as(i32, @intCast(sample_count)))); + break :blk @intCast(z); + } else 0; + const array_z: i32 = if (sample_count > 1 and + (image_view.interface.view_type == .@"2d_array" or image_view.interface.view_type == .cube_array)) + @divFloor(z, @as(i32, @intCast(sample_count))) + else + z; const array_layer = image_view.interface.subresource_range.base_array_layer + switch (image_view.interface.view_type) { .@"1d_array" => @as(u32, @intCast(y)), - .@"2d_array" => @as(u32, @intCast(z)), + .@"2d_array" => @as(u32, @intCast(array_z)), .cube => cube_face, + .cube_array => @as(u32, @intCast(array_z)), else => 0, }; const aspect_mask = imageReadAspect(image_view, false); @@ -498,7 +507,6 @@ fn readImageFloat4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32, .mip_level = mip_level, .array_layer = array_layer, }; - const sample_index: u32 = if (image.interface.samples.toInt() > 1) @intCast(z) else 0; const format = base.format.fromAspect(image_view.interface.format, aspect_mask); const raw_pixel = if (dim == .SubpassData) blk: { if (findInputAttachmentSnapshot(image_view, subresource)) |snapshot| { @@ -541,10 +549,22 @@ fn readImageInt4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32, l .@"2d", .@"2d_array", .cube, .cube_array => .{ .x = x, .y = y, .z = 0 }, else => .{ .x = x, .y = y, .z = z }, }; + const sample_count = image.interface.samples.toInt(); + const sample_index: u32 = if (sample_count > 1) blk: { + if (image_view.interface.view_type == .@"2d_array" or image_view.interface.view_type == .cube_array) + break :blk @intCast(@mod(z, @as(i32, @intCast(sample_count)))); + break :blk @intCast(z); + } else 0; + const array_z: i32 = if (sample_count > 1 and + (image_view.interface.view_type == .@"2d_array" or image_view.interface.view_type == .cube_array)) + @divFloor(z, @as(i32, @intCast(sample_count))) + else + z; const array_layer = image_view.interface.subresource_range.base_array_layer + switch (image_view.interface.view_type) { .@"1d_array" => @as(u32, @intCast(y)), - .@"2d_array" => @as(u32, @intCast(z)), + .@"2d_array" => @as(u32, @intCast(array_z)), .cube => cube_face, + .cube_array => @as(u32, @intCast(array_z)), else => 0, }; const aspect_mask = imageReadAspect(image_view, true); @@ -553,7 +573,6 @@ fn readImageInt4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32, l .mip_level = mip_level, .array_layer = array_layer, }; - const sample_index: u32 = if (image.interface.samples.toInt() > 1) @intCast(z) else 0; const format = base.format.fromAspect(image_view.interface.format, aspect_mask); const raw_pixel = if (dim == .SubpassData) blk: { if (findInputAttachmentSnapshot(image_view, subresource)) |snapshot| { diff --git a/src/software/SoftPipelineCache.zig b/src/software/SoftPipelineCache.zig index f82be8d..bab0295 100644 --- a/src/software/SoftPipelineCache.zig +++ b/src/software/SoftPipelineCache.zig @@ -67,8 +67,7 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void { const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); - const soft_device: *SoftDevice = @alignCast(@fieldParentPtr("interface", interface.owner)); - const data_allocator = soft_device.device_allocator.allocator(); + const data_allocator = interface.owner.device_allocator.allocator(); for (self.shaders.items) |*shader| { shader.deinit(allocator, data_allocator); diff --git a/src/software/SoftQueue.zig b/src/software/SoftQueue.zig index 152cc6f..d112a80 100644 --- a/src/software/SoftQueue.zig +++ b/src/software/SoftQueue.zig @@ -69,7 +69,7 @@ pub fn submit(interface: *Interface, infos: []Interface.SubmitInfo, p_fence: ?*b const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); const soft_device: *SoftDevice = @alignCast(@fieldParentPtr("interface", interface.owner)); const io = interface.owner.io(); - const allocator = soft_device.device_allocator.allocator(); + const allocator = interface.owner.device_allocator.allocator(); const data = allocator.create(TaskData) catch return VkError.OutOfDeviceMemory; errdefer allocator.destroy(data); @@ -111,7 +111,7 @@ fn executeSubmitInfo(soft_device: *SoftDevice, info: Interface.SubmitInfo) VkErr var execution_device: ExecutionDevice = undefined; execution_device.setup(soft_device); - defer execution_device.deinit(soft_device.device_allocator.allocator()); + defer execution_device.deinit(soft_device.interface.device_allocator.allocator()); for (info.command_buffers.items) |command_buffer| { const soft_command_buffer: *SoftCommandBuffer = @alignCast(@fieldParentPtr("interface", command_buffer)); @@ -125,7 +125,7 @@ fn executeSubmitInfo(soft_device: *SoftDevice, info: Interface.SubmitInfo) VkErr fn taskRunner(data: *TaskData) void { const io = data.queue.interface.owner.io(); - const allocator = data.soft_device.device_allocator.allocator(); + const allocator = data.soft_device.interface.device_allocator.allocator(); defer { deinitSubmitInfos(allocator, &data.infos); diff --git a/src/software/SoftSampler.zig b/src/software/SoftSampler.zig index 71d0b6d..282cd3b 100644 --- a/src/software/SoftSampler.zig +++ b/src/software/SoftSampler.zig @@ -276,7 +276,6 @@ pub fn queryImageLod(image: *SoftImage, image_view: *SoftImageView, sampler: *Se const clamped_lod = std.math.clamp(biased_lod, sampler.interface.min_lod, sampler.interface.max_lod); const max_level: f32 = @floatFromInt(viewMipCount(image_view) - 1); const level = std.math.clamp(mipmapModeLevel(sampler, clamped_lod), 0.0, max_level); - return .{ level, lod, 0.0, 0.0 }; } diff --git a/src/software/SoftShaderModule.zig b/src/software/SoftShaderModule.zig index 2f72142..3df942d 100644 --- a/src/software/SoftShaderModule.zig +++ b/src/software/SoftShaderModule.zig @@ -25,8 +25,7 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v var interface = try Interface.init(device, allocator, info); - const soft_device: *SoftDevice = @alignCast(@fieldParentPtr("interface", device)); - const device_allocator = soft_device.device_allocator.allocator(); + const device_allocator = device.device_allocator.allocator(); interface.vtable = &.{ .destroy = destroy, @@ -62,8 +61,7 @@ pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void { } pub fn drop(self: *Self, allocator: std.mem.Allocator) void { - const soft_device: *SoftDevice = @alignCast(@fieldParentPtr("interface", self.interface.owner)); - const device_allocator = soft_device.device_allocator.allocator(); + const device_allocator = self.interface.owner.device_allocator.allocator(); self.module.deinit(device_allocator); diff --git a/src/software/device/ComputeDispatcher.zig b/src/software/device/ComputeDispatcher.zig index 8794a70..dd4139d 100644 --- a/src/software/device/ComputeDispatcher.zig +++ b/src/software/device/ComputeDispatcher.zig @@ -75,7 +75,7 @@ pub fn dispatchBase(self: *Self, base_group_x: u32, base_group_y: u32, base_grou const spv_module = &shader.module.module; self.batch_size = if (spv_module.reflection_infos.has_atomics) 1 else shader.runtimes.len; - const allocator = self.device.device_allocator.allocator(); + const allocator = self.device.interface.device_allocator.allocator(); const local_size = try getLocalSize(&shader.runtimes[0].rt, allocator, spv_module); const local_size_xy = std.math.mul(usize, local_size[0], local_size[1]) catch return VkError.ValidationFailed; const invocations_per_workgroup = std.math.mul(usize, local_size_xy, local_size[2]) catch return VkError.ValidationFailed; @@ -124,7 +124,7 @@ fn runWrapper(data: RunData) void { } inline fn run(data: RunData) !void { - const allocator = data.self.device.device_allocator.allocator(); + const allocator = data.self.device.interface.device_allocator.allocator(); const io = data.self.device.interface.io(); const shader = data.pipeline.stages.getPtrAssertContains(.compute); @@ -233,7 +233,8 @@ fn runBarrierWorkgroup( group_count: @Vector(3, u32), group_id: @Vector(3, u32), ) !void { - const allocator = data.self.device.device_allocator.allocator(); + const allocator = data.self.device.interface.device_allocator.allocator(); + const workgroup_memory = try runtimes[0].createWorkgroupMemory(allocator); defer runtimes[0].destroyWorkgroupMemory(allocator, workgroup_memory); for (runtimes, 0..) |*rt, i| { @@ -292,21 +293,25 @@ fn dumpResultsTable(allocator: std.mem.Allocator, io: std.Io, rt: *spv.Runtime, } fn setupWorkgroupBuiltins(self: *Self, rt: *spv.Runtime, local_size: @Vector(3, u32), group_count: @Vector(3, u32), group_id: @Vector(3, u32)) spv.Runtime.RuntimeError!void { - rt.writeBuiltIn(self.device.device_allocator.allocator(), std.mem.asBytes(&local_size), .WorkgroupSize) catch |err| switch (err) { + const allocator = self.device.interface.device_allocator.allocator(); + + rt.writeBuiltIn(allocator, std.mem.asBytes(&local_size), .WorkgroupSize) catch |err| switch (err) { SpvRuntimeError.NotFound => {}, else => return err, }; - rt.writeBuiltIn(self.device.device_allocator.allocator(), std.mem.asBytes(&group_count), .NumWorkgroups) catch |err| switch (err) { + rt.writeBuiltIn(allocator, std.mem.asBytes(&group_count), .NumWorkgroups) catch |err| switch (err) { SpvRuntimeError.NotFound => {}, else => return err, }; - rt.writeBuiltIn(self.device.device_allocator.allocator(), std.mem.asBytes(&group_id), .WorkgroupId) catch |err| switch (err) { + rt.writeBuiltIn(allocator, std.mem.asBytes(&group_id), .WorkgroupId) catch |err| switch (err) { SpvRuntimeError.NotFound => {}, else => return err, }; } fn setupSubgroupBuiltins(self: *Self, rt: *spv.Runtime, local_size: @Vector(3, u32), group_id: @Vector(3, u32), local_invocation_index: usize) spv.Runtime.RuntimeError!void { + const allocator = self.device.interface.device_allocator.allocator(); + const local_base = local_size * group_id; var local_invocation = @Vector(3, u32){ 0, 0, 0 }; @@ -320,15 +325,15 @@ fn setupSubgroupBuiltins(self: *Self, rt: *spv.Runtime, local_size: @Vector(3, u const global_invocation_index = local_base + local_invocation; const local_invocation_index_u32: u32 = @intCast(local_invocation_index); - rt.writeBuiltIn(self.device.device_allocator.allocator(), std.mem.asBytes(&local_invocation), .LocalInvocationId) catch |err| switch (err) { + rt.writeBuiltIn(allocator, std.mem.asBytes(&local_invocation), .LocalInvocationId) catch |err| switch (err) { SpvRuntimeError.NotFound => {}, else => return err, }; - rt.writeBuiltIn(self.device.device_allocator.allocator(), std.mem.asBytes(&local_invocation_index_u32), .LocalInvocationIndex) catch |err| switch (err) { + rt.writeBuiltIn(allocator, std.mem.asBytes(&local_invocation_index_u32), .LocalInvocationIndex) catch |err| switch (err) { SpvRuntimeError.NotFound => {}, else => return err, }; - rt.writeBuiltIn(self.device.device_allocator.allocator(), std.mem.asBytes(&global_invocation_index), .GlobalInvocationId) catch |err| switch (err) { + rt.writeBuiltIn(allocator, std.mem.asBytes(&global_invocation_index), .GlobalInvocationId) catch |err| switch (err) { SpvRuntimeError.NotFound => {}, else => return err, }; diff --git a/src/software/device/Renderer.zig b/src/software/device/Renderer.zig index 3b3c477..78e529c 100644 --- a/src/software/device/Renderer.zig +++ b/src/software/device/Renderer.zig @@ -187,7 +187,7 @@ pub fn init(device: *SoftDevice, state: *PipelineState, active_occlusion_queries } pub fn resetInputAttachmentSnapshots(self: *Self) void { - const allocator = self.device.device_allocator.allocator(); + const allocator = self.device.interface.device_allocator.allocator(); for (self.input_attachment_snapshots) |snapshot| { allocator.free(snapshot.data); } @@ -196,12 +196,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.device_allocator.allocator(), 4 * @"1GiB"); + var bounded_allocator: BoundedAllocator = .init(self.device.interface.device_allocator.allocator(), 4 * @"1GiB"); 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.device_allocator.allocator(), 4 * @"1GiB"); + var bounded_allocator: BoundedAllocator = .init(self.device.interface.device_allocator.allocator(), 4 * @"1GiB"); const allocator = bounded_allocator.allocator(); const indexed_draw = try self.readIndexBuffer(allocator, index_count, first_index, vertex_offset); @@ -266,6 +266,9 @@ fn drawCall(self: *Self, bounded_allocator: *BoundedAllocator, vertex_count: usi return VkError.Unknown; }; + if (pipeline.interface.mode.graphics.rasterization.rasterizer_discard_enable) + return; + draw_call.viewport = try self.resolveViewport(0); draw_call.scissor = try self.resolveScissor(0); diff --git a/src/software/device/fragment.zig b/src/software/device/fragment.zig index d9696a8..68c76d0 100644 --- a/src/software/device/fragment.zig +++ b/src/software/device/fragment.zig @@ -67,10 +67,8 @@ pub fn shaderInvocation( else => return err, }; if (rt.getBuiltinResult(.FragCoord)) |frag_coord_word| { - const frag_x: i32 = @intFromFloat(position[0]); - const frag_y: i32 = @intFromFloat(position[1]); - const frag_coord_dx = zm.f32x4(if (@mod(frag_x, 2) == 0) 1.0 else -1.0, 0.0, 0.0, 0.0); - const frag_coord_dy = zm.f32x4(0.0, if (@mod(frag_y, 2) == 0) 1.0 else -1.0, 0.0, 0.0); + const frag_coord_dx = zm.f32x4(1.0, 0.0, 0.0, 0.0); + const frag_coord_dy = zm.f32x4(0.0, 1.0, 0.0, 0.0); try rt.setDerivativeFromMemory(allocator, frag_coord_word, std.mem.asBytes(&frag_coord_dx), std.mem.asBytes(&frag_coord_dy)); } if (point_coord) |coord| { @@ -170,7 +168,9 @@ pub fn shaderInvocation( if (input.size != 0) { const input_bytes = input.blob[0..input.size]; - if (!has_result_value or input.size < memory_size) + if (has_result_value and input.size < memory_size) + try writeAggregateInputLocations(allocator, rt, fragment_inputs, result_word, location, component, memory_size) + else if (!has_result_value) try rt.writeInputLocation(input_bytes, @intCast(location)) else try rt.writeInput(allocator, input_bytes, result_word); @@ -262,6 +262,70 @@ pub fn shaderInvocation( }; } +fn writeAggregateInputLocations( + allocator: std.mem.Allocator, + rt: *spv.Runtime, + inputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolationLocation, + result_word: spv.SpvWord, + location: usize, + component: usize, + memory_size: usize, +) SpvRuntimeError!void { + const value = try rt.results[result_word].getConstValue(); + switch (value.*) { + .Array, .Matrix => {}, + else => { + const input = inputs[location][component]; + if (input.size != 0) + try rt.writeInputLocation(input.blob[0..input.size], @intCast(location)); + return; + }, + } + + const bytes = allocator.alloc(u8, memory_size + INTERFACE_BLOB_PADDING) catch return SpvRuntimeError.OutOfMemory; + defer allocator.free(bytes); + @memset(bytes, 0); + + var source_location = location; + var offset: usize = 0; + try copyAggregateInputLocations(value, inputs, &source_location, component, bytes[0..memory_size], &offset); + try rt.writeInput(allocator, bytes[0..memory_size], result_word); +} + +fn copyAggregateInputLocations( + value: anytype, + inputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolationLocation, + source_location: *usize, + component: usize, + bytes: []u8, + offset: *usize, +) SpvRuntimeError!void { + switch (value.*) { + .Array => |array| { + for (array.values) |*element| + try copyAggregateInputLocations(element, inputs, source_location, component, bytes, offset); + }, + .Matrix => |columns| { + for (columns) |*column| + try copyAggregateInputLocations(column, inputs, source_location, component, bytes, offset); + }, + else => { + if (source_location.* >= inputs.len or component >= 4) + return SpvRuntimeError.OutOfBounds; + const input = inputs[source_location.*][component]; + const element_size = try value.getPlainMemorySize(); + if (offset.* > bytes.len or element_size > bytes.len - offset.*) + return SpvRuntimeError.OutOfBounds; + if (input.size != 0) { + const copy_size = @min(element_size, input.size); + @memcpy(bytes[offset.* .. offset.* + copy_size], input.blob[0..copy_size]); + } + offset.* += element_size; + source_location.* += 1; + }, + } +} + fn readFragmentOutput( allocator: std.mem.Allocator, rt: anytype, diff --git a/src/software/device/rasterizer.zig b/src/software/device/rasterizer.zig index 959473e..16497ec 100644 --- a/src/software/device/rasterizer.zig +++ b/src/software/device/rasterizer.zig @@ -107,7 +107,7 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato const pipeline_data = (renderer.state.pipeline orelse return VkError.InvalidHandleDrv).interface.mode.graphics; const topology = pipeline_data.input_assembly.topology; if (renderer.input_attachment_snapshots.len == 0) { - renderer.input_attachment_snapshots = try snapshotInputAttachments(renderer.device.device_allocator.allocator(), draw_call); + renderer.input_attachment_snapshots = try snapshotInputAttachments(renderer.device.interface.device_allocator.allocator(), draw_call); } draw_call.input_attachment_snapshots = renderer.input_attachment_snapshots; diff --git a/src/software/device/rasterizer/common.zig b/src/software/device/rasterizer/common.zig index 40e1f89..3b6d975 100644 --- a/src/software/device/rasterizer/common.zig +++ b/src/software/device/rasterizer/common.zig @@ -612,7 +612,8 @@ pub fn writeToTargets( const color = maybe_color orelse continue; const color_offset = targetSampleOffset(color, x, y, sample_index) orelse continue; if (base.format.isUnnormalizedInteger(color.format)) { - blitter.writeInt4(std.mem.bytesToValue(U32x4, &outputs[location]), color.base[color_offset..], color.format); + const value = std.mem.bytesToValue(U32x4, &outputs[location]); + blitter.writeInt4(value, color.base[color_offset..], color.format); } else { const src = fragmentOutputFloat4(outputs[location], color.format); const encoded_dst = blitter.readFloat4(color.base[color_offset..], color.format); diff --git a/src/software/device/rasterizer/edge_function.zig b/src/software/device/rasterizer/edge_function.zig index af15d07..21597de 100644 --- a/src/software/device/rasterizer/edge_function.zig +++ b/src/software/device/rasterizer/edge_function.zig @@ -381,7 +381,7 @@ inline fn run(data: RunData) !void { if (early_depth.mask == 0) continue; - const interpolation_barycentrics = if (data.sample_count > 1) blk: { + const interpolation_barycentrics = if (data.sample_count > 1 and data.fragment_uses_centroid) blk: { const sample_pos = firstCoveredSamplePosition(data.sample_count, early_depth.mask); const centroid_p = zm.f32x4( @as(f32, @floatFromInt(x)) + sample_pos.x, diff --git a/src/software/device/vertex_dispatcher.zig b/src/software/device/vertex_dispatcher.zig index b81798c..fb5d29a 100644 --- a/src/software/device/vertex_dispatcher.zig +++ b/src/software/device/vertex_dispatcher.zig @@ -115,6 +115,7 @@ inline fn run(data: RunData) !void { try readPosition(rt, std.mem.asBytes(&output.position)); try readPointSize(rt, &output.point_size); + try readActiveInterfaceOutputs(data, output, rt, entry); for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| { const location_result = rt.getResultByLocation(@intCast(location), .output) catch |err| switch (err) { @@ -122,7 +123,8 @@ inline fn run(data: RunData) !void { else => return err, }; - try readVertexOutput(data, output, rt, location, 0, location_result); + if (output.outputs[location][0] == null) + try readVertexOutput(data, output, rt, location, 0, location_result); for (1..4) |component| { const component_result = rt.getResultByLocationComponent(@intCast(location), @intCast(component), .output) catch |err| switch (err) { @@ -133,12 +135,11 @@ inline fn run(data: RunData) !void { if (component_result == location_result) continue; - try readVertexOutput(data, output, rt, location, component, component_result); + if (output.outputs[location][component] == null) + try readVertexOutput(data, output, rt, location, component, component_result); } } - try readActiveInterfaceOutputs(data, output, rt, entry); - try rt.flushDescriptorSets(data.allocator); } } @@ -170,9 +171,8 @@ fn readActiveInterfaceOutputs(data: RunData, output: *Renderer.Vertex, rt: *spv. if (rt.results[type_word].variant) |type_variant| switch (type_variant) { .Type => |t| switch (t) { .Structure => |structure| { - const base_location = location orelse continue; for (structure.members_type_word, 0..) |_, member_index| { - const member_location = interfaceMemberLocation(rt, type_word, base_location, @intCast(member_index)); + const member_location = explicitInterfaceMemberLocation(rt, type_word, @intCast(member_index)) orelse continue; if (member_location >= spv.SPIRV_MAX_OUTPUT_LOCATIONS) continue; if (output.outputs[member_location][component] != null) @@ -199,8 +199,16 @@ fn readActiveInterfaceOutputs(data: RunData, output: *Renderer.Vertex, rt: *spv. } fn readVertexOutput(data: RunData, output: *Renderer.Vertex, rt: *spv.Runtime, location: usize, component: usize, result_word: spv.SpvWord) !void { + const value = try rt.results[result_word].getConstValue(); const memory_size = try rt.getResultMemorySize(result_word); const interpolation_type = vertexOutputInterpolationType(data, rt, location, component, result_word); + switch (value.*) { + .Array, .Matrix => { + try readVertexOutputAggregate(data, output, value, location, component, interpolation_type); + return; + }, + else => {}, + } output.outputs[location][component] = .{ .interpolation_type = interpolation_type, @@ -211,6 +219,66 @@ fn readVertexOutput(data: RunData, output: *Renderer.Vertex, rt: *spv.Runtime, l try rt.readOutput(output.outputs[location][component].?.blob, result_word); } +fn readVertexOutputAggregate( + data: RunData, + output: *Renderer.Vertex, + value: anytype, + location: usize, + component: usize, + interpolation_type: Renderer.InterpolationType, +) !void { + var target_location = location; + switch (value.*) { + .Array => |array| { + for (array.values) |*element| { + try readVertexOutputAggregate(data, output, element, target_location, component, interpolation_type); + target_location += vertexOutputValueLocationCount(element); + } + }, + .Matrix => |columns| { + for (columns) |*column| { + try writeVertexOutputValue(data, output, column, target_location, component, interpolation_type); + target_location += 1; + } + }, + else => try writeVertexOutputValue(data, output, value, location, component, interpolation_type), + } +} + +fn writeVertexOutputValue( + data: RunData, + output: *Renderer.Vertex, + value: anytype, + location: usize, + component: usize, + interpolation_type: Renderer.InterpolationType, +) !void { + if (location >= spv.SPIRV_MAX_OUTPUT_LOCATIONS or component >= 4) + return VkError.ValidationFailed; + + const memory_size = try value.getPlainMemorySize(); + output.outputs[location][component] = .{ + .interpolation_type = interpolation_type, + .blob = data.allocator.alloc(u8, memory_size + INTERFACE_BLOB_PADDING) catch return VkError.OutOfDeviceMemory, + .size = memory_size, + }; + @memset(output.outputs[location][component].?.blob, 0); + _ = try value.read(output.outputs[location][component].?.blob[0..memory_size]); +} + +fn vertexOutputValueLocationCount(value: anytype) usize { + return switch (value.*) { + .Array => |array| count: { + var location_count: usize = 0; + for (array.values) |*element| + location_count += vertexOutputValueLocationCount(element); + break :count location_count; + }, + .Matrix => |columns| columns.len, + else => 1, + }; +} + fn vertexOutputInterpolationType(data: RunData, rt: *spv.Runtime, location: usize, component: usize, result_word: spv.SpvWord) Renderer.InterpolationType { const result_is_integer = resultIsInteger(rt, result_word); @@ -259,7 +327,7 @@ fn interfaceLocationMemberHasDecoration(rt: *const spv.Runtime, location: usize, continue; const type_word = pointerTargetType(rt, variable.type_word) orelse continue; - const base_location = resultLocation(rt, @intCast(id)) orelse continue; + const base_location = resultLocation(rt, @intCast(id)); const type_result = rt.results[type_word]; const type_variant = type_result.variant orelse continue; switch (type_variant) { @@ -292,14 +360,21 @@ fn resultLocation(rt: *const spv.Runtime, result_word: spv.SpvWord) ?usize { return null; } -fn interfaceMemberLocation(rt: *const spv.Runtime, type_word: spv.SpvWord, base_location: usize, member_index: spv.SpvWord) usize { +fn interfaceMemberLocation(rt: *const spv.Runtime, type_word: spv.SpvWord, base_location: ?usize, member_index: spv.SpvWord) ?usize { + if (explicitInterfaceMemberLocation(rt, type_word, member_index)) |location| + return location; + + return if (base_location) |location| location + @as(usize, @intCast(member_index)) else null; +} + +fn explicitInterfaceMemberLocation(rt: *const spv.Runtime, type_word: spv.SpvWord, member_index: spv.SpvWord) ?usize { if (type_word < rt.results.len) { for (rt.results[type_word].decorations.items) |decoration| { if (decoration.rtype == .Location and decoration.index == member_index) return decoration.literal_1; } } - return base_location + @as(usize, @intCast(member_index)); + return null; } fn interfaceMemberHasDecoration(rt: *const spv.Runtime, variable_word: spv.SpvWord, member_index: spv.SpvWord, decoration: anytype) bool { diff --git a/src/vulkan/Device.zig b/src/vulkan/Device.zig index af04ea5..288a312 100644 --- a/src/vulkan/Device.zig +++ b/src/vulkan/Device.zig @@ -1,5 +1,7 @@ const std = @import("std"); const vk = @import("vulkan"); +const lib = @import("lib.zig"); +const config = lib.config; const Dispatchable = @import("Dispatchable.zig").Dispatchable; const NonDispatchable = @import("NonDispatchable.zig").NonDispatchable; @@ -37,10 +39,17 @@ const SwapchainKHR = @import("wsi/SwapchainKHR.zig"); const Self = @This(); pub const ObjectType: vk.ObjectType = .device; +const DeviceAllocator = struct { + pub inline fn allocator(_: @This()) std.mem.Allocator { + return lib.fallback_host_allocator; + } +}; + instance: *Instance, physical_device: *const PhysicalDevice, queues: std.AutoArrayHashMapUnmanaged(u32, std.ArrayList(*Dispatchable(Queue))), host_allocator: VulkanAllocator, +device_allocator: if (config.device_debug_allocator) std.heap.DebugAllocator(.{}) else DeviceAllocator, enabled_khr_swapchain: bool, enabled_khr_device_group: bool, @@ -110,6 +119,7 @@ pub fn init(allocator: std.mem.Allocator, instance: *Instance, physical_device: .host_allocator = VulkanAllocator.from(allocator).cloneWithScope(.object), .enabled_khr_swapchain = enabled_khr_swapchain, .enabled_khr_device_group = enabled_khr_device_group, + .device_allocator = if (config.device_debug_allocator) .init else .{}, .dispatch_table = undefined, .vtable = undefined, }; @@ -154,6 +164,17 @@ pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) VkError!void { family.deinit(allocator); } self.queues.deinit(allocator); + + if (config.device_debug_allocator) { + // All device memory allocations should've been freed by now + const leaks = self.device_allocator.detectLeaks(); + if (leaks != 0) + std.log.scoped(.vkDestroyDevice).err("Device was destroyed leaking {d:.3}KB", .{@as(f32, @floatFromInt(leaks)) / 1000}) + else + std.log.scoped(.vkDestroyDevice).debug("No device memory leaks detected", .{}); + self.device_allocator.deinitWithoutLeakChecks(); + } + try self.dispatch_table.destroy(self, allocator); } diff --git a/src/vulkan/Pipeline.zig b/src/vulkan/Pipeline.zig index 7fbc3f6..d8c84cb 100644 --- a/src/vulkan/Pipeline.zig +++ b/src/vulkan/Pipeline.zig @@ -45,6 +45,7 @@ mode: union(enum) { scissor: ?[]vk.Rect2D, }, rasterization: struct { + rasterizer_discard_enable: bool, polygon_mode: vk.PolygonMode, cull_mode: vk.CullModeFlags, front_face: vk.FrontFace, @@ -194,6 +195,7 @@ pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*Pipe }, }, .rasterization = .{ + .rasterizer_discard_enable = rasterizer_discard_enable, .polygon_mode = if (info.p_rasterization_state) |state| state.polygon_mode else return VkError.ValidationFailed, .cull_mode = if (info.p_rasterization_state) |state| state.cull_mode else return VkError.ValidationFailed, .front_face = if (info.p_rasterization_state) |state| state.front_face else return VkError.ValidationFailed,