adding fragment derivatives, base pipeline cache, missing KHR function to avoid CTS crash + lots of minor bugfixes
Build / build (push) Failing after 36s
Test / build_and_test (push) Successful in 35s

This commit is contained in:
2026-06-08 14:55:41 +02:00
parent 688e212bfd
commit a8d9f3b333
30 changed files with 625 additions and 144 deletions
+16 -12
View File
@@ -32,6 +32,7 @@ owner: *Device,
pool: *CommandPool,
state: State,
begin_info: ?vk.CommandBufferBeginInfo,
usage_flags: vk.CommandBufferUsageFlags,
host_allocator: VulkanAllocator,
state_mutex: std.Io.Mutex,
@@ -94,6 +95,7 @@ pub fn init(device: *Device, allocator: std.mem.Allocator, info: *const vk.Comma
.pool = try NonDispatchable(CommandPool).fromHandleObject(info.command_pool),
.state = .Initial,
.begin_info = null,
.usage_flags = .{},
.host_allocator = VulkanAllocator.from(allocator).cloneWithScope(.object),
.state_mutex = .init,
.vtable = undefined,
@@ -120,7 +122,7 @@ pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) void {
pub fn begin(self: *Self, info: *const vk.CommandBufferBeginInfo) VkError!void {
const implicitly_reset = self.state == .Executable;
self.transitionState(.Recording, &.{ .Initial, .Executable }) catch return VkError.ValidationFailed;
self.transitionState(.Recording, &.{ .Initial, .Executable, .Invalid }) catch return VkError.ValidationFailed;
if (implicitly_reset) {
try self.dispatch_table.reset(self, .{});
self.begin_info = null;
@@ -128,12 +130,12 @@ pub fn begin(self: *Self, info: *const vk.CommandBufferBeginInfo) VkError!void {
try self.dispatch_table.begin(self, info);
self.begin_info = info.*;
self.usage_flags = info.flags;
}
pub fn end(self: *Self) VkError!void {
self.transitionState(.Executable, &.{.Recording}) catch return VkError.ValidationFailed;
try self.dispatch_table.end(self);
self.begin_info = null;
}
pub fn reset(self: *Self, flags: vk.CommandBufferResetFlags) VkError!void {
@@ -141,26 +143,28 @@ pub fn reset(self: *Self, flags: vk.CommandBufferResetFlags) VkError!void {
return VkError.ValidationFailed;
}
try self.resetFromPool(flags);
}
pub fn resetFromPool(self: *Self, flags: vk.CommandBufferResetFlags) VkError!void {
self.transitionState(.Initial, &.{ .Initial, .Recording, .Executable, .Invalid }) catch return VkError.ValidationFailed;
try self.dispatch_table.reset(self, flags);
self.begin_info = null;
self.usage_flags = .{};
}
pub fn submit(self: *Self) VkError!void {
if (self.begin_info) |begin_info| {
if (!begin_info.flags.simultaneous_use_bit) {
self.transitionState(.Pending, &.{.Executable}) catch return VkError.ValidationFailed;
return;
}
if (!self.usage_flags.simultaneous_use_bit) {
self.transitionState(.Pending, &.{.Executable}) catch return VkError.ValidationFailed;
return;
}
self.transitionState(.Pending, &.{ .Pending, .Executable }) catch return VkError.ValidationFailed;
}
pub fn finish(self: *Self) VkError!void {
if (self.begin_info) |begin_info| {
if (!begin_info.flags.one_time_submit_bit) {
self.transitionState(.Invalid, &.{.Pending}) catch return VkError.ValidationFailed;
return;
}
if (self.usage_flags.one_time_submit_bit) {
self.transitionState(.Invalid, &.{.Pending}) catch return VkError.ValidationFailed;
return;
}
self.transitionState(.Executable, &.{.Pending}) catch return VkError.ValidationFailed;
}
+15 -10
View File
@@ -55,10 +55,23 @@ pub fn allocateCommandBuffers(self: *Self, info: *const vk.CommandBufferAllocate
while (self.buffers.capacity < self.buffers.items.len + info.command_buffer_count) {
self.buffers.ensureUnusedCapacity(allocator, BUFFER_POOL_BASE_CAPACITY) catch return VkError.OutOfHostMemory;
}
const original_len = self.buffers.items.len;
errdefer {
for (self.buffers.items[original_len..]) |dis_cmd| {
dis_cmd.intrusiveDestroy(allocator);
}
self.buffers.shrinkRetainingCapacity(original_len);
}
for (0..info.command_buffer_count) |_| {
const cmd = try self.vtable.createCommandBuffer(self, allocator, info);
var cmd_owned = true;
errdefer if (cmd_owned) cmd.destroy(allocator);
const dis_cmd = try Dispatchable(CommandBuffer).wrap(allocator, cmd);
cmd_owned = false;
var dis_cmd_owned = true;
errdefer if (dis_cmd_owned) dis_cmd.intrusiveDestroy(allocator);
self.buffers.appendAssumeCapacity(dis_cmd);
dis_cmd_owned = false;
}
}
@@ -97,15 +110,7 @@ pub fn reset(self: *Self, flags: vk.CommandPoolResetFlags) VkError!void {
self.first_free_buffer_index = 0;
if (flags.release_resources_bit) {
const allocator = self.host_allocator.allocator();
for (self.buffers.items) |dis_cmd| {
dis_cmd.intrusiveDestroy(allocator);
}
self.buffers.clearRetainingCapacity();
} else {
for (self.buffers.items) |dis_cmd| {
_ = dis_cmd.object.reset(.{ .release_resources_bit = true }) catch {};
}
for (self.buffers.items) |dis_cmd| {
_ = dis_cmd.object.resetFromPool(.{ .release_resources_bit = flags.release_resources_bit }) catch {};
}
}
+15 -1
View File
@@ -41,6 +41,7 @@ instance: *Instance,
physical_device: *const PhysicalDevice,
queues: std.AutoArrayHashMapUnmanaged(u32, std.ArrayList(*Dispatchable(Queue))),
host_allocator: VulkanAllocator,
khr_swapchain_enabled: bool,
dispatch_table: *const DispatchTable,
vtable: *const VTable,
@@ -75,17 +76,28 @@ pub const DispatchTable = struct {
};
pub fn init(allocator: std.mem.Allocator, instance: *Instance, physical_device: *const PhysicalDevice, info: *const vk.DeviceCreateInfo) VkError!Self {
_ = info;
return .{
.instance = instance,
.physical_device = physical_device,
.queues = .empty,
.host_allocator = VulkanAllocator.from(allocator).cloneWithScope(.object),
.khr_swapchain_enabled = isExtensionEnabled(info, vk.extensions.khr_swapchain.name),
.dispatch_table = undefined,
.vtable = undefined,
};
}
fn isExtensionEnabled(info: *const vk.DeviceCreateInfo, extension_name: []const u8) bool {
if (info.enabled_extension_count == 0) return false;
const names = info.pp_enabled_extension_names orelse return false;
for (0..info.enabled_extension_count) |i| {
if (std.mem.eql(u8, std.mem.span(names[i]), extension_name)) {
return true;
}
}
return false;
}
pub fn createQueues(self: *Self, allocator: std.mem.Allocator, info: *const vk.DeviceCreateInfo) VkError!void {
if (info.queue_create_info_count == 0) {
return;
@@ -102,8 +114,10 @@ pub fn createQueues(self: *Self, allocator: std.mem.Allocator, info: *const vk.D
}
const queue = try self.vtable.createQueue(allocator, self, queue_info.queue_family_index, @intCast(family_ptr.items.len), queue_info.flags);
errdefer self.vtable.destroyQueue(queue, allocator) catch {};
const dispatchable_queue = try Dispatchable(Queue).wrap(allocator, queue);
errdefer dispatchable_queue.destroy(allocator);
family_ptr.append(allocator, dispatchable_queue) catch return VkError.OutOfHostMemory;
}
}
+1 -1
View File
@@ -63,7 +63,7 @@ pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) void {
pub fn bindMemory(self: *Self, memory: *DeviceMemory, offset: vk.DeviceSize) VkError!void {
const image_size = try self.getTotalSize();
if (offset >= image_size or !self.allowed_memory_types.isSet(memory.memory_type_index)) {
if (offset + image_size > memory.size or !self.allowed_memory_types.isSet(memory.memory_type_index)) {
return VkError.ValidationFailed;
}
self.memory = memory;
+50 -4
View File
@@ -2,6 +2,7 @@ const std = @import("std");
const builtin = @import("builtin");
const vk = @import("vulkan");
const config = @import("lib.zig").config;
const utils = @import("utils.zig");
const VkError = @import("error_set.zig").VkError;
const Dispatchable = @import("Dispatchable.zig").Dispatchable;
@@ -50,6 +51,41 @@ pub fn init(allocator: std.mem.Allocator, infos: *const vk.InstanceCreateInfo) V
};
}
pub fn validateCreateInfo(info: *const vk.InstanceCreateInfo) VkError!void {
if (info.p_application_info) |application_info| {
const requested: vk.Version = @bitCast(application_info.api_version);
const supported: vk.Version = if (comptime builtin.is_test)
vk.API_VERSION_1_0
else
@bitCast(root.VULKAN_VERSION);
if (requested.variant != 0 or requested.major > supported.major or (requested.major == supported.major and requested.minor > supported.minor)) {
return VkError.IncompatibleDriver;
}
}
if (info.enabled_layer_count != 0) {
const names = info.pp_enabled_layer_names orelse return VkError.LayerNotPresent;
for (0..info.enabled_layer_count) |i| {
_ = utils.boundedName(names[i], vk.MAX_EXTENSION_NAME_SIZE) orelse return VkError.LayerNotPresent;
return VkError.LayerNotPresent;
}
}
if (info.enabled_extension_count != 0) {
const names = info.pp_enabled_extension_names orelse return VkError.ExtensionNotPresent;
const supported_extensions = if (comptime builtin.is_test)
&[_]vk.ExtensionProperties{}
else
root.Instance.EXTENSIONS[0..];
for (0..info.enabled_extension_count) |i| {
const name = utils.boundedName(names[i], vk.MAX_EXTENSION_NAME_SIZE) orelse return VkError.ExtensionNotPresent;
if (!utils.isSupportedExtension(name, supported_extensions)) {
return VkError.ExtensionNotPresent;
}
}
}
}
/// Dummy to avoid compile error in tests and doc generation
pub fn create(allocator: std.mem.Allocator, infos: *const vk.InstanceCreateInfo) VkError!*Self {
_ = allocator;
@@ -64,11 +100,16 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator) VkError!void {
pub fn enumerateLayerProperties(count: *u32, p_properties: ?[*]vk.LayerProperties) VkError!void {
if (comptime !builtin.is_test and @hasDecl(root.Instance, "LAYERS")) {
count.* = root.Instance.LAYERS.len;
const available = root.Instance.LAYERS.len;
if (p_properties) |properties| {
for (root.Instance.LAYERS, properties[0..]) |layer, *prop| {
const write_count = @min(count.*, available);
for (root.Instance.LAYERS[0..write_count], properties[0..write_count]) |layer, *prop| {
prop.* = layer;
}
count.* = @intCast(write_count);
if (write_count < available) return VkError.Incomplete;
} else {
count.* = @intCast(available);
}
} else {
count.* = 0;
@@ -81,11 +122,16 @@ pub fn enumerateExtensionProperties(layer_name: ?[]const u8, count: *u32, p_prop
}
if (comptime !builtin.is_test and @hasDecl(root.Instance, "EXTENSIONS")) {
count.* = root.Instance.EXTENSIONS.len;
const available = root.Instance.EXTENSIONS.len;
if (p_properties) |properties| {
for (root.Instance.EXTENSIONS, properties[0..]) |ext, *prop| {
const write_count = @min(count.*, available);
for (root.Instance.EXTENSIONS[0..write_count], properties[0..write_count]) |ext, *prop| {
prop.* = ext;
}
count.* = @intCast(write_count);
if (write_count < available) return VkError.Incomplete;
} else {
count.* = @intCast(available);
}
} else {
count.* = 0;
+38
View File
@@ -1,6 +1,7 @@
const std = @import("std");
const vk = @import("vulkan");
const root = @import("lib.zig");
const utils = @import("utils.zig");
const Instance = @import("Instance.zig");
const VkError = @import("error_set.zig").VkError;
@@ -64,6 +65,43 @@ pub inline fn createDevice(self: *Self, allocator: std.mem.Allocator, infos: *co
return try self.dispatch_table.createDevice(self, allocator, infos);
}
pub fn validateCreateInfo(self: *const Self, allocator: std.mem.Allocator, info: *const vk.DeviceCreateInfo) VkError!void {
if (info.enabled_layer_count != 0) {
const names = info.pp_enabled_layer_names orelse return VkError.LayerNotPresent;
for (0..info.enabled_layer_count) |i| {
_ = utils.boundedName(names[i], vk.MAX_EXTENSION_NAME_SIZE) orelse return VkError.LayerNotPresent;
return VkError.LayerNotPresent;
}
}
if (info.enabled_extension_count != 0) {
const names = info.pp_enabled_extension_names orelse return VkError.ExtensionNotPresent;
var available_count: u32 = 0;
try self.enumerateExtensionProperties(null, &available_count, null);
const supported_extensions = allocator.alloc(vk.ExtensionProperties, available_count) catch return VkError.OutOfHostMemory;
defer allocator.free(supported_extensions);
var written_count = available_count;
try self.enumerateExtensionProperties(null, &written_count, supported_extensions.ptr);
for (0..info.enabled_extension_count) |i| {
const name = utils.boundedName(names[i], vk.MAX_EXTENSION_NAME_SIZE) orelse return VkError.ExtensionNotPresent;
if (!utils.isSupportedExtension(name, supported_extensions[0..written_count])) {
return VkError.ExtensionNotPresent;
}
}
}
if (info.p_enabled_features) |requested_features| {
inline for (std.meta.fields(vk.PhysicalDeviceFeatures)) |field| {
if (@field(requested_features, field.name) == .true and @field(self.features, field.name) == .false) {
return VkError.FeatureNotPresent;
}
}
}
}
pub inline fn getFormatProperties(self: *Self, format: vk.Format) VkError!vk.FormatProperties {
return try self.dispatch_table.getFormatProperties(self, format);
}
+37 -10
View File
@@ -93,6 +93,21 @@ pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*Pipe
}
}
var binding_description: ?[]vk.VertexInputBindingDescription = null;
errdefer if (binding_description) |value| allocator.free(value);
var attribute_description: ?[]vk.VertexInputAttributeDescription = null;
errdefer if (attribute_description) |value| allocator.free(value);
var viewports: ?[]vk.Viewport = null;
errdefer if (viewports) |value| allocator.free(value);
var scissors: ?[]vk.Rect2D = null;
errdefer if (scissors) |value| allocator.free(value);
var color_attachments: ?[]vk.PipelineColorBlendAttachmentState = null;
errdefer if (color_attachments) |value| allocator.free(value);
return .{
.owner = device,
.vtable = undefined,
@@ -105,7 +120,8 @@ pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*Pipe
.binding_description = blk: {
if (info.p_vertex_input_state) |vertex_input_state| {
if (vertex_input_state.p_vertex_binding_descriptions) |vertex_binding_descriptions| {
break :blk allocator.dupe(vk.VertexInputBindingDescription, vertex_binding_descriptions[0..vertex_input_state.vertex_binding_description_count]) catch return VkError.OutOfHostMemory;
binding_description = allocator.dupe(vk.VertexInputBindingDescription, vertex_binding_descriptions[0..vertex_input_state.vertex_binding_description_count]) catch return VkError.OutOfHostMemory;
break :blk binding_description;
}
} else {
return VkError.ValidationFailed;
@@ -115,7 +131,8 @@ pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*Pipe
.attribute_description = blk: {
if (info.p_vertex_input_state) |vertex_input_state| {
if (vertex_input_state.p_vertex_attribute_descriptions) |vertex_attribute_descriptions| {
break :blk allocator.dupe(vk.VertexInputAttributeDescription, vertex_attribute_descriptions[0..vertex_input_state.vertex_attribute_description_count]) catch return VkError.OutOfHostMemory;
attribute_description = allocator.dupe(vk.VertexInputAttributeDescription, vertex_attribute_descriptions[0..vertex_input_state.vertex_attribute_description_count]) catch return VkError.OutOfHostMemory;
break :blk attribute_description;
}
} else {
return VkError.ValidationFailed;
@@ -128,16 +145,20 @@ pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*Pipe
.viewport_state = .{
.viewports = blk: {
if (info.p_viewport_state) |viewport_state| {
if (viewport_state.p_viewports) |viewports| {
break :blk allocator.dupe(vk.Viewport, viewports[0..viewport_state.viewport_count]) catch return VkError.OutOfHostMemory;
if (viewport_state.p_viewports) |p_viewports| {
const copy = allocator.dupe(vk.Viewport, p_viewports[0..viewport_state.viewport_count]) catch return VkError.OutOfHostMemory;
viewports = copy;
break :blk viewports;
}
}
break :blk null;
},
.scissor = blk: {
if (info.p_viewport_state) |viewport_state| {
if (viewport_state.p_scissors) |scissors| {
break :blk allocator.dupe(vk.Rect2D, scissors[0..viewport_state.scissor_count]) catch return VkError.OutOfHostMemory;
if (viewport_state.p_scissors) |p_scissors| {
const copy = allocator.dupe(vk.Rect2D, p_scissors[0..viewport_state.scissor_count]) catch return VkError.OutOfHostMemory;
scissors = copy;
break :blk scissors;
}
}
break :blk null;
@@ -152,10 +173,10 @@ pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*Pipe
.color_blend = blk: {
if (info.p_color_blend_state) |state| {
break :blk .{
.attachments = if (state.p_attachments) |attachments|
allocator.dupe(vk.PipelineColorBlendAttachmentState, attachments[0..state.attachment_count]) catch return VkError.OutOfHostMemory
else
null,
.attachments = if (state.p_attachments) |attachments| blk_attachments: {
color_attachments = allocator.dupe(vk.PipelineColorBlendAttachmentState, attachments[0..state.attachment_count]) catch return VkError.OutOfHostMemory;
break :blk_attachments color_attachments;
} else null,
.constants = state.blend_constants,
};
}
@@ -205,6 +226,12 @@ pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) void {
if (graphics.input_assembly.attribute_description) |attribute_description| {
allocator.free(attribute_description);
}
if (graphics.viewport_state.viewports) |viewports| {
allocator.free(viewports);
}
if (graphics.viewport_state.scissor) |scissor| {
allocator.free(scissor);
}
if (graphics.color_blend.attachments) |attachments| {
allocator.free(attachments);
}
+47 -1
View File
@@ -4,6 +4,7 @@ const vk = @import("vulkan");
const NonDispatchable = @import("NonDispatchable.zig");
const VkError = @import("error_set.zig").VkError;
const root = @import("lib.zig");
const Device = @import("Device.zig");
@@ -11,6 +12,7 @@ const Self = @This();
pub const ObjectType: vk.ObjectType = .pipeline_cache;
owner: *Device,
data_available: bool,
vtable: *const VTable,
@@ -18,11 +20,14 @@ pub const VTable = struct {
destroy: *const fn (*Self, std.mem.Allocator) void,
};
pub const Header = vk.PipelineCacheHeaderVersionOne;
pub fn init(device: *Device, allocator: std.mem.Allocator, info: *const vk.PipelineCacheCreateInfo) VkError!Self {
_ = allocator;
_ = info;
const has_initial_data = info.initial_data_size != 0 or info.p_initial_data != null;
return .{
.owner = device,
.data_available = !has_initial_data or info.initial_data_size >= dataSize(),
.vtable = undefined,
};
}
@@ -30,3 +35,44 @@ pub fn init(device: *Device, allocator: std.mem.Allocator, info: *const vk.Pipel
pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) void {
self.vtable.destroy(self, allocator);
}
pub fn merge(self: *Self) VkError!void {
_ = self;
}
pub fn getData(self: *Self, data: ?[]u8) vk.Result {
if (!self.data_available) {
return .success;
}
const cache_header = self.header();
const bytes = std.mem.asBytes(&cache_header);
if (data) |dst| {
if (dst.len < bytes.len) {
return .incomplete;
}
@memcpy(dst[0..bytes.len], bytes);
}
return .success;
}
pub inline fn dataSize() usize {
return @sizeOf(Header);
}
pub inline fn availableDataSize(self: *const Self) usize {
return if (self.data_available) dataSize() else 0;
}
fn header(self: *const Self) Header {
return .{
.header_size = @sizeOf(Header),
.header_version = .one,
.vendor_id = @intCast(root.VULKAN_VENDOR_ID),
.device_id = self.owner.physical_device.props.device_id,
.pipeline_cache_uuid = self.owner.physical_device.props.pipeline_cache_uuid,
};
}
+3 -1
View File
@@ -52,7 +52,9 @@ pub const ShaderModule = @import("ShaderModule.zig");
pub const SurfaceKHR = @import("wsi/SurfaceKHR.zig");
pub const SwapchainKHR = @import("wsi/SwapchainKHR.zig");
pub const VULKAN_VENDOR_ID = 0x10008;
// To be commented out when spec 1.4.354 is released
// pub const VULKAN_VENDOR_ID: i32 = @intFromEnum(vk.VendorId.ape);
pub const VULKAN_VENDOR_ID: i32 = 0x10008;
/// Default driver name
pub const DRIVER_NAME = "Unnamed Ape Driver";
+125 -43
View File
@@ -64,6 +64,22 @@ inline fn notImplementedWarning() void {
logger.fixme("function not yet implemented", .{});
}
fn isSwapchainDeviceFunction(name: []const u8) bool {
return std.mem.eql(u8, name, "vkAcquireNextImageKHR") or
std.mem.eql(u8, name, "vkCreateSwapchainKHR") or
std.mem.eql(u8, name, "vkDestroySwapchainKHR") or
std.mem.eql(u8, name, "vkGetSwapchainImagesKHR") or
std.mem.eql(u8, name, "vkQueuePresentKHR");
}
fn wrapNonDispatchable(comptime T: type, allocator: std.mem.Allocator, object: *T, comptime VkT: type) VkError!VkT {
const handle = NonDispatchable(T).wrap(allocator, object) catch |err| {
object.destroy(allocator);
return err;
};
return handle.toVkHandle(VkT);
}
fn functionMapEntryPoint(comptime name: []const u8) struct { []const u8, vk.PfnVoidFunction } {
// Mapping 'vkFnName' to 'apeFnName'
const ape_name = std.fmt.comptimePrint("ape{s}", .{name[2..]});
@@ -94,6 +110,8 @@ const instance_pfn_map = std.StaticStringMap(vk.PfnVoidFunction).initComptime(.{
functionMapEntryPoint("vkCreateWaylandSurfaceKHR"),
functionMapEntryPoint("vkDestroyInstance"),
functionMapEntryPoint("vkDestroySurfaceKHR"),
functionMapEntryPoint("vkEnumeratePhysicalDeviceGroups"),
functionMapEntryPoint("vkEnumeratePhysicalDeviceGroupsKHR"),
functionMapEntryPoint("vkEnumeratePhysicalDevices"),
functionMapEntryPoint("vkGetDeviceProcAddr"),
});
@@ -312,15 +330,23 @@ pub export fn apeCreateInstance(info: *const vk.InstanceCreateInfo, callbacks: ?
return .error_validation_failed;
}
const allocator = VulkanAllocator.init(callbacks, .instance).allocator();
Instance.validateCreateInfo(info) catch |err| return toVkResult(err);
var instance: *lib.Instance = undefined;
if (!builtin.is_test) {
// Will call impl instead of interface as root refs the impl module
instance = root.Instance.create(allocator, info) catch |err| return toVkResult(err);
}
instance.requestPhysicalDevices(allocator) catch |err| return toVkResult(err);
instance.requestPhysicalDevices(allocator) catch |err| {
if (!builtin.is_test) instance.deinit(allocator) catch {};
return toVkResult(err);
};
p_instance.* = (Dispatchable(Instance).wrap(allocator, instance) catch |err| return toVkResult(err)).toVkHandle(vk.Instance);
const dispatchable = Dispatchable(Instance).wrap(allocator, instance) catch |err| {
if (!builtin.is_test) instance.deinit(allocator) catch {};
return toVkResult(err);
};
p_instance.* = dispatchable.toVkHandle(vk.Instance);
return .success;
}
@@ -370,15 +396,63 @@ pub export fn apeEnumeratePhysicalDevices(p_instance: vk.Instance, count: *u32,
defer entryPointEndLogTrace();
const instance = Dispatchable(Instance).fromHandleObject(p_instance) catch |err| return toVkResult(err);
count.* = @intCast(instance.physical_devices.items.len);
const available = instance.physical_devices.items.len;
if (p_devices) |devices| {
for (0..count.*) |i| {
const write_count = @min(count.*, available);
for (0..write_count) |i| {
devices[i] = instance.physical_devices.items[i].toVkHandle(vk.PhysicalDevice);
}
count.* = @intCast(write_count);
if (write_count < available) return .incomplete;
} else {
count.* = @intCast(available);
}
return .success;
}
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;
if (p_groups) |groups| {
const write_count = @min(count.*, available);
if (write_count == 0) {
count.* = 0;
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);
}
group.subset_allocation = .false;
groups[0] = group;
count.* = write_count;
return .success;
}
count.* = available;
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);
}
// Physical Device functions =================================================================================================================================
pub export fn apeCreateDevice(p_physical_device: vk.PhysicalDevice, info: *const vk.DeviceCreateInfo, callbacks: ?*const vk.AllocationCallbacks, p_device: *vk.Device) callconv(vk.vulkan_call_conv) vk.Result {
@@ -391,11 +465,16 @@ pub export fn apeCreateDevice(p_physical_device: vk.PhysicalDevice, info: *const
const allocator = VulkanAllocator.init(callbacks, .device).allocator();
const physical_device = Dispatchable(PhysicalDevice).fromHandleObject(p_physical_device) catch |err| return toVkResult(err);
physical_device.validateCreateInfo(allocator, info) catch |err| return toVkResult(err);
std.log.scoped(.vkCreateDevice).debug("Using VkPhysicalDevice named {s}", .{physical_device.props.device_name});
const device = physical_device.createDevice(allocator, info) catch |err| return toVkResult(err);
p_device.* = (Dispatchable(Device).wrap(allocator, device) catch |err| return toVkResult(err)).toVkHandle(vk.Device);
const dispatchable = Dispatchable(Device).wrap(allocator, device) catch |err| {
device.destroy(allocator) catch {};
return toVkResult(err);
};
p_device.* = dispatchable.toVkHandle(vk.Device);
return .success;
}
@@ -679,7 +758,7 @@ pub export fn apeAllocateMemory(p_device: vk.Device, info: *const vk.MemoryAlloc
const device = Dispatchable(Device).fromHandleObject(p_device) catch |err| return toVkResult(err);
const device_memory = device.allocateMemory(allocator, info) catch |err| return toVkResult(err);
p_memory.* = (NonDispatchable(DeviceMemory).wrap(allocator, device_memory) catch |err| return toVkResult(err)).toVkHandle(vk.DeviceMemory);
p_memory.* = wrapNonDispatchable(DeviceMemory, allocator, device_memory, vk.DeviceMemory) catch |err| return toVkResult(err);
return .success;
}
@@ -723,7 +802,7 @@ pub export fn apeCreateBuffer(p_device: vk.Device, info: *const vk.BufferCreateI
const allocator = VulkanAllocator.init(callbacks, .object).allocator();
const device = Dispatchable(Device).fromHandleObject(p_device) catch |err| return toVkResult(err);
const buffer = device.createBuffer(allocator, info) catch |err| return toVkResult(err);
p_buffer.* = (NonDispatchable(Buffer).wrap(allocator, buffer) catch |err| return toVkResult(err)).toVkHandle(vk.Buffer);
p_buffer.* = wrapNonDispatchable(Buffer, allocator, buffer, vk.Buffer) catch |err| return toVkResult(err);
return .success;
}
@@ -737,7 +816,7 @@ pub export fn apeCreateBufferView(p_device: vk.Device, info: *const vk.BufferVie
const allocator = VulkanAllocator.init(callbacks, .object).allocator();
const device = Dispatchable(Device).fromHandleObject(p_device) catch |err| return toVkResult(err);
const view = device.createBufferView(allocator, info) catch |err| return toVkResult(err);
p_view.* = (NonDispatchable(BufferView).wrap(allocator, view) catch |err| return toVkResult(err)).toVkHandle(vk.BufferView);
p_view.* = wrapNonDispatchable(BufferView, allocator, view, vk.BufferView) catch |err| return toVkResult(err);
return .success;
}
@@ -752,7 +831,7 @@ pub export fn apeCreateCommandPool(p_device: vk.Device, info: *const vk.CommandP
const allocator = VulkanAllocator.init(callbacks, .object).allocator();
const device = Dispatchable(Device).fromHandleObject(p_device) catch |err| return toVkResult(err);
const pool = device.createCommandPool(allocator, info) catch |err| return toVkResult(err);
p_pool.* = (NonDispatchable(CommandPool).wrap(allocator, pool) catch |err| return toVkResult(err)).toVkHandle(vk.CommandPool);
p_pool.* = wrapNonDispatchable(CommandPool, allocator, pool, vk.CommandPool) catch |err| return toVkResult(err);
return .success;
}
@@ -808,7 +887,7 @@ pub export fn apeCreateDescriptorPool(p_device: vk.Device, info: *const vk.Descr
const allocator = VulkanAllocator.init(callbacks, .object).allocator();
const device = Dispatchable(Device).fromHandleObject(p_device) catch |err| return toVkResult(err);
const pool = device.createDescriptorPool(allocator, info) catch |err| return toVkResult(err);
p_pool.* = (NonDispatchable(DescriptorPool).wrap(allocator, pool) catch |err| return toVkResult(err)).toVkHandle(vk.DescriptorPool);
p_pool.* = wrapNonDispatchable(DescriptorPool, allocator, pool, vk.DescriptorPool) catch |err| return toVkResult(err);
return .success;
}
@@ -824,7 +903,7 @@ pub export fn apeCreateDescriptorSetLayout(p_device: vk.Device, info: *const vk.
const allocator = VulkanAllocator.init(callbacks, .device).allocator();
const device = Dispatchable(Device).fromHandleObject(p_device) catch |err| return toVkResult(err);
const layout = device.createDescriptorSetLayout(allocator, info) catch |err| return toVkResult(err);
p_layout.* = (NonDispatchable(DescriptorSetLayout).wrap(allocator, layout) catch |err| return toVkResult(err)).toVkHandle(vk.DescriptorSetLayout);
p_layout.* = wrapNonDispatchable(DescriptorSetLayout, allocator, layout, vk.DescriptorSetLayout) catch |err| return toVkResult(err);
return .success;
}
@@ -839,7 +918,7 @@ pub export fn apeCreateEvent(p_device: vk.Device, info: *const vk.EventCreateInf
const allocator = VulkanAllocator.init(callbacks, .object).allocator();
const device = Dispatchable(Device).fromHandleObject(p_device) catch |err| return toVkResult(err);
const event = device.createEvent(allocator, info) catch |err| return toVkResult(err);
p_event.* = (NonDispatchable(Event).wrap(allocator, event) catch |err| return toVkResult(err)).toVkHandle(vk.Event);
p_event.* = wrapNonDispatchable(Event, allocator, event, vk.Event) catch |err| return toVkResult(err);
return .success;
}
@@ -854,7 +933,7 @@ pub export fn apeCreateFence(p_device: vk.Device, info: *const vk.FenceCreateInf
const allocator = VulkanAllocator.init(callbacks, .object).allocator();
const device = Dispatchable(Device).fromHandleObject(p_device) catch |err| return toVkResult(err);
const fence = device.createFence(allocator, info) catch |err| return toVkResult(err);
p_fence.* = (NonDispatchable(Fence).wrap(allocator, fence) catch |err| return toVkResult(err)).toVkHandle(vk.Fence);
p_fence.* = wrapNonDispatchable(Fence, allocator, fence, vk.Fence) catch |err| return toVkResult(err);
return .success;
}
@@ -869,7 +948,7 @@ pub export fn apeCreateFramebuffer(p_device: vk.Device, info: *const vk.Framebuf
const allocator = VulkanAllocator.init(callbacks, .object).allocator();
const device = Dispatchable(Device).fromHandleObject(p_device) catch |err| return toVkResult(err);
const framebuffer = device.createFramebuffer(allocator, info) catch |err| return toVkResult(err);
p_framebuffer.* = (NonDispatchable(Framebuffer).wrap(allocator, framebuffer) catch |err| return toVkResult(err)).toVkHandle(vk.Framebuffer);
p_framebuffer.* = wrapNonDispatchable(Framebuffer, allocator, framebuffer, vk.Framebuffer) catch |err| return toVkResult(err);
return .success;
}
@@ -922,7 +1001,7 @@ pub export fn apeCreateImage(p_device: vk.Device, info: *const vk.ImageCreateInf
const allocator = VulkanAllocator.init(callbacks, .object).allocator();
const device = Dispatchable(Device).fromHandleObject(p_device) catch |err| return toVkResult(err);
const image = device.createImage(allocator, info) catch |err| return toVkResult(err);
p_image.* = (NonDispatchable(Image).wrap(allocator, image) catch |err| return toVkResult(err)).toVkHandle(vk.Image);
p_image.* = wrapNonDispatchable(Image, allocator, image, vk.Image) catch |err| return toVkResult(err);
return .success;
}
@@ -936,7 +1015,7 @@ pub export fn apeCreateImageView(p_device: vk.Device, info: *const vk.ImageViewC
const allocator = VulkanAllocator.init(callbacks, .object).allocator();
const device = Dispatchable(Device).fromHandleObject(p_device) catch |err| return toVkResult(err);
const image_view = device.createImageView(allocator, info) catch |err| return toVkResult(err);
p_image_view.* = (NonDispatchable(ImageView).wrap(allocator, image_view) catch |err| return toVkResult(err)).toVkHandle(vk.ImageView);
p_image_view.* = wrapNonDispatchable(ImageView, allocator, image_view, vk.ImageView) catch |err| return toVkResult(err);
return .success;
}
@@ -951,7 +1030,7 @@ pub export fn apeCreatePipelineCache(p_device: vk.Device, info: *const vk.Pipeli
const allocator = VulkanAllocator.init(callbacks, .object).allocator();
const device = Dispatchable(Device).fromHandleObject(p_device) catch |err| return toVkResult(err);
const cache = device.createPipelineCache(allocator, info) catch |err| return toVkResult(err);
p_cache.* = (NonDispatchable(PipelineCache).wrap(allocator, cache) catch |err| return toVkResult(err)).toVkHandle(vk.PipelineCache);
p_cache.* = wrapNonDispatchable(PipelineCache, allocator, cache, vk.PipelineCache) catch |err| return toVkResult(err);
return .success;
}
@@ -967,7 +1046,7 @@ pub export fn apeCreatePipelineLayout(p_device: vk.Device, info: *const vk.Pipel
const allocator = VulkanAllocator.init(callbacks, .device).allocator();
const device = Dispatchable(Device).fromHandleObject(p_device) catch |err| return toVkResult(err);
const layout = device.createPipelineLayout(allocator, info) catch |err| return toVkResult(err);
p_layout.* = (NonDispatchable(PipelineLayout).wrap(allocator, layout) catch |err| return toVkResult(err)).toVkHandle(vk.PipelineLayout);
p_layout.* = wrapNonDispatchable(PipelineLayout, allocator, layout, vk.PipelineLayout) catch |err| return toVkResult(err);
return .success;
}
@@ -982,7 +1061,7 @@ pub export fn apeCreateQueryPool(p_device: vk.Device, info: *const vk.QueryPoolC
const allocator = VulkanAllocator.init(callbacks, .object).allocator();
const device = Dispatchable(Device).fromHandleObject(p_device) catch |err| return toVkResult(err);
const pool = device.createQueryPool(allocator, info) catch |err| return toVkResult(err);
p_pool.* = (NonDispatchable(QueryPool).wrap(allocator, pool) catch |err| return toVkResult(err)).toVkHandle(vk.QueryPool);
p_pool.* = wrapNonDispatchable(QueryPool, allocator, pool, vk.QueryPool) catch |err| return toVkResult(err);
return .success;
}
@@ -997,7 +1076,7 @@ pub export fn apeCreateRenderPass(p_device: vk.Device, info: *const vk.RenderPas
const allocator = VulkanAllocator.init(callbacks, .object).allocator();
const device = Dispatchable(Device).fromHandleObject(p_device) catch |err| return toVkResult(err);
const pass = device.createRenderPass(allocator, info) catch |err| return toVkResult(err);
p_pass.* = (NonDispatchable(RenderPass).wrap(allocator, pass) catch |err| return toVkResult(err)).toVkHandle(vk.RenderPass);
p_pass.* = wrapNonDispatchable(RenderPass, allocator, pass, vk.RenderPass) catch |err| return toVkResult(err);
return .success;
}
@@ -1012,7 +1091,7 @@ pub export fn apeCreateSampler(p_device: vk.Device, info: *const vk.SamplerCreat
const allocator = VulkanAllocator.init(callbacks, .object).allocator();
const device = Dispatchable(Device).fromHandleObject(p_device) catch |err| return toVkResult(err);
const sampler = device.createSampler(allocator, info) catch |err| return toVkResult(err);
p_sampler.* = (NonDispatchable(Sampler).wrap(allocator, sampler) catch |err| return toVkResult(err)).toVkHandle(vk.Sampler);
p_sampler.* = wrapNonDispatchable(Sampler, allocator, sampler, vk.Sampler) catch |err| return toVkResult(err);
return .success;
}
@@ -1027,7 +1106,7 @@ pub export fn apeCreateSemaphore(p_device: vk.Device, info: *const vk.SemaphoreC
const allocator = VulkanAllocator.init(callbacks, .object).allocator();
const device = Dispatchable(Device).fromHandleObject(p_device) catch |err| return toVkResult(err);
const semaphore = device.createSemaphore(allocator, info) catch |err| return toVkResult(err);
p_semaphore.* = (NonDispatchable(BinarySemaphore).wrap(allocator, semaphore) catch |err| return toVkResult(err)).toVkHandle(vk.Semaphore);
p_semaphore.* = wrapNonDispatchable(BinarySemaphore, allocator, semaphore, vk.Semaphore) catch |err| return toVkResult(err);
return .success;
}
@@ -1042,7 +1121,7 @@ pub export fn apeCreateShaderModule(p_device: vk.Device, info: *const vk.ShaderM
const allocator = VulkanAllocator.init(callbacks, .object).allocator();
const device = Dispatchable(Device).fromHandleObject(p_device) catch |err| return toVkResult(err);
const module = device.createShaderModule(allocator, info) catch |err| return toVkResult(err);
p_module.* = (NonDispatchable(ShaderModule).wrap(allocator, module) catch |err| return toVkResult(err)).toVkHandle(vk.ShaderModule);
p_module.* = wrapNonDispatchable(ShaderModule, allocator, module, vk.ShaderModule) catch |err| return toVkResult(err);
return .success;
}
@@ -1348,6 +1427,8 @@ pub export fn apeGetDeviceProcAddr(p_device: vk.Device, p_name: ?[*:0]const u8)
const name = std.mem.span(p_name.?);
if (p_device == .null_handle) return null;
const device = Dispatchable(Device).fromHandleObject(p_device) catch return null;
if (isSwapchainDeviceFunction(name) and !device.khr_swapchain_enabled) return null;
if (device_pfn_map.get(name)) |pfn| return pfn;
return null;
@@ -1380,7 +1461,7 @@ pub export fn apeGetEventStatus(p_device: vk.Device, p_event: vk.Event) callconv
const event = NonDispatchable(Event).fromHandleObject(p_event) catch |err| return toVkResult(err);
event.getStatus() catch |err| return toVkResult(err);
return .success;
return .event_set;
}
pub export fn apeGetFenceStatus(p_device: vk.Device, p_fence: vk.Fence) callconv(vk.vulkan_call_conv) vk.Result {
@@ -1391,7 +1472,7 @@ pub export fn apeGetFenceStatus(p_device: vk.Device, p_fence: vk.Fence) callconv
const fence = NonDispatchable(Fence).fromHandleObject(p_fence) catch |err| return toVkResult(err);
fence.getStatus() catch |err| return toVkResult(err);
return .event_set;
return .success;
}
pub export fn apeGetImageMemoryRequirements(p_device: vk.Device, p_image: vk.Image, requirements: *vk.MemoryRequirements) callconv(vk.vulkan_call_conv) void {
@@ -1428,19 +1509,20 @@ pub export fn apeGetImageSubresourceLayout(p_device: vk.Device, p_image: vk.Imag
layout.* = image.getSubresourceLayout(subresource.*) catch |err| return errorLogger(err);
}
pub export fn apeGetPipelineCacheData(p_device: vk.Device, p_cache: vk.PipelineCache, size: *usize, data: *anyopaque) callconv(vk.vulkan_call_conv) vk.Result {
pub export fn apeGetPipelineCacheData(p_device: vk.Device, p_cache: vk.PipelineCache, size: *usize, data: ?*anyopaque) callconv(vk.vulkan_call_conv) vk.Result {
entryPointBeginLogTrace(.vkGetPipelineCacheData);
defer entryPointEndLogTrace();
Dispatchable(Device).checkHandleValidity(p_device) catch |err| return toVkResult(err);
const cache = NonDispatchable(PipelineCache).fromHandleObject(p_cache) catch |err| return toVkResult(err);
notImplementedWarning();
_ = p_cache;
_ = size;
_ = data;
return .success;
const available = cache.availableDataSize();
const result = if (data) |ptr| blk: {
const bytes = @as([*]u8, @ptrCast(ptr))[0..size.*];
break :blk cache.getData(bytes);
} else cache.getData(null);
size.* = if (result == .incomplete) 0 else available;
return result;
}
pub export fn apeGetQueryPoolResults(
@@ -1503,14 +1585,14 @@ pub export fn apeMergePipelineCaches(p_device: vk.Device, p_dst: vk.PipelineCach
defer entryPointEndLogTrace();
Dispatchable(Device).checkHandleValidity(p_device) catch |err| return toVkResult(err);
const dst = NonDispatchable(PipelineCache).fromHandleObject(p_dst) catch |err| return toVkResult(err);
notImplementedWarning();
for (0..count) |i| {
_ = NonDispatchable(PipelineCache).fromHandleObject(p_srcs[i]) catch |err| return toVkResult(err);
}
_ = p_dst;
_ = count;
_ = p_srcs;
return .error_unknown;
dst.merge() catch |err| return toVkResult(err);
return .success;
}
pub export fn apeResetCommandPool(p_device: vk.Device, p_pool: vk.CommandPool, flags: vk.CommandPoolResetFlags) callconv(vk.vulkan_call_conv) vk.Result {
@@ -1533,7 +1615,7 @@ pub export fn apeResetDescriptorPool(p_device: vk.Device, p_pool: vk.DescriptorP
return .success;
}
pub export fn apeResetEvent(p_device: vk.Device, p_event: vk.Fence) callconv(vk.vulkan_call_conv) vk.Result {
pub export fn apeResetEvent(p_device: vk.Device, p_event: vk.Event) callconv(vk.vulkan_call_conv) vk.Result {
entryPointBeginLogTrace(.vkResetEvent);
defer entryPointEndLogTrace();
@@ -1557,7 +1639,7 @@ pub export fn apeResetFences(p_device: vk.Device, count: u32, p_fences: [*]const
return .success;
}
pub export fn apeSetEvent(p_device: vk.Device, p_event: vk.Fence) callconv(vk.vulkan_call_conv) vk.Result {
pub export fn apeSetEvent(p_device: vk.Device, p_event: vk.Event) callconv(vk.vulkan_call_conv) vk.Result {
entryPointBeginLogTrace(.vkSetEvent);
defer entryPointEndLogTrace();
@@ -2168,7 +2250,7 @@ pub export fn apeCreateSwapchainKHR(p_device: vk.Device, info: *const vk.Swapcha
const device = Dispatchable(Device).fromHandleObject(p_device) catch |err| return toVkResult(err);
const surface = NonDispatchable(SurfaceKHR).fromHandleObject(info.surface) catch |err| return toVkResult(err);
const swapchain = SwapchainKHR.create(device, allocator, surface, info) catch |err| return toVkResult(err);
p_swapchain.* = (NonDispatchable(SwapchainKHR).wrap(allocator, swapchain) catch |err| return toVkResult(err)).toVkHandle(vk.SwapchainKHR);
p_swapchain.* = wrapNonDispatchable(SwapchainKHR, allocator, swapchain, vk.SwapchainKHR) catch |err| return toVkResult(err);
return .success;
}
@@ -2183,7 +2265,7 @@ pub export fn apeCreateWaylandSurfaceKHR(p_instance: vk.Instance, info: *const v
const allocator = VulkanAllocator.init(callbacks, .object).allocator();
const instance = Dispatchable(Instance).fromHandleObject(p_instance) catch |err| return toVkResult(err);
const surface = WaylandSurfaceKHR.create(instance, allocator, info) catch |err| return toVkResult(err);
p_surface.* = (NonDispatchable(SurfaceKHR).wrap(allocator, surface) catch |err| return toVkResult(err)).toVkHandle(vk.SurfaceKHR);
p_surface.* = wrapNonDispatchable(SurfaceKHR, allocator, surface, vk.SurfaceKHR) catch |err| return toVkResult(err);
return .success;
} else {
return .error_unknown;
+26
View File
@@ -1,3 +1,29 @@
const std = @import("std");
const vk = @import("vulkan");
pub fn boundedName(name: [*:0]const u8, max_len: usize) ?[]const u8 {
const bytes = name[0..max_len];
const len = std.mem.indexOfScalar(u8, bytes, 0) orelse return null;
return bytes[0..len];
}
pub fn propertyName(comptime max_len: usize, name: *const [max_len]u8) []const u8 {
return std.mem.sliceTo(name[0..], 0);
}
pub fn extensionName(name: *const [vk.MAX_EXTENSION_NAME_SIZE]u8) []const u8 {
return propertyName(vk.MAX_EXTENSION_NAME_SIZE, name);
}
pub fn isSupportedExtension(name: []const u8, extensions: []const vk.ExtensionProperties) bool {
for (extensions) |extension| {
if (std.mem.eql(u8, name, extensionName(&extension.extension_name))) {
return true;
}
}
return false;
}
pub fn writePacked(comptime T: type, bytes: []u8, value: T) void {
const raw: [@sizeOf(T)]u8 = @bitCast(value);
@memcpy(bytes[0..@sizeOf(T)], raw[0..]);
+2 -2
View File
@@ -68,8 +68,8 @@ pub fn getNextImage(self: *const Self, timeout: u64, semaphore: ?*BinarySemaphor
if (image.state == .Available) {
image.state = .Drawing;
index.* = @intCast(i);
// TODO: signal semaphore
_ = semaphore;
if (semaphore) |s|
try s.signal();
if (fence) |f|
try f.signal();
return;