diff --git a/README.md b/README.md index d4da4d6..e4e933d 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ Assume thou that functions lacking in this array are, for now, not intended to b Name | Status -------------------------------------------------|-------- +vkAcquireNextImage2KHR | ✅ Implemented vkAcquireNextImageKHR | ✅ Implemented vkAllocateCommandBuffers | ✅ Implemented vkAllocateDescriptorSets | ✅ Implemented @@ -71,6 +72,7 @@ vkCmdCopyImage | ✅ Implemented vkCmdCopyImageToBuffer | ✅ Implemented vkCmdCopyQueryPoolResults | ✅ Implemented vkCmdDispatch | ✅ Implemented +vkCmdDispatchBaseKHR | ✅ Implemented vkCmdDispatchIndirect | ✅ Implemented vkCmdDraw | ✅ Implemented vkCmdDrawIndexed | ✅ Implemented @@ -89,6 +91,7 @@ vkCmdResolveImage | ✅ Implemented vkCmdSetBlendConstants | ✅ Implemented vkCmdSetDepthBias | ⚙️ WIP vkCmdSetDepthBounds | ⚙️ WIP +vkCmdSetDeviceMaskKHR | ✅ Implemented vkCmdSetEvent | ✅ Implemented vkCmdSetLineWidth | ⚙️ WIP vkCmdSetScissor | ✅ Implemented @@ -153,13 +156,16 @@ vkEnumerateDeviceExtensionProperties | ✅ Implemented vkEnumerateDeviceLayerProperties | ✅ Implemented vkEnumerateInstanceExtensionProperties | ✅ Implemented vkEnumerateInstanceLayerProperties | ✅ Implemented -vkEnumeratePhysicalDevices | ✅ Implemented vkEnumeratePhysicalDeviceGroupsKHR | ✅ Implemented +vkEnumeratePhysicalDevices | ✅ Implemented vkFlushMappedMemoryRanges | ✅ Implemented vkFreeCommandBuffers | ✅ Implemented vkFreeDescriptorSets | ✅ Implemented vkFreeMemory | ✅ Implemented vkGetBufferMemoryRequirements | ✅ Implemented +vkGetDeviceGroupPeerMemoryFeaturesKHR | ✅ Implemented +vkGetDeviceGroupPresentCapabilitiesKHR | ✅ Implemented +vkGetDeviceGroupSurfacePresentModesKHR | ✅ Implemented vkGetDeviceMemoryCommitment | ⚙️ WIP vkGetDeviceProcAddr | ✅ Implemented vkGetDeviceQueue | ✅ Implemented diff --git a/build.zig b/build.zig index 5b26201..51a3ce4 100644 --- a/build.zig +++ b/build.zig @@ -4,24 +4,37 @@ const builtin = @import("builtin"); const ImplementationDesc = struct { name: []const u8, + icd_name: ?[]const u8 = null, root_source_file: []const u8, vulkan_version: std.SemanticVersion, custom: ?*const fn ( *std.Build, *Step.Compile, + *std.Build.Module, + *std.Build.Module, + *std.Build.Module, + *std.Build.Module, std.Build.ResolvedTarget, std.builtin.OptimizeMode, - *Step.Options, bool, ) anyerror!void = null, + options: ?*const fn (*std.Build, *Step.Options) anyerror!void = null, }; const implementations = [_]ImplementationDesc{ + .{ + .name = "ape", + .icd_name = "ape", + .root_source_file = "src/ape/lib.zig", + .vulkan_version = .{ .major = 1, .minor = 0, .patch = 0 }, + .custom = customApe, + }, .{ .name = "soft", .root_source_file = "src/soft/lib.zig", .vulkan_version = .{ .major = 1, .minor = 0, .patch = 0 }, .custom = customSoft, + .options = optionsSoft, }, }; @@ -79,7 +92,8 @@ pub fn build(b: *std.Build) !void { base_c_includes.link_libc = true; } - base_mod.addImport("base_c", base_c_includes.createModule()); + const base_c_mod = base_c_includes.createModule(); + base_mod.addImport("base_c", base_c_mod); const use_llvm = b.option(bool, "use-llvm", "LLVM build") orelse (b.release_mode != .off); @@ -104,12 +118,22 @@ pub fn build(b: *std.Build) !void { .use_llvm = use_llvm, }); - if (impl.custom) |custom| { - custom(b, lib, target, optimize, options, use_llvm) catch continue; + if (impl.custom) |func| { + func(b, lib, lib_mod, base_mod, vulkan, base_c_mod, target, optimize, use_llvm) catch continue; + } + + if (impl.options) |func| { + func(b, options) catch continue; } const icd_file = b.addWriteFile( - b.getInstallPath(.lib, b.fmt("vk_ape_{s}.json", .{impl.name})), + b.getInstallPath( + .lib, + if (impl.icd_name) |icd_name| + b.fmt("vk_{s}.json", .{icd_name}) + else + b.fmt("vk_ape_{s}.json", .{impl.name}), + ), b.fmt( \\{{ \\ "file_format_version": "1.0.1", @@ -171,12 +195,48 @@ pub fn build(b: *std.Build) !void { docs_step.dependOn(&install_docs.step); } -fn customSoft( +fn customApe( b: *std.Build, lib: *Step.Compile, + lib_mod: *std.Build.Module, + base_mod: *std.Build.Module, + vulkan: *std.Build.Module, + base_c_mod: *std.Build.Module, + target: std.Build.ResolvedTarget, + optimize: std.builtin.OptimizeMode, + use_llvm: bool, +) !void { + for (implementations) |impl| { + if (std.mem.eql(u8, impl.name, "ape")) + continue; + + const mod = b.createModule(.{ + .root_source_file = b.path(impl.root_source_file), + .target = target, + .optimize = optimize, + .imports = &.{ + .{ .name = "base", .module = base_mod }, + .{ .name = "vulkan", .module = vulkan }, + }, + }); + + if (impl.custom) |func| { + func(b, lib, mod, base_mod, vulkan, base_c_mod, target, optimize, use_llvm) catch continue; + } + + lib_mod.addImport(impl.name, mod); + } +} + +fn customSoft( + b: *std.Build, + _: *Step.Compile, + lib_mod: *std.Build.Module, + _: *std.Build.Module, + _: *std.Build.Module, + base_c_mod: *std.Build.Module, _: std.Build.ResolvedTarget, _: std.builtin.OptimizeMode, - options: *Step.Options, use_llvm: bool, ) !void { const spv = b.lazyDependency("SPIRV_Interpreter", .{ @@ -184,23 +244,25 @@ fn customSoft( .@"no-test" = true, .@"use-llvm" = use_llvm, }) orelse return error.UnresolvedDependency; - lib.root_module.addImport("spv", spv.module("spv")); - const single_threaded_option = b.option(bool, "single-threaded", "Single threaded runtime mode") orelse false; - const debug_allocator_option = b.option(bool, "debug-allocator", "Debug device allocator") orelse false; - const shaders_simd_option = b.option(bool, "shader-simd", "Shaders SIMD acceleration") orelse true; - const single_threaded_compute_option = b.option(bool, "single-threaded-compute", "Single threaded compute shaders execution") orelse true; - const compute_dump_early_results_table_option = b.option(u32, "compute-dump-early-results-table", "Dump compute shaders results table before invocation"); - const compute_dump_final_results_table_option = b.option(u32, "compute-dump-final-results-table", "Dump compute shaders results table after invocation"); - const approxiamte_rgb_option = b.option(bool, "approximates-rgb", "Approximate sRGB <-> RGB conversions") orelse true; + lib_mod.addImport("soft_c", base_c_mod); + lib_mod.addImport("spv", spv.module("spv")); +} - options.addOption(bool, "single_threaded", single_threaded_option); - options.addOption(bool, "debug_allocator", debug_allocator_option); - options.addOption(bool, "shaders_simd", shaders_simd_option); - options.addOption(bool, "single_threaded_compute", single_threaded_compute_option); - options.addOption(?u32, "compute_dump_early_results_table", compute_dump_early_results_table_option); - options.addOption(?u32, "compute_dump_final_results_table", compute_dump_final_results_table_option); - options.addOption(bool, "approximates_rgb", approxiamte_rgb_option); +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); + options.addOption(bool, "soft_approximates_rgb", approxiamte_rgb_option); } fn addCTS(b: *std.Build, target: std.Build.ResolvedTarget, impl: *const ImplementationDesc, impl_lib: *Step.Compile, comptime mode: RunningMode) !*Step { diff --git a/build.zig.zon b/build.zig.zon index a259fb2..8ea540f 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -26,7 +26,7 @@ .hash = "N-V-__8AAF9uOh0I4P_99za7N822J3JwsDaqONrFVrcEQo59", }, .SPIRV_Interpreter = .{ - .url = "git+https://git.kbz8.me/kbz_8/SPIRV-Interpreter#52ded6b490a47d843ad401cbd2c41b4dbdc7dca1", + .url = "git+https://git.kbz8.me/kbz_8/SPIRV-Interpreter#42554a5cc1069e8174b377e95ce2ac2d802a44ff", .hash = "SPIRV_Interpreter-0.0.1-ajmpn0GXBwD01BnmV_Kf8EeNqTDoyuq7UvHVaq-WoBP0", .lazy = true, }, diff --git a/src/ape/ApeInstance.zig b/src/ape/ApeInstance.zig new file mode 100644 index 0000000..3af2c27 --- /dev/null +++ b/src/ape/ApeInstance.zig @@ -0,0 +1,92 @@ +const std = @import("std"); +const vk = @import("vulkan"); +const base = @import("base"); +const soft = @import("soft"); + +const Dispatchable = base.Dispatchable; +const VkError = base.VkError; + +const Self = @This(); +pub const Interface = base.Instance; + +interface: Interface, +backend_instances: std.ArrayList(*Interface), + +pub const EXTENSIONS = soft.Instance.EXTENSIONS; + +pub fn create(allocator: std.mem.Allocator, infos: *const vk.InstanceCreateInfo) VkError!*Interface { + const self = allocator.create(Self) catch return VkError.OutOfHostMemory; + errdefer allocator.destroy(self); + + self.* = .{ + .interface = try base.Instance.init(allocator, infos), + .backend_instances = .empty, + }; + errdefer destroyBackendInstances(self, allocator); + + self.interface.dispatch_table = &.{ + .destroy = destroy, + }; + self.interface.vtable = &.{ + .requestPhysicalDevices = requestPhysicalDevices, + .releasePhysicalDevices = releasePhysicalDevices, + .io = io, + }; + + const soft_instance = try soft.Instance.create(allocator, infos); + errdefer soft_instance.deinit(allocator) catch {}; + self.backend_instances.append(allocator, soft_instance) catch return VkError.OutOfHostMemory; + + return &self.interface; +} + +fn destroy(interface: *Interface, allocator: std.mem.Allocator) VkError!void { + const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); + destroyBackendInstances(self, allocator); + self.backend_instances.deinit(allocator); + allocator.destroy(self); +} + +fn requestPhysicalDevices(interface: *Interface, allocator: std.mem.Allocator) VkError!void { + const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); + + for (self.backend_instances.items) |backend| { + try appendBackendPhysicalDevices(self, allocator, backend); + } +} + +fn appendBackendPhysicalDevices(self: *Self, allocator: std.mem.Allocator, backend: *Interface) VkError!void { + try backend.requestPhysicalDevices(allocator); + errdefer backend.releasePhysicalDevices(allocator) catch {}; + + self.interface.physical_devices.appendSlice(allocator, backend.physical_devices.items) catch return VkError.OutOfHostMemory; + backend.physical_devices.deinit(allocator); + backend.physical_devices = .empty; +} + +fn releasePhysicalDevices(interface: *Interface, allocator: std.mem.Allocator) VkError!void { + for (interface.physical_devices.items) |physical_device| { + try physical_device.object.releasePhysicalDevice(allocator); + physical_device.destroy(allocator); + } + + interface.physical_devices.deinit(allocator); + interface.physical_devices = .empty; +} + +fn destroyBackendInstances(self: *Self, allocator: std.mem.Allocator) void { + for (self.backend_instances.items) |backend| { + backend.dispatch_table.destroy(backend, allocator) catch |err| { + base.errors.errorLogger(err); + }; + } + self.backend_instances.clearRetainingCapacity(); +} + +fn io(interface: *Interface) std.Io { + const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); + if (self.backend_instances.items.len != 0) { + return self.backend_instances.items[0].io(); + } + unreachable; +} diff --git a/src/ape/lib.zig b/src/ape/lib.zig new file mode 100644 index 0000000..a8f2adb --- /dev/null +++ b/src/ape/lib.zig @@ -0,0 +1,28 @@ +const std = @import("std"); +const vk = @import("vulkan"); +pub const base = @import("base"); +pub const soft = @import("soft"); + +pub const c = base.c; + +pub const ApeInstance = @import("ApeInstance.zig"); + +pub const Instance = ApeInstance; + +pub const DRIVER_NAME = "Ape"; + +pub const VULKAN_VERSION = vk.makeApiVersion(0, 1, 0, 0); +pub const DRIVER_VERSION = vk.makeApiVersion(0, 0, 0, 1); + +pub const std_options = base.std_options; + +comptime { + _ = base; + _ = soft; +} + +test { + std.testing.refAllDecls(ApeInstance); + std.testing.refAllDecls(soft); + std.testing.refAllDecls(base); +} diff --git a/src/soft/SoftCommandBuffer.zig b/src/soft/SoftCommandBuffer.zig index 556b81c..aea9419 100644 --- a/src/soft/SoftCommandBuffer.zig +++ b/src/soft/SoftCommandBuffer.zig @@ -61,6 +61,7 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v .copyImageToBuffer = copyImageToBuffer, .copyQueryPoolResults = copyQueryPoolResults, .dispatch = dispatch, + .dispatchBase = dispatchBase, .dispatchIndirect = dispatchIndirect, .draw = draw, .drawIndexed = drawIndexed, @@ -80,6 +81,7 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v .resolveImage = resolveImage, .setEvent = setEvent, .setBlendConstants = setBlendConstants, + .setDeviceMask = setDeviceMask, .setScissor = setScissor, .setStencilCompareMask = setStencilCompareMask, .setStencilReference = setStencilReference, @@ -782,25 +784,35 @@ pub fn copyQueryPoolResults(interface: *Interface, pool: *base.QueryPool, first: } pub fn dispatch(interface: *Interface, group_count_x: u32, group_count_y: u32, group_count_z: u32) VkError!void { + try dispatchBase(interface, 0, 0, 0, group_count_x, group_count_y, group_count_z); +} + +pub fn dispatchBase(interface: *Interface, base_group_x: u32, base_group_y: u32, base_group_z: u32, group_count_x: u32, group_count_y: u32, group_count_z: u32) VkError!void { const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); const allocator = self.command_allocator.allocator(); const CommandImpl = struct { const Impl = @This(); + base_group_x: u32, + base_group_y: u32, + base_group_z: u32, group_count_x: u32, group_count_y: u32, group_count_z: u32, pub fn execute(context: *anyopaque, device: *ExecutionDevice) VkError!void { const impl: *Impl = @ptrCast(@alignCast(context)); - try device.compute.dispatch(impl.group_count_x, impl.group_count_y, impl.group_count_z); + try device.compute.dispatchBase(impl.base_group_x, impl.base_group_y, impl.base_group_z, impl.group_count_x, impl.group_count_y, impl.group_count_z); } }; const cmd = allocator.create(CommandImpl) catch return VkError.OutOfHostMemory; errdefer allocator.destroy(cmd); cmd.* = .{ + .base_group_x = base_group_x, + .base_group_y = base_group_y, + .base_group_z = base_group_z, .group_count_x = group_count_x, .group_count_y = group_count_y, .group_count_z = group_count_z, @@ -808,6 +820,10 @@ pub fn dispatch(interface: *Interface, group_count_x: u32, group_count_y: u32, g self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory; } +pub fn setDeviceMask(_: *Interface, device_mask: u32) VkError!void { + if (device_mask != 1) return VkError.ValidationFailed; +} + pub fn dispatchIndirect(interface: *Interface, buffer: *base.Buffer, offset: vk.DeviceSize) VkError!void { const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); const allocator = self.command_allocator.allocator(); diff --git a/src/soft/SoftDevice.zig b/src/soft/SoftDevice.zig index aa55f8a..9d62097 100644 --- a/src/soft/SoftDevice.zig +++ b/src/soft/SoftDevice.zig @@ -42,7 +42,7 @@ const DeviceAllocator = struct { }; interface: Interface, -device_allocator: if (config.debug_allocator) std.heap.DebugAllocator(.{}) else DeviceAllocator, +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; @@ -84,11 +84,14 @@ pub fn create(instance: *base.Instance, physical_device: *base.PhysicalDevice, a .createSemaphore = createSemaphore, .createShaderModule = createShaderModule, .destroy = destroy, + .getDeviceGroupPeerMemoryFeatures = getDeviceGroupPeerMemoryFeatures, + .getDeviceGroupPresentCapabilitiesKHR = getDeviceGroupPresentCapabilitiesKHR, + .getDeviceGroupSurfacePresentModesKHR = getDeviceGroupSurfacePresentModesKHR, }; self.* = .{ .interface = interface, - .device_allocator = if (config.debug_allocator) .init else .{}, + .device_allocator = if (config.soft_debug_allocator) .init else .{}, }; initialized = true; @@ -99,7 +102,7 @@ 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.debug_allocator) { + if (config.soft_debug_allocator) { // All device memory allocations should've been freed by now const leaks = self.device_allocator.detectLeaks(); if (leaks != 0) @@ -212,3 +215,25 @@ pub fn createShaderModule(interface: *Interface, allocator: std.mem.Allocator, i const module = try SoftShaderModule.create(interface, allocator, info); return &module.interface; } + +pub fn getDeviceGroupPeerMemoryFeatures(interface: *Interface, heap_index: u32, local_device_index: u32, remote_device_index: u32) VkError!vk.PeerMemoryFeatureFlags { + if (heap_index >= interface.physical_device.mem_props.memory_heap_count) return VkError.ValidationFailed; + if (local_device_index != 0 or remote_device_index != 0) return VkError.ValidationFailed; + + return .{ + .copy_src_bit = true, + .copy_dst_bit = true, + .generic_src_bit = true, + .generic_dst_bit = true, + }; +} + +pub fn getDeviceGroupPresentCapabilitiesKHR(_: *Interface, capabilities: *vk.DeviceGroupPresentCapabilitiesKHR) VkError!void { + capabilities.present_mask = @splat(0); + capabilities.present_mask[0] = 1; + capabilities.modes = .{ .local_bit_khr = true }; +} + +pub fn getDeviceGroupSurfacePresentModesKHR(_: *Interface, _: *base.SurfaceKHR) VkError!vk.DeviceGroupPresentModeFlagsKHR { + return .{ .local_bit_khr = true }; +} diff --git a/src/soft/SoftInstance.zig b/src/soft/SoftInstance.zig index 887455d..325e297 100644 --- a/src/soft/SoftInstance.zig +++ b/src/soft/SoftInstance.zig @@ -36,7 +36,7 @@ pub fn create(allocator: std.mem.Allocator, infos: *const vk.InstanceCreateInfo) errdefer allocator.destroy(self); self.allocator = std.heap.smp_allocator; - self.threaded = if (comptime base.config.single_threaded) .init_single_threaded else std.Io.Threaded.init(self.allocator, .{}); + self.threaded = if (comptime base.config.soft_single_threaded) .init_single_threaded else std.Io.Threaded.init(self.allocator, .{}); self.io_impl = self.threaded.io(); self.interface = try base.Instance.init(allocator, infos); diff --git a/src/soft/SoftPhysicalDevice.zig b/src/soft/SoftPhysicalDevice.zig index b9d130d..6a6b046 100644 --- a/src/soft/SoftPhysicalDevice.zig +++ b/src/soft/SoftPhysicalDevice.zig @@ -23,6 +23,7 @@ fn castExtension(comptime ext: vk.ApiInfo) vk.ExtensionProperties { } pub const EXTENSIONS = [_]vk.ExtensionProperties{ + castExtension(vk.extensions.khr_device_group), castExtension(vk.extensions.khr_swapchain), }; diff --git a/src/soft/SoftShaderModule.zig b/src/soft/SoftShaderModule.zig index 2df9fe2..2f72142 100644 --- a/src/soft/SoftShaderModule.zig +++ b/src/soft/SoftShaderModule.zig @@ -37,7 +37,7 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v self.* = .{ .interface = interface, .module = spv.Module.init(device_allocator, code, .{ - .use_simd_vectors_specializations = base.config.shaders_simd, + .use_simd_vectors_specializations = base.config.soft_shaders_simd, }) catch |err| switch (err) { spv.Module.ModuleError.OutOfMemory => return VkError.OutOfHostMemory, else => { diff --git a/src/soft/device/ComputeDispatcher.zig b/src/soft/device/ComputeDispatcher.zig index 4aba796..ab4e95e 100644 --- a/src/soft/device/ComputeDispatcher.zig +++ b/src/soft/device/ComputeDispatcher.zig @@ -19,6 +19,9 @@ const RunData = struct { self: *Self, batch_id: usize, group_count: usize, + base_group_x: usize, + base_group_y: usize, + base_group_z: usize, group_count_x: usize, group_count_y: usize, group_count_z: usize, @@ -41,12 +44,16 @@ pub fn init(device: *SoftDevice, state: *PipelineState) Self { .state = state, .batch_size = 0, .invocation_index = .init(0), - .early_dump = base.config.compute_dump_early_results_table, - .final_dump = base.config.compute_dump_final_results_table, + .early_dump = base.config.soft_compute_dump_early_results_table, + .final_dump = base.config.soft_compute_dump_final_results_table, }; } pub fn dispatch(self: *Self, group_count_x: u32, group_count_y: u32, group_count_z: u32) VkError!void { + try self.dispatchBase(0, 0, 0, group_count_x, group_count_y, group_count_z); +} + +pub fn dispatchBase(self: *Self, base_group_x: u32, base_group_y: u32, base_group_z: u32, group_count_x: u32, group_count_y: u32, group_count_z: u32) VkError!void { const group_count_xy = std.math.mul(usize, group_count_x, group_count_y) catch return VkError.ValidationFailed; const group_count = std.math.mul(usize, group_count_xy, group_count_z) catch return VkError.ValidationFailed; @@ -73,6 +80,9 @@ pub fn dispatch(self: *Self, group_count_x: u32, group_count_y: u32, group_count .self = self, .batch_id = batch_id, .group_count = group_count, + .base_group_x = @as(usize, @intCast(base_group_x)), + .base_group_y = @as(usize, @intCast(base_group_y)), + .base_group_z = @as(usize, @intCast(base_group_z)), .group_count_x = @as(usize, @intCast(group_count_x)), .group_count_y = @as(usize, @intCast(group_count_y)), .group_count_z = @as(usize, @intCast(group_count_z)), @@ -80,10 +90,7 @@ pub fn dispatch(self: *Self, group_count_x: u32, group_count_y: u32, group_count .pipeline = pipeline, }; - if (comptime base.config.single_threaded_compute) - runWrapper(run_data) - else - wg.async(self.device.interface.io(), runWrapper, .{run_data}); + wg.async(self.device.interface.io(), runWrapper, .{run_data}); } wg.await(self.device.interface.io()) catch return VkError.DeviceLost; } @@ -151,9 +158,9 @@ inline fn run(data: RunData) !void { @as(u32, @intCast(data.group_count_z)), }; const group_id_vec = @Vector(3, u32){ - @as(u32, @intCast(group_x)), - @as(u32, @intCast(group_y)), - @as(u32, @intCast(group_z)), + @as(u32, @intCast(data.base_group_x + group_x)), + @as(u32, @intCast(data.base_group_y + group_y)), + @as(u32, @intCast(data.base_group_z + group_z)), }; if (uses_control_barrier) { @@ -168,9 +175,9 @@ inline fn run(data: RunData) !void { const invocation_index = data.self.invocation_index.fetchAdd(1, .monotonic); try setupSubgroupBuiltins(data.self, rt, .{ - @as(u32, @intCast(group_x)), - @as(u32, @intCast(group_y)), - @as(u32, @intCast(group_z)), + @as(u32, @intCast(data.base_group_x + group_x)), + @as(u32, @intCast(data.base_group_y + group_y)), + @as(u32, @intCast(data.base_group_z + group_z)), }, i); if (data.self.early_dump != null and data.self.early_dump.? == invocation_index) { diff --git a/src/vulkan/CommandBuffer.zig b/src/vulkan/CommandBuffer.zig index ae2b1e2..3664b04 100644 --- a/src/vulkan/CommandBuffer.zig +++ b/src/vulkan/CommandBuffer.zig @@ -33,6 +33,7 @@ pool: *CommandPool, state: State, begin_info: ?vk.CommandBufferBeginInfo, usage_flags: vk.CommandBufferUsageFlags, +device_mask: u32, host_allocator: VulkanAllocator, state_mutex: std.Io.Mutex, @@ -57,6 +58,7 @@ pub const DispatchTable = struct { copyImageToBuffer: *const fn (*Self, *Image, vk.ImageLayout, *Buffer, []const vk.BufferImageCopy) VkError!void, copyQueryPoolResults: *const fn (*Self, *QueryPool, u32, u32, *Buffer, vk.DeviceSize, vk.DeviceSize, vk.QueryResultFlags) VkError!void, dispatch: *const fn (*Self, u32, u32, u32) VkError!void, + dispatchBase: *const fn (*Self, u32, u32, u32, u32, u32, u32) VkError!void, dispatchIndirect: *const fn (*Self, *Buffer, vk.DeviceSize) VkError!void, draw: *const fn (*Self, usize, usize, usize, usize) VkError!void, drawIndexed: *const fn (*Self, usize, usize, usize, i32, usize) VkError!void, @@ -76,6 +78,7 @@ pub const DispatchTable = struct { resolveImage: *const fn (*Self, *Image, vk.ImageLayout, *Image, vk.ImageLayout, vk.ImageResolve) VkError!void, setEvent: *const fn (*Self, *Event, vk.PipelineStageFlags) VkError!void, setBlendConstants: *const fn (*Self, [4]f32) VkError!void, + setDeviceMask: *const fn (*Self, u32) VkError!void, setScissor: *const fn (*Self, u32, []const vk.Rect2D) VkError!void, setStencilCompareMask: *const fn (*Self, vk.StencilFaceFlags, u32) VkError!void, setStencilReference: *const fn (*Self, vk.StencilFaceFlags, u32) VkError!void, @@ -96,6 +99,7 @@ pub fn init(device: *Device, allocator: std.mem.Allocator, info: *const vk.Comma .state = .Initial, .begin_info = null, .usage_flags = .{}, + .device_mask = 1, .host_allocator = VulkanAllocator.from(allocator).cloneWithScope(.object), .state_mutex = .init, .vtable = undefined, @@ -152,6 +156,7 @@ pub fn resetFromPool(self: *Self, flags: vk.CommandBufferResetFlags) VkError!voi try self.dispatch_table.reset(self, flags); self.begin_info = null; self.usage_flags = .{}; + self.device_mask = 1; } pub fn submit(self: *Self) VkError!void { @@ -247,6 +252,10 @@ pub inline fn dispatch(self: *Self, group_count_x: u32, group_count_y: u32, grou try self.dispatch_table.dispatch(self, group_count_x, group_count_y, group_count_z); } +pub inline fn dispatchBase(self: *Self, base_group_x: u32, base_group_y: u32, base_group_z: u32, group_count_x: u32, group_count_y: u32, group_count_z: u32) VkError!void { + try self.dispatch_table.dispatchBase(self, base_group_x, base_group_y, base_group_z, group_count_x, group_count_y, group_count_z); +} + pub inline fn dispatchIndirect(self: *Self, buffer: *Buffer, offset: vk.DeviceSize) VkError!void { try self.dispatch_table.dispatchIndirect(self, buffer, offset); } @@ -329,6 +338,13 @@ pub inline fn setBlendConstants(self: *Self, constants: [4]f32) VkError!void { try self.dispatch_table.setBlendConstants(self, constants); } +pub inline fn setDeviceMask(self: *Self, device_mask: u32) VkError!void { + if (device_mask != 1) + return VkError.ValidationFailed; + self.device_mask = device_mask; + try self.dispatch_table.setDeviceMask(self, device_mask); +} + pub inline fn setScissor(self: *Self, first: u32, scissor: []const vk.Rect2D) VkError!void { try self.dispatch_table.setScissor(self, first, scissor); } diff --git a/src/vulkan/Device.zig b/src/vulkan/Device.zig index 6c5f042..3ea01ce 100644 --- a/src/vulkan/Device.zig +++ b/src/vulkan/Device.zig @@ -72,6 +72,9 @@ pub const DispatchTable = struct { createSemaphore: *const fn (*Self, std.mem.Allocator, *const vk.SemaphoreCreateInfo) VkError!*BinarySemaphore, createShaderModule: *const fn (*Self, std.mem.Allocator, *const vk.ShaderModuleCreateInfo) VkError!*ShaderModule, destroy: *const fn (*Self, std.mem.Allocator) VkError!void, + getDeviceGroupPeerMemoryFeatures: *const fn (*Self, u32, u32, u32) VkError!vk.PeerMemoryFeatureFlags, + getDeviceGroupPresentCapabilitiesKHR: *const fn (*Self, *vk.DeviceGroupPresentCapabilitiesKHR) VkError!void, + getDeviceGroupSurfacePresentModesKHR: *const fn (*Self, *SurfaceKHR) VkError!vk.DeviceGroupPresentModeFlagsKHR, }; pub fn init(allocator: std.mem.Allocator, instance: *Instance, physical_device: *const PhysicalDevice, _: *const vk.DeviceCreateInfo) VkError!Self { @@ -215,3 +218,15 @@ pub fn waitIdle(self: *Self) VkError!void { } } } + +pub inline fn getDeviceGroupPeerMemoryFeatures(self: *Self, heap_index: u32, local_device_index: u32, remote_device_index: u32) VkError!vk.PeerMemoryFeatureFlags { + return self.dispatch_table.getDeviceGroupPeerMemoryFeatures(self, heap_index, local_device_index, remote_device_index); +} + +pub inline fn getDeviceGroupPresentCapabilitiesKHR(self: *Self, capabilities: *vk.DeviceGroupPresentCapabilitiesKHR) VkError!void { + try self.dispatch_table.getDeviceGroupPresentCapabilitiesKHR(self, capabilities); +} + +pub inline fn getDeviceGroupSurfacePresentModesKHR(self: *Self, surface: *SurfaceKHR) VkError!vk.DeviceGroupPresentModeFlagsKHR { + return self.dispatch_table.getDeviceGroupSurfacePresentModesKHR(self, surface); +} diff --git a/src/vulkan/lib_vulkan.zig b/src/vulkan/lib_vulkan.zig index b0ed139..e803757 100644 --- a/src/vulkan/lib_vulkan.zig +++ b/src/vulkan/lib_vulkan.zig @@ -137,6 +137,7 @@ const device_pfn_map = block: { @setEvalBranchQuota(65535); break :block std.StaticStringMap(vk.PfnVoidFunction).initComptime(.{ functionMapEntryPoint("vkAcquireNextImageKHR"), + functionMapEntryPoint("vkAcquireNextImage2KHR"), functionMapEntryPoint("vkAllocateCommandBuffers"), functionMapEntryPoint("vkAllocateDescriptorSets"), functionMapEntryPoint("vkAllocateDescriptorSets"), @@ -160,6 +161,8 @@ const device_pfn_map = block: { functionMapEntryPoint("vkCmdCopyImageToBuffer"), functionMapEntryPoint("vkCmdCopyQueryPoolResults"), functionMapEntryPoint("vkCmdDispatch"), + functionMapEntryPoint("vkCmdDispatchBase"), + functionMapEntryPoint("vkCmdDispatchBaseKHR"), functionMapEntryPoint("vkCmdDispatchIndirect"), functionMapEntryPoint("vkCmdDraw"), functionMapEntryPoint("vkCmdDrawIndexed"), @@ -178,6 +181,8 @@ const device_pfn_map = block: { functionMapEntryPoint("vkCmdSetBlendConstants"), functionMapEntryPoint("vkCmdSetDepthBias"), functionMapEntryPoint("vkCmdSetDepthBounds"), + functionMapEntryPoint("vkCmdSetDeviceMask"), + functionMapEntryPoint("vkCmdSetDeviceMaskKHR"), functionMapEntryPoint("vkCmdSetEvent"), functionMapEntryPoint("vkCmdSetLineWidth"), functionMapEntryPoint("vkCmdSetScissor"), @@ -236,6 +241,10 @@ const device_pfn_map = block: { functionMapEntryPoint("vkFreeMemory"), functionMapEntryPoint("vkGetBufferMemoryRequirements"), functionMapEntryPoint("vkGetDeviceMemoryCommitment"), + functionMapEntryPoint("vkGetDeviceGroupPeerMemoryFeatures"), + functionMapEntryPoint("vkGetDeviceGroupPeerMemoryFeaturesKHR"), + functionMapEntryPoint("vkGetDeviceGroupPresentCapabilitiesKHR"), + functionMapEntryPoint("vkGetDeviceGroupSurfacePresentModesKHR"), functionMapEntryPoint("vkGetDeviceProcAddr"), functionMapEntryPoint("vkGetDeviceQueue"), functionMapEntryPoint("vkGetEventStatus"), @@ -402,16 +411,12 @@ pub export fn apeEnumeratePhysicalDevices(p_instance: vk.Instance, count: *u32, return .success; } -pub export fn apeEnumeratePhysicalDeviceGroups( - p_instance: vk.Instance, - count: *u32, - p_groups: ?[*]vk.PhysicalDeviceGroupProperties, -) callconv(vk.vulkan_call_conv) vk.Result { +pub export fn apeEnumeratePhysicalDeviceGroups(p_instance: vk.Instance, count: *u32, p_groups: ?[*]vk.PhysicalDeviceGroupProperties) callconv(vk.vulkan_call_conv) vk.Result { entryPointBeginLogTrace(.vkEnumeratePhysicalDeviceGroups); defer entryPointEndLogTrace(); const instance = Dispatchable(Instance).fromHandleObject(p_instance) catch |err| return toVkResult(err); - const available: u32 = 1; + const available: u32 = @intCast(instance.physical_devices.items.len); if (p_groups) |groups| { const write_count = @min(count.*, available); @@ -420,16 +425,15 @@ pub export fn apeEnumeratePhysicalDeviceGroups( return .incomplete; } - var group = groups[0]; - group.physical_device_count = @intCast(instance.physical_devices.items.len); - group.physical_devices = @splat(.null_handle); - for (instance.physical_devices.items, 0..) |physical_device, i| { - group.physical_devices[i] = physical_device.toVkHandle(vk.PhysicalDevice); + for (groups[0..write_count], instance.physical_devices.items[0..write_count]) |*group, physical_device| { + group.physical_device_count = 1; + group.physical_devices = @splat(.null_handle); + group.physical_devices[0] = physical_device.toVkHandle(vk.PhysicalDevice); + group.subset_allocation = .false; } - group.subset_allocation = .false; - groups[0] = group; count.* = write_count; + if (write_count < available) return .incomplete; return .success; } @@ -437,12 +441,8 @@ pub export fn apeEnumeratePhysicalDeviceGroups( return .success; } -pub export fn apeEnumeratePhysicalDeviceGroupsKHR( - p_instance: vk.Instance, - count: *u32, - p_groups: ?[*]vk.PhysicalDeviceGroupProperties, -) callconv(vk.vulkan_call_conv) vk.Result { - return apeEnumeratePhysicalDeviceGroups(p_instance, count, p_groups); +pub export fn apeEnumeratePhysicalDeviceGroupsKHR(p_instance: vk.Instance, count: *u32, p_groups: ?[*]vk.PhysicalDeviceGroupProperties) callconv(vk.vulkan_call_conv) vk.Result { + return @call(.always_inline, apeEnumeratePhysicalDeviceGroups, .{ p_instance, count, p_groups }); } // Physical Device functions ================================================================================================================================= @@ -1412,6 +1412,60 @@ pub export fn apeGetDeviceMemoryCommitment(p_device: vk.Device, p_memory: vk.Dev _ = committed_memory; } +pub export fn apeGetDeviceGroupPeerMemoryFeatures( + p_device: vk.Device, + heap_index: u32, + local_device_index: u32, + remote_device_index: u32, + p_peer_memory_features: *vk.PeerMemoryFeatureFlags, +) callconv(vk.vulkan_call_conv) void { + entryPointBeginLogTrace(.vkGetDeviceGroupPeerMemoryFeatures); + defer entryPointEndLogTrace(); + + const device = Dispatchable(Device).fromHandleObject(p_device) catch |err| return errorLogger(err); + p_peer_memory_features.* = device.getDeviceGroupPeerMemoryFeatures(heap_index, local_device_index, remote_device_index) catch |err| return errorLogger(err); +} + +pub export fn apeGetDeviceGroupPeerMemoryFeaturesKHR( + p_device: vk.Device, + heap_index: u32, + local_device_index: u32, + remote_device_index: u32, + p_peer_memory_features: *vk.PeerMemoryFeatureFlags, +) callconv(vk.vulkan_call_conv) void { + apeGetDeviceGroupPeerMemoryFeatures(p_device, heap_index, local_device_index, remote_device_index, p_peer_memory_features); +} + +pub export fn apeGetDeviceGroupPresentCapabilitiesKHR( + p_device: vk.Device, + p_device_group_present_capabilities: *vk.DeviceGroupPresentCapabilitiesKHR, +) callconv(vk.vulkan_call_conv) vk.Result { + entryPointBeginLogTrace(.vkGetDeviceGroupPresentCapabilitiesKHR); + defer entryPointEndLogTrace(); + + if (p_device_group_present_capabilities.s_type != .device_group_present_capabilities_khr) { + return .error_validation_failed; + } + + const device = Dispatchable(Device).fromHandleObject(p_device) catch |err| return toVkResult(err); + device.getDeviceGroupPresentCapabilitiesKHR(p_device_group_present_capabilities) catch |err| return toVkResult(err); + return .success; +} + +pub export fn apeGetDeviceGroupSurfacePresentModesKHR( + p_device: vk.Device, + p_surface: vk.SurfaceKHR, + p_modes: *vk.DeviceGroupPresentModeFlagsKHR, +) callconv(vk.vulkan_call_conv) vk.Result { + entryPointBeginLogTrace(.vkGetDeviceGroupSurfacePresentModesKHR); + defer entryPointEndLogTrace(); + + const device = Dispatchable(Device).fromHandleObject(p_device) catch |err| return toVkResult(err); + const surface = NonDispatchable(SurfaceKHR).fromHandleObject(p_surface) catch |err| return toVkResult(err); + p_modes.* = device.getDeviceGroupSurfacePresentModesKHR(surface) catch |err| return toVkResult(err); + return .success; +} + pub export fn apeGetDeviceProcAddr(p_device: vk.Device, p_name: ?[*:0]const u8) callconv(vk.vulkan_call_conv) vk.PfnVoidFunction { defer entryPointEndLogTrace(); @@ -1876,6 +1930,18 @@ pub export fn apeCmdDispatch(p_cmd: vk.CommandBuffer, group_count_x: u32, group_ cmd.dispatch(group_count_x, group_count_y, group_count_z) catch |err| return errorLogger(err); } +pub export fn apeCmdDispatchBase(p_cmd: vk.CommandBuffer, base_group_x: u32, base_group_y: u32, base_group_z: u32, group_count_x: u32, group_count_y: u32, group_count_z: u32) callconv(vk.vulkan_call_conv) void { + entryPointBeginLogTrace(.vkCmdDispatchBase); + defer entryPointEndLogTrace(); + + const cmd = Dispatchable(CommandBuffer).fromHandleObject(p_cmd) catch |err| return errorLogger(err); + cmd.dispatchBase(base_group_x, base_group_y, base_group_z, group_count_x, group_count_y, group_count_z) catch |err| return errorLogger(err); +} + +pub export fn apeCmdDispatchBaseKHR(p_cmd: vk.CommandBuffer, base_group_x: u32, base_group_y: u32, base_group_z: u32, group_count_x: u32, group_count_y: u32, group_count_z: u32) callconv(vk.vulkan_call_conv) void { + @call(.always_inline, apeCmdDispatchBase, .{ p_cmd, base_group_x, base_group_y, base_group_z, group_count_x, group_count_y, group_count_z }); +} + pub export fn apeCmdDispatchIndirect(p_cmd: vk.CommandBuffer, p_buffer: vk.Buffer, offset: vk.DeviceSize) callconv(vk.vulkan_call_conv) void { entryPointBeginLogTrace(.vkCmdDispatchIndirect); defer entryPointEndLogTrace(); @@ -2073,6 +2139,18 @@ pub export fn apeCmdSetDepthBounds(p_cmd: vk.CommandBuffer, min: f32, max: f32) _ = max; } +pub export fn apeCmdSetDeviceMask(p_cmd: vk.CommandBuffer, device_mask: u32) callconv(vk.vulkan_call_conv) void { + entryPointBeginLogTrace(.vkCmdSetDeviceMask); + defer entryPointEndLogTrace(); + + const cmd = Dispatchable(CommandBuffer).fromHandleObject(p_cmd) catch |err| return errorLogger(err); + cmd.setDeviceMask(device_mask) catch |err| return errorLogger(err); +} + +pub export fn apeCmdSetDeviceMaskKHR(p_cmd: vk.CommandBuffer, device_mask: u32) callconv(vk.vulkan_call_conv) void { + @call(.always_inline, apeCmdSetDeviceMask, .{ p_cmd, device_mask }); +} + pub export fn apeCmdSetEvent(p_cmd: vk.CommandBuffer, p_event: vk.Event, stage_mask: vk.PipelineStageFlags) callconv(vk.vulkan_call_conv) void { entryPointBeginLogTrace(.vkCmdSetEvent); defer entryPointEndLogTrace(); @@ -2221,6 +2299,27 @@ pub export fn apeAcquireNextImageKHR(p_device: vk.Device, p_swapchain: vk.Swapch return .success; } +pub export fn apeAcquireNextImage2KHR(p_device: vk.Device, p_acquire_info: *const vk.AcquireNextImageInfoKHR, image_index: *u32) callconv(vk.vulkan_call_conv) vk.Result { + entryPointBeginLogTrace(.vkAcquireNextImage2KHR); + defer entryPointEndLogTrace(); + + if (p_acquire_info.s_type != .acquire_next_image_info_khr) { + return .error_validation_failed; + } + + if (p_acquire_info.device_mask != 1) { + return .error_validation_failed; + } + + Dispatchable(Device).checkHandleValidity(p_device) catch |err| return toVkResult(err); + + const swapchain = NonDispatchable(SwapchainKHR).fromHandleObject(p_acquire_info.swapchain) catch |err| return toVkResult(err); + const semaphore = if (p_acquire_info.semaphore != .null_handle) NonDispatchable(BinarySemaphore).fromHandleObject(p_acquire_info.semaphore) catch |err| return toVkResult(err) else null; + const fence = if (p_acquire_info.fence != .null_handle) NonDispatchable(Fence).fromHandleObject(p_acquire_info.fence) catch |err| return toVkResult(err) else null; + swapchain.getNextImage(p_acquire_info.timeout, semaphore, fence, image_index) catch |err| return toVkResult(err); + return .success; +} + pub export fn apeCreateSwapchainKHR(p_device: vk.Device, info: *const vk.SwapchainCreateInfoKHR, callbacks: ?*const vk.AllocationCallbacks, p_swapchain: *vk.SwapchainKHR) callconv(vk.vulkan_call_conv) vk.Result { entryPointBeginLogTrace(.vkCreateSwapchainKHR); defer entryPointEndLogTrace();