From 0e65efd9d66ccda51a9df5f31833b85c11e352e4 Mon Sep 17 00:00:00 2001 From: ashpil Date: Sat, 19 Jun 2021 16:01:26 -0400 Subject: [PATCH] less verbose interface via `@Type` --- README.md | 12 +-- examples/graphics_context.zig | 145 +++++++++++++++++----------------- generator/vulkan/render.zig | 101 ++++++++++++++++++++--- 3 files changed, 168 insertions(+), 90 deletions(-) diff --git a/README.md b/README.md index 2c78367..52b3ffb 100644 --- a/README.md +++ b/README.md @@ -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`. * 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 const vk = @import("vulkan"); -const BaseDispatch = struct { - vkCreateInstance: vk.PfnCreateInstance, - usingnamespace vk.BaseWrapper(@This()); -}; +const BaseDispatch = vk.BaseWrapper([_]vk.BaseCommand { + .create_instance, +}); ``` The wrapper struct then provides wrapper functions for each function pointer in the dispatch struct: ```zig pub const BaseWrapper(comptime Self: type) type { + ... return struct { pub fn createInstance( 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 `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 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. diff --git a/examples/graphics_context.zig b/examples/graphics_context.zig index 389088d..29304b4 100644 --- a/examples/graphics_context.zig +++ b/examples/graphics_context.zig @@ -5,81 +5,78 @@ const Allocator = std.mem.Allocator; const required_device_extensions = [_][]const u8{vk.extension_info.khr_swapchain.name}; -const BaseDispatch = struct { - vkCreateInstance: vk.PfnCreateInstance, - usingnamespace vk.BaseWrapper(@This()); -}; +const BaseDispatch = vk.BaseWrapper([_]vk.BaseCommand{ + .create_instance, +}); -const InstanceDispatch = struct { - vkDestroyInstance: vk.PfnDestroyInstance, - vkCreateDevice: vk.PfnCreateDevice, - vkDestroySurfaceKHR: vk.PfnDestroySurfaceKHR, - vkEnumeratePhysicalDevices: vk.PfnEnumeratePhysicalDevices, - vkGetPhysicalDeviceProperties: vk.PfnGetPhysicalDeviceProperties, - vkEnumerateDeviceExtensionProperties: vk.PfnEnumerateDeviceExtensionProperties, - vkGetPhysicalDeviceSurfaceFormatsKHR: vk.PfnGetPhysicalDeviceSurfaceFormatsKHR, - vkGetPhysicalDeviceSurfacePresentModesKHR: vk.PfnGetPhysicalDeviceSurfacePresentModesKHR, - vkGetPhysicalDeviceSurfaceCapabilitiesKHR: vk.PfnGetPhysicalDeviceSurfaceCapabilitiesKHR, - vkGetPhysicalDeviceQueueFamilyProperties: vk.PfnGetPhysicalDeviceQueueFamilyProperties, - vkGetPhysicalDeviceSurfaceSupportKHR: vk.PfnGetPhysicalDeviceSurfaceSupportKHR, - vkGetPhysicalDeviceMemoryProperties: vk.PfnGetPhysicalDeviceMemoryProperties, - vkGetDeviceProcAddr: vk.PfnGetDeviceProcAddr, - usingnamespace vk.InstanceWrapper(@This()); -}; +const InstanceDispatch = vk.InstanceWrapper([_]vk.InstanceCommand{ + .destroy_instance, + .create_device, + .destroy_surface_khr, + .enumerate_physical_devices, + .get_physical_device_properties, + .enumerate_device_extension_properties, + .get_physical_device_surface_formats_khr, + .get_physical_device_surface_present_modes_khr, + .get_physical_device_surface_capabilities_khr, + .get_physical_device_queue_family_properties, + .get_physical_device_surface_support_khr, + .get_physical_device_memory_properties, + .get_device_proc_addr, +}); -const DeviceDispatch = struct { - vkDestroyDevice: vk.PfnDestroyDevice, - vkGetDeviceQueue: vk.PfnGetDeviceQueue, - vkCreateSemaphore: vk.PfnCreateSemaphore, - vkCreateFence: vk.PfnCreateFence, - vkCreateImageView: vk.PfnCreateImageView, - vkDestroyImageView: vk.PfnDestroyImageView, - vkDestroySemaphore: vk.PfnDestroySemaphore, - vkDestroyFence: vk.PfnDestroyFence, - vkGetSwapchainImagesKHR: vk.PfnGetSwapchainImagesKHR, - vkCreateSwapchainKHR: vk.PfnCreateSwapchainKHR, - vkDestroySwapchainKHR: vk.PfnDestroySwapchainKHR, - vkAcquireNextImageKHR: vk.PfnAcquireNextImageKHR, - vkDeviceWaitIdle: vk.PfnDeviceWaitIdle, - vkWaitForFences: vk.PfnWaitForFences, - vkResetFences: vk.PfnResetFences, - vkQueueSubmit: vk.PfnQueueSubmit, - vkQueuePresentKHR: vk.PfnQueuePresentKHR, - vkCreateCommandPool: vk.PfnCreateCommandPool, - vkDestroyCommandPool: vk.PfnDestroyCommandPool, - vkAllocateCommandBuffers: vk.PfnAllocateCommandBuffers, - vkFreeCommandBuffers: vk.PfnFreeCommandBuffers, - vkQueueWaitIdle: vk.PfnQueueWaitIdle, - vkCreateShaderModule: vk.PfnCreateShaderModule, - vkDestroyShaderModule: vk.PfnDestroyShaderModule, - vkCreatePipelineLayout: vk.PfnCreatePipelineLayout, - vkDestroyPipelineLayout: vk.PfnDestroyPipelineLayout, - vkCreateRenderPass: vk.PfnCreateRenderPass, - vkDestroyRenderPass: vk.PfnDestroyRenderPass, - vkCreateGraphicsPipelines: vk.PfnCreateGraphicsPipelines, - vkDestroyPipeline: vk.PfnDestroyPipeline, - vkCreateFramebuffer: vk.PfnCreateFramebuffer, - vkDestroyFramebuffer: vk.PfnDestroyFramebuffer, - vkBeginCommandBuffer: vk.PfnBeginCommandBuffer, - vkEndCommandBuffer: vk.PfnEndCommandBuffer, - vkAllocateMemory: vk.PfnAllocateMemory, - vkFreeMemory: vk.PfnFreeMemory, - vkCreateBuffer: vk.PfnCreateBuffer, - vkDestroyBuffer: vk.PfnDestroyBuffer, - vkGetBufferMemoryRequirements: vk.PfnGetBufferMemoryRequirements, - vkMapMemory: vk.PfnMapMemory, - vkUnmapMemory: vk.PfnUnmapMemory, - vkBindBufferMemory: vk.PfnBindBufferMemory, - vkCmdBeginRenderPass: vk.PfnCmdBeginRenderPass, - vkCmdEndRenderPass: vk.PfnCmdEndRenderPass, - vkCmdBindPipeline: vk.PfnCmdBindPipeline, - vkCmdDraw: vk.PfnCmdDraw, - vkCmdSetViewport: vk.PfnCmdSetViewport, - vkCmdSetScissor: vk.PfnCmdSetScissor, - vkCmdBindVertexBuffers: vk.PfnCmdBindVertexBuffers, - vkCmdCopyBuffer: vk.PfnCmdCopyBuffer, - usingnamespace vk.DeviceWrapper(@This()); -}; +const DeviceDispatch = vk.DeviceWrapper([_]vk.DeviceCommand{ + .destroy_device, + .get_device_queue, + .create_semaphore, + .create_fence, + .create_image_view, + .destroy_image_view, + .destroy_semaphore, + .destroy_fence, + .get_swapchain_images_khr, + .create_swapchain_khr, + .destroy_swapchain_khr, + .acquire_next_image_khr, + .device_wait_idle, + .wait_for_fences, + .reset_fences, + .queue_submit, + .queue_present_khr, + .create_command_pool, + .destroy_command_pool, + .allocate_command_buffers, + .free_command_buffers, + .queue_wait_idle, + .create_shader_module, + .destroy_shader_module, + .create_pipeline_layout, + .destroy_pipeline_layout, + .create_render_pass, + .destroy_render_pass, + .create_graphics_pipelines, + .destroy_pipeline, + .create_framebuffer, + .destroy_framebuffer, + .begin_command_buffer, + .end_command_buffer, + .allocate_memory, + .free_memory, + .create_buffer, + .destroy_buffer, + .get_buffer_memory_requirements, + .map_memory, + .unmap_memory, + .bind_buffer_memory, + .cmd_begin_render_pass, + .cmd_end_render_pass, + .cmd_bind_pipeline, + .cmd_draw, + .cmd_set_viewport, + .cmd_set_scissor, + .cmd_bind_vertex_buffers, + .cmd_copy_buffer, +}); pub const GraphicsContext = struct { vkb: BaseDispatch, @@ -130,7 +127,7 @@ pub const GraphicsContext = struct { self.pdev = candidate.pdev; self.props = candidate.props; 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); self.graphics_queue = Queue.init(self.vkd, self.dev, candidate.queues.graphics_family); diff --git a/generator/vulkan/render.zig b/generator/vulkan/render.zig index b6efd3b..a812f8f 100644 --- a/generator/vulkan/render.zig +++ b/generator/vulkan/render.zig @@ -14,6 +14,9 @@ const preamble = \\const std = @import("std"); \\const builtin = @import("builtin"); \\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) \\ .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) @@ -412,6 +415,7 @@ fn Renderer(comptime WriterType: type) type { fn render(self: *Self) !void { try self.renderCopyright(); try self.writer.writeAll(preamble); + try self.renderCommandEnums(); for (self.registry.api_constants) |api_constant| { try self.renderApiConstant(api_constant); @@ -426,6 +430,54 @@ fn Renderer(comptime WriterType: type) type { 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 { var it = mem.split(self.registry.copyright, "\n"); while (it.next()) |line| { @@ -910,17 +962,44 @@ fn Renderer(comptime WriterType: type) type { } fn renderWrappers(self: *Self) !void { - try self.renderWrappersOfDispatchType("BaseWrapper", .base); - try self.renderWrappersOfDispatchType("InstanceWrapper", .instance); - try self.renderWrappersOfDispatchType("DeviceWrapper", .device); + try self.renderWrappersOfDispatchType(.base); + try self.renderWrappersOfDispatchType(.instance); + 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( - \\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 {{ - \\ - , .{name}); + \\ dispatch: Dispatch, + \\ + \\ const Self = @This(); + , .{ name, name }); try self.renderWrapperLoader(dispatch_type); @@ -954,10 +1033,10 @@ fn Renderer(comptime WriterType: type) type { try self.writer.print( \\pub fn load({s}) !Self {{ \\ 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 cmd_ptr = loader({s}name) orelse return error.InvalidCommand; - \\ @field(self, field.name) = @ptrCast(field.field_type, cmd_ptr); + \\ const cmd_ptr = loader({s}name) orelse return error.InvalidLoader; + \\ @field(self.dispatch, field.name) = @ptrCast(field.field_type, cmd_ptr); \\ }} \\ 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 { - try self.writer.writeAll("self."); + try self.writer.writeAll("self.dispatch."); try self.writeIdentifier(name); try self.writer.writeAll("(");