less verbose interface via @Type

This commit is contained in:
ashpil
2021-06-19 16:01:26 -04:00
parent 79d901a3e0
commit e36930ad5d
3 changed files with 168 additions and 90 deletions

View File

@@ -67,17 +67,17 @@ For each function, a wrapper is generated into one of three structs:
* InstanceWrapper. This contains wrappers for functions which are otherwise loaded by `vkGetInstanceProcAddr`. * InstanceWrapper. This contains wrappers for functions which are otherwise loaded by `vkGetInstanceProcAddr`.
* DeviceWrapper. This contains wrappers for functions which are loaded by `vkGetDeviceProcAddr`. * DeviceWrapper. This contains wrappers for functions which are loaded by `vkGetDeviceProcAddr`.
Each wrapper struct is to be used as a mixin on a struct containing **just** function pointers as members: Each wrapper struct can be called with an array of the appropriate enums:
```zig ```zig
const vk = @import("vulkan"); const vk = @import("vulkan");
const BaseDispatch = struct { const BaseDispatch = vk.BaseWrapper([_]vk.BaseCommand {
vkCreateInstance: vk.PfnCreateInstance, .create_instance,
usingnamespace vk.BaseWrapper(@This()); });
};
``` ```
The wrapper struct then provides wrapper functions for each function pointer in the dispatch struct: The wrapper struct then provides wrapper functions for each function pointer in the dispatch struct:
```zig ```zig
pub const BaseWrapper(comptime Self: type) type { pub const BaseWrapper(comptime Self: type) type {
...
return struct { return struct {
pub fn createInstance( pub fn createInstance(
self: Self, self: Self,
@@ -129,6 +129,8 @@ Furthermore, each wrapper contains a function to load each function pointer memb
* For `InstanceWrapper`, this function has signature `fn load(instance: Instance, loader: PfnGetInstanceProcAddr) !Self`. * For `InstanceWrapper`, this function has signature `fn load(instance: Instance, loader: PfnGetInstanceProcAddr) !Self`.
* For `DeviceWrapper`, this function has signature `fn load(device: Device, loader: PfnGetDeviceProcAddr) !Self`. * For `DeviceWrapper`, this function has signature `fn load(device: Device, loader: PfnGetDeviceProcAddr) !Self`.
One can access the underlying unwrapped C functions by doing `wrapper.dispatch.vkFuncYouWant(..)`.
### Bitflags ### Bitflags
Packed structs of bools are used for bit flags in vulkan-zig, instead of both a `FlagBits` and `Flags` variant. Places where either of these variants are used are both replaced by this packed struct instead. This means that even in places where just one flag would normally be accepted, the packed struct is accepted. The programmer is responsible for only enabling a single bit. Packed structs of bools are used for bit flags in vulkan-zig, instead of both a `FlagBits` and `Flags` variant. Places where either of these variants are used are both replaced by this packed struct instead. This means that even in places where just one flag would normally be accepted, the packed struct is accepted. The programmer is responsible for only enabling a single bit.

View File

@@ -5,81 +5,78 @@ const Allocator = std.mem.Allocator;
const required_device_extensions = [_][]const u8{vk.extension_info.khr_swapchain.name}; const required_device_extensions = [_][]const u8{vk.extension_info.khr_swapchain.name};
const BaseDispatch = struct { const BaseDispatch = vk.BaseWrapper([_]vk.BaseCommand{
vkCreateInstance: vk.PfnCreateInstance, .create_instance,
usingnamespace vk.BaseWrapper(@This()); });
};
const InstanceDispatch = struct { const InstanceDispatch = vk.InstanceWrapper([_]vk.InstanceCommand{
vkDestroyInstance: vk.PfnDestroyInstance, .destroy_instance,
vkCreateDevice: vk.PfnCreateDevice, .create_device,
vkDestroySurfaceKHR: vk.PfnDestroySurfaceKHR, .destroy_surface_khr,
vkEnumeratePhysicalDevices: vk.PfnEnumeratePhysicalDevices, .enumerate_physical_devices,
vkGetPhysicalDeviceProperties: vk.PfnGetPhysicalDeviceProperties, .get_physical_device_properties,
vkEnumerateDeviceExtensionProperties: vk.PfnEnumerateDeviceExtensionProperties, .enumerate_device_extension_properties,
vkGetPhysicalDeviceSurfaceFormatsKHR: vk.PfnGetPhysicalDeviceSurfaceFormatsKHR, .get_physical_device_surface_formats_khr,
vkGetPhysicalDeviceSurfacePresentModesKHR: vk.PfnGetPhysicalDeviceSurfacePresentModesKHR, .get_physical_device_surface_present_modes_khr,
vkGetPhysicalDeviceSurfaceCapabilitiesKHR: vk.PfnGetPhysicalDeviceSurfaceCapabilitiesKHR, .get_physical_device_surface_capabilities_khr,
vkGetPhysicalDeviceQueueFamilyProperties: vk.PfnGetPhysicalDeviceQueueFamilyProperties, .get_physical_device_queue_family_properties,
vkGetPhysicalDeviceSurfaceSupportKHR: vk.PfnGetPhysicalDeviceSurfaceSupportKHR, .get_physical_device_surface_support_khr,
vkGetPhysicalDeviceMemoryProperties: vk.PfnGetPhysicalDeviceMemoryProperties, .get_physical_device_memory_properties,
vkGetDeviceProcAddr: vk.PfnGetDeviceProcAddr, .get_device_proc_addr,
usingnamespace vk.InstanceWrapper(@This()); });
};
const DeviceDispatch = struct { const DeviceDispatch = vk.DeviceWrapper([_]vk.DeviceCommand{
vkDestroyDevice: vk.PfnDestroyDevice, .destroy_device,
vkGetDeviceQueue: vk.PfnGetDeviceQueue, .get_device_queue,
vkCreateSemaphore: vk.PfnCreateSemaphore, .create_semaphore,
vkCreateFence: vk.PfnCreateFence, .create_fence,
vkCreateImageView: vk.PfnCreateImageView, .create_image_view,
vkDestroyImageView: vk.PfnDestroyImageView, .destroy_image_view,
vkDestroySemaphore: vk.PfnDestroySemaphore, .destroy_semaphore,
vkDestroyFence: vk.PfnDestroyFence, .destroy_fence,
vkGetSwapchainImagesKHR: vk.PfnGetSwapchainImagesKHR, .get_swapchain_images_khr,
vkCreateSwapchainKHR: vk.PfnCreateSwapchainKHR, .create_swapchain_khr,
vkDestroySwapchainKHR: vk.PfnDestroySwapchainKHR, .destroy_swapchain_khr,
vkAcquireNextImageKHR: vk.PfnAcquireNextImageKHR, .acquire_next_image_khr,
vkDeviceWaitIdle: vk.PfnDeviceWaitIdle, .device_wait_idle,
vkWaitForFences: vk.PfnWaitForFences, .wait_for_fences,
vkResetFences: vk.PfnResetFences, .reset_fences,
vkQueueSubmit: vk.PfnQueueSubmit, .queue_submit,
vkQueuePresentKHR: vk.PfnQueuePresentKHR, .queue_present_khr,
vkCreateCommandPool: vk.PfnCreateCommandPool, .create_command_pool,
vkDestroyCommandPool: vk.PfnDestroyCommandPool, .destroy_command_pool,
vkAllocateCommandBuffers: vk.PfnAllocateCommandBuffers, .allocate_command_buffers,
vkFreeCommandBuffers: vk.PfnFreeCommandBuffers, .free_command_buffers,
vkQueueWaitIdle: vk.PfnQueueWaitIdle, .queue_wait_idle,
vkCreateShaderModule: vk.PfnCreateShaderModule, .create_shader_module,
vkDestroyShaderModule: vk.PfnDestroyShaderModule, .destroy_shader_module,
vkCreatePipelineLayout: vk.PfnCreatePipelineLayout, .create_pipeline_layout,
vkDestroyPipelineLayout: vk.PfnDestroyPipelineLayout, .destroy_pipeline_layout,
vkCreateRenderPass: vk.PfnCreateRenderPass, .create_render_pass,
vkDestroyRenderPass: vk.PfnDestroyRenderPass, .destroy_render_pass,
vkCreateGraphicsPipelines: vk.PfnCreateGraphicsPipelines, .create_graphics_pipelines,
vkDestroyPipeline: vk.PfnDestroyPipeline, .destroy_pipeline,
vkCreateFramebuffer: vk.PfnCreateFramebuffer, .create_framebuffer,
vkDestroyFramebuffer: vk.PfnDestroyFramebuffer, .destroy_framebuffer,
vkBeginCommandBuffer: vk.PfnBeginCommandBuffer, .begin_command_buffer,
vkEndCommandBuffer: vk.PfnEndCommandBuffer, .end_command_buffer,
vkAllocateMemory: vk.PfnAllocateMemory, .allocate_memory,
vkFreeMemory: vk.PfnFreeMemory, .free_memory,
vkCreateBuffer: vk.PfnCreateBuffer, .create_buffer,
vkDestroyBuffer: vk.PfnDestroyBuffer, .destroy_buffer,
vkGetBufferMemoryRequirements: vk.PfnGetBufferMemoryRequirements, .get_buffer_memory_requirements,
vkMapMemory: vk.PfnMapMemory, .map_memory,
vkUnmapMemory: vk.PfnUnmapMemory, .unmap_memory,
vkBindBufferMemory: vk.PfnBindBufferMemory, .bind_buffer_memory,
vkCmdBeginRenderPass: vk.PfnCmdBeginRenderPass, .cmd_begin_render_pass,
vkCmdEndRenderPass: vk.PfnCmdEndRenderPass, .cmd_end_render_pass,
vkCmdBindPipeline: vk.PfnCmdBindPipeline, .cmd_bind_pipeline,
vkCmdDraw: vk.PfnCmdDraw, .cmd_draw,
vkCmdSetViewport: vk.PfnCmdSetViewport, .cmd_set_viewport,
vkCmdSetScissor: vk.PfnCmdSetScissor, .cmd_set_scissor,
vkCmdBindVertexBuffers: vk.PfnCmdBindVertexBuffers, .cmd_bind_vertex_buffers,
vkCmdCopyBuffer: vk.PfnCmdCopyBuffer, .cmd_copy_buffer,
usingnamespace vk.DeviceWrapper(@This()); });
};
pub const GraphicsContext = struct { pub const GraphicsContext = struct {
vkb: BaseDispatch, vkb: BaseDispatch,
@@ -130,7 +127,7 @@ pub const GraphicsContext = struct {
self.pdev = candidate.pdev; self.pdev = candidate.pdev;
self.props = candidate.props; self.props = candidate.props;
self.dev = try initializeCandidate(self.vki, candidate); self.dev = try initializeCandidate(self.vki, candidate);
self.vkd = try DeviceDispatch.load(self.dev, self.vki.vkGetDeviceProcAddr); self.vkd = try DeviceDispatch.load(self.dev, self.vki.dispatch.vkGetDeviceProcAddr);
errdefer self.vkd.destroyDevice(self.dev, null); errdefer self.vkd.destroyDevice(self.dev, null);
self.graphics_queue = Queue.init(self.vkd, self.dev, candidate.queues.graphics_family); self.graphics_queue = Queue.init(self.vkd, self.dev, candidate.queues.graphics_family);

View File

@@ -14,6 +14,9 @@ const preamble =
\\const std = @import("std"); \\const std = @import("std");
\\const builtin = @import("builtin"); \\const builtin = @import("builtin");
\\const root = @import("root"); \\const root = @import("root");
\\
\\const GlobalScope = @This();
\\
\\pub const vulkan_call_conv: std.builtin.CallingConvention = if (builtin.os.tag == .windows and builtin.cpu.arch == .i386) \\pub const vulkan_call_conv: std.builtin.CallingConvention = if (builtin.os.tag == .windows and builtin.cpu.arch == .i386)
\\ .Stdcall \\ .Stdcall
\\ else if (builtin.abi == .android and (builtin.cpu.arch.isARM() or builtin.cpu.arch.isThumb()) and std.Target.arm.featureSetHas(builtin.cpu.features, .has_v7) and builtin.cpu.arch.ptrBitWidth() == 32) \\ else if (builtin.abi == .android and (builtin.cpu.arch.isARM() or builtin.cpu.arch.isThumb()) and std.Target.arm.featureSetHas(builtin.cpu.features, .has_v7) and builtin.cpu.arch.ptrBitWidth() == 32)
@@ -412,6 +415,7 @@ fn Renderer(comptime WriterType: type) type {
fn render(self: *Self) !void { fn render(self: *Self) !void {
try self.renderCopyright(); try self.renderCopyright();
try self.writer.writeAll(preamble); try self.writer.writeAll(preamble);
try self.renderCommandEnums();
for (self.registry.api_constants) |api_constant| { for (self.registry.api_constants) |api_constant| {
try self.renderApiConstant(api_constant); try self.renderApiConstant(api_constant);
@@ -426,6 +430,54 @@ fn Renderer(comptime WriterType: type) type {
try self.renderWrappers(); try self.renderWrappers();
} }
fn renderCommandEnums(self: *Self) !void {
try self.renderCommandEnumOfDispatchType(.base);
try self.renderCommandEnumOfDispatchType(.instance);
try self.renderCommandEnumOfDispatchType(.device);
try self.writer.writeAll("\n");
}
fn renderCommandEnumOfDispatchType(self: *Self, dispatch_type: CommandDispatchType) !void {
const dispatch_type_name = switch (dispatch_type) {
.base => "Base",
.instance => "Instance",
.device => "Device",
};
try self.writer.print("\npub const {s}Command = enum {{\n", .{dispatch_type_name});
for (self.registry.decls) |decl| {
if (decl.decl_type == .command) {
const command = decl.decl_type.command;
if (classifyCommandDispatch(decl.name, command) == dispatch_type) {
try self.writeIdentifierWithCase(.snake, trimVkNamespace(decl.name));
try self.writer.writeAll(",\n");
}
}
}
try self.writer.writeAll("};\n\n");
try self.writer.print(
\\fn snakeToCamel{s}(cmd: {s}Command) [:0]const u8 {{
\\ return switch(cmd) {{
, .{dispatch_type_name, dispatch_type_name});
for (self.registry.decls) |decl| {
if (decl.decl_type == .command) {
const command = decl.decl_type.command;
if (classifyCommandDispatch(decl.name, command) == dispatch_type) {
const trimmed_name = trimVkNamespace(decl.name);
try self.writer.writeAll(".");
try self.writeIdentifierWithCase(.snake, trimmed_name);
try self.writer.print(" => \"{s}\",\n", .{ trimmed_name });
}
}
}
try self.writer.writeAll(
\\ };
\\}
);
}
fn renderCopyright(self: *Self) !void { fn renderCopyright(self: *Self) !void {
var it = mem.split(self.registry.copyright, "\n"); var it = mem.split(self.registry.copyright, "\n");
while (it.next()) |line| { while (it.next()) |line| {
@@ -910,17 +962,44 @@ fn Renderer(comptime WriterType: type) type {
} }
fn renderWrappers(self: *Self) !void { fn renderWrappers(self: *Self) !void {
try self.renderWrappersOfDispatchType("BaseWrapper", .base); try self.renderWrappersOfDispatchType(.base);
try self.renderWrappersOfDispatchType("InstanceWrapper", .instance); try self.renderWrappersOfDispatchType(.instance);
try self.renderWrappersOfDispatchType("DeviceWrapper", .device); try self.renderWrappersOfDispatchType(.device);
} }
fn renderWrappersOfDispatchType(self: *Self, name: []const u8, dispatch_type: CommandDispatchType) !void { fn renderWrappersOfDispatchType(self: *Self, dispatch_type: CommandDispatchType) !void {
const name = switch (dispatch_type) {
.base => "Base",
.instance => "Instance",
.device => "Device",
};
try self.writer.print( try self.writer.print(
\\pub fn {s}(comptime Self: type) type {{ \\pub fn {s}Wrapper(comptime cmds: anytype) type {{
\\ comptime var fields: [cmds.len]std.builtin.TypeInfo.StructField = undefined;
\\ inline for (cmds) |cmd, i| {{
\\ const cmd_camel_case = snakeToCamel{s}(cmd);
\\ const cmd_type_name = "Pfn" ++ cmd_camel_case;
\\ const cmd_type = @field(GlobalScope, cmd_type_name);
\\ fields[i] = .{{
\\ .name = "vk" ++ cmd_camel_case,
\\ .field_type = cmd_type,
\\ .default_value = @as(?cmd_type, null),
\\ .is_comptime = false,
\\ .alignment = @alignOf(*cmd_type),
\\ }};
\\ }}
\\ const Dispatch = @Type(.{{ .Struct = .{{
\\ .layout = .Auto,
\\ .fields = &fields,
\\ .decls = &[_]std.builtin.TypeInfo.Declaration{{}},
\\ .is_tuple = false,
\\ }}}});
\\ return struct {{ \\ return struct {{
\\ dispatch: Dispatch,
\\ \\
, .{name}); \\ const Self = @This();
, .{ name, name });
try self.renderWrapperLoader(dispatch_type); try self.renderWrapperLoader(dispatch_type);
@@ -954,10 +1033,10 @@ fn Renderer(comptime WriterType: type) type {
try self.writer.print( try self.writer.print(
\\pub fn load({s}) !Self {{ \\pub fn load({s}) !Self {{
\\ var self: Self = undefined; \\ var self: Self = undefined;
\\ inline for (std.meta.fields(Self)) |field| {{ \\ inline for (std.meta.fields(Dispatch)) |field| {{
\\ const name = @ptrCast([*:0]const u8, field.name ++ "\x00"); \\ const name = @ptrCast([*:0]const u8, field.name ++ "\x00");
\\ const cmd_ptr = loader({s}name) orelse return error.InvalidCommand; \\ const cmd_ptr = loader({s}name) orelse return error.InvalidLoader;
\\ @field(self, field.name) = @ptrCast(field.field_type, cmd_ptr); \\ @field(self.dispatch, field.name) = @ptrCast(field.field_type, cmd_ptr);
\\ }} \\ }}
\\ return self; \\ return self;
\\}} \\}}
@@ -1019,7 +1098,7 @@ fn Renderer(comptime WriterType: type) type {
} }
fn renderWrapperCall(self: *Self, name: []const u8, command: reg.Command, returns: []const ReturnValue) !void { fn renderWrapperCall(self: *Self, name: []const u8, command: reg.Command, returns: []const ReturnValue) !void {
try self.writer.writeAll("self."); try self.writer.writeAll("self.dispatch.");
try self.writeIdentifier(name); try self.writeIdentifier(name);
try self.writer.writeAll("("); try self.writer.writeAll("(");