forked from mirror/vulkan-zig
Compare commits
34 Commits
zig-0.7.1-
...
zig-0.8.0-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a2c379146 | ||
|
|
4429151d9c | ||
|
|
77651872ab | ||
|
|
5a51d18bda | ||
|
|
6feeeac109 | ||
|
|
8f10cec149 | ||
|
|
0e65efd9d6 | ||
|
|
b3c71d69ea | ||
|
|
b63533d95b | ||
|
|
419e541a16 | ||
|
|
4b4ef38c93 | ||
|
|
2af84b5212 | ||
|
|
beaa5bb0a5 | ||
|
|
e0101accda | ||
|
|
1ebdd7f063 | ||
|
|
0c404b3c32 | ||
|
|
5375c873d9 | ||
|
|
711bc08211 | ||
|
|
80d338e984 | ||
|
|
fc7c823293 | ||
|
|
4c96d30e11 | ||
|
|
1e7b5edb10 | ||
|
|
938359c6c9 | ||
|
|
bb21cf6892 | ||
|
|
5c5134269b | ||
|
|
9f23e2e16d | ||
|
|
e7d6f9f012 | ||
|
|
4ccb530585 | ||
|
|
d8b223bb3b | ||
|
|
a1f08ee687 | ||
|
|
fe85c8975e | ||
|
|
1c8e6b5a4b | ||
|
|
4a3700dbdd | ||
|
|
caaf915671 |
19
.github/workflows/build.yml
vendored
19
.github/workflows/build.yml
vendored
@@ -2,9 +2,9 @@ name: Build
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ master ]
|
branches: [ zig-0.8.0-compat ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ master ]
|
branches: [ zig-0.8.0-compat ]
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 6 * * *'
|
- cron: '0 6 * * *'
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ jobs:
|
|||||||
- name: Setup Zig
|
- name: Setup Zig
|
||||||
uses: goto-bus-stop/setup-zig@v1.3.0
|
uses: goto-bus-stop/setup-zig@v1.3.0
|
||||||
with:
|
with:
|
||||||
version: master
|
version: 0.8.0
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: |
|
run: |
|
||||||
@@ -27,11 +27,20 @@ jobs:
|
|||||||
- name: Fetch Vulkan SDK
|
- name: Fetch Vulkan SDK
|
||||||
run: |
|
run: |
|
||||||
wget -qO - https://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add -
|
wget -qO - https://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add -
|
||||||
sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-1.2.162-focal.list https://packages.lunarg.com/vulkan/1.2.162/lunarg-vulkan-1.2.162-focal.list
|
sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-1.2.176-focal.list https://packages.lunarg.com/vulkan/1.2.176/lunarg-vulkan-1.2.176-focal.list
|
||||||
sudo apt update
|
sudo apt update
|
||||||
sudo apt install shaderc libglfw3 libglfw3-dev
|
sudo apt install shaderc libglfw3 libglfw3-dev
|
||||||
|
|
||||||
- name: Build with latest zig & vk.xml
|
- name: Fetch latest vk.xml
|
||||||
run: |
|
run: |
|
||||||
wget https://raw.githubusercontent.com/KhronosGroup/Vulkan-Docs/main/xml/vk.xml
|
wget https://raw.githubusercontent.com/KhronosGroup/Vulkan-Docs/main/xml/vk.xml
|
||||||
|
|
||||||
|
- name: Build with latest zig & vk.xml
|
||||||
|
run: |
|
||||||
zig build -Dvulkan-registry=./vk.xml
|
zig build -Dvulkan-registry=./vk.xml
|
||||||
|
|
||||||
|
- name: Archive vk.xml
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: vk.zig
|
||||||
|
path: zig-cache/vk.zig
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
zig-cache/
|
zig-cache/
|
||||||
|
zig-out/
|
||||||
|
|||||||
44
README.md
44
README.md
@@ -8,7 +8,11 @@ A Vulkan binding generator for Zig.
|
|||||||
|
|
||||||
vulkan-zig attempts to provide a better experience to programming Vulkan applications in Zig, by providing features such as integration of vulkan errors with Zig's error system, function pointer loading, renaming fields to standard Zig style, better bitfield handling, turning out parameters into return values and more.
|
vulkan-zig attempts to provide a better experience to programming Vulkan applications in Zig, by providing features such as integration of vulkan errors with Zig's error system, function pointer loading, renaming fields to standard Zig style, better bitfield handling, turning out parameters into return values and more.
|
||||||
|
|
||||||
vulkan-zig is automatically tested against the latest vk.xml and zig, and supports vk.xml from version 1.x.163.
|
vulkan-zig is automatically tested daily against the latest vk.xml and zig, and supports vk.xml from version 1.x.163.
|
||||||
|
|
||||||
|
### Zig versions
|
||||||
|
|
||||||
|
vulkan-zig aims to be always compatible with the ever-changing Zig master branch (however, development may lag a few days behind). Sometimes, the Zig master branch breaks a bunch of functionality however, which may make the latest version vulkan-zig incompatible with older releases of Zig. This repository aims to have a version compatible for both the latest Zig master, and the latest Zig release. The `master` branch is compatible with the `master` branch of Zig, and versions for older versions of Zig are maintained in the `zig-<version>-compat` branch.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
### CLI-interface
|
### CLI-interface
|
||||||
@@ -63,23 +67,22 @@ 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(.{
|
||||||
vkCreateInstance: vk.PfnCreateInstance,
|
.CreateInstance,
|
||||||
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 cmds: anytype) type {
|
||||||
|
...
|
||||||
|
const Dispatch = CreateDispatchStruct(cmds);
|
||||||
return struct {
|
return struct {
|
||||||
pub fn createInstance(
|
dispatch: Dispatch,
|
||||||
self: Self,
|
|
||||||
create_info: InstanceCreateInfo,
|
pub const CreateInstanceError = error{
|
||||||
p_allocator: ?*const AllocationCallbacks,
|
|
||||||
) error{
|
|
||||||
OutOfHostMemory,
|
OutOfHostMemory,
|
||||||
OutOfDeviceMemory,
|
OutOfDeviceMemory,
|
||||||
InitializationFailed,
|
InitializationFailed,
|
||||||
@@ -87,9 +90,14 @@ pub const BaseWrapper(comptime Self: type) type {
|
|||||||
ExtensionNotPresent,
|
ExtensionNotPresent,
|
||||||
IncompatibleDriver,
|
IncompatibleDriver,
|
||||||
Unknown,
|
Unknown,
|
||||||
}!Instance {
|
};
|
||||||
|
pub fn createInstance(
|
||||||
|
self: Self,
|
||||||
|
create_info: InstanceCreateInfo,
|
||||||
|
p_allocator: ?*const AllocationCallbacks,
|
||||||
|
) CreateInstanceError!Instance {
|
||||||
var instance: Instance = undefined;
|
var instance: Instance = undefined;
|
||||||
const result = self.vkCreateInstance(
|
const result = self.dispatch.vkCreateInstance(
|
||||||
&create_info,
|
&create_info,
|
||||||
p_allocator,
|
p_allocator,
|
||||||
&instance,
|
&instance,
|
||||||
@@ -121,9 +129,11 @@ Wrappers are generated according to the following rules:
|
|||||||
* As of yet, there is no specific handling of enumeration style commands or other commands which accept slices.
|
* As of yet, there is no specific handling of enumeration style commands or other commands which accept slices.
|
||||||
|
|
||||||
Furthermore, each wrapper contains a function to load each function pointer member when passed either `PfnGetInstanceProcAddr` or `PfnGetDeviceProcAddr`, which attempts to load each member as function pointer and casts it to the appropriate type. These functions are loaded literally, and any wrongly named member or member with a wrong function pointer type will result in problems.
|
Furthermore, each wrapper contains a function to load each function pointer member when passed either `PfnGetInstanceProcAddr` or `PfnGetDeviceProcAddr`, which attempts to load each member as function pointer and casts it to the appropriate type. These functions are loaded literally, and any wrongly named member or member with a wrong function pointer type will result in problems.
|
||||||
* For `BaseWrapper`, this function has signature `fn load(loader: PfnGetInstanceProcAddr) !Self`.
|
* For `BaseWrapper`, this function has signature `fn load(loader: anytype) !Self`, where the type of `loader` must resemble `PfnGetInstanceProcAddr` (with optionally having a different calling convention).
|
||||||
* For `InstanceWrapper`, this function has signature `fn load(instance: Instance, loader: PfnGetInstanceProcAddr) !Self`.
|
* For `InstanceWrapper`, this function has signature `fn load(instance: Instance, loader: anytype) !Self`, where the type of `loader` must resemble `PfnGetInstanceProcAddr`.
|
||||||
* For `DeviceWrapper`, this function has signature `fn load(device: Device, loader: PfnGetDeviceProcAddr) !Self`.
|
* For `DeviceWrapper`, this function has signature `fn load(device: Device, loader: anytype) !Self`, where the type of `loader` must resemble `PfnGetDeviceProcAddr`.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|||||||
12
build.zig
12
build.zig
@@ -34,15 +34,15 @@ pub const ResourceGenStep = struct {
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn renderPath(self: *ResourceGenStep, path: []const u8, writer: anytype) void {
|
fn renderPath(path: []const u8, writer: anytype) void {
|
||||||
const separators = &[_]u8{ std.fs.path.sep_windows, std.fs.path.sep_posix };
|
const separators = &[_]u8{ std.fs.path.sep_windows, std.fs.path.sep_posix };
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
while (std.mem.indexOfAnyPos(u8, path, i, separators)) |j| {
|
while (std.mem.indexOfAnyPos(u8, path, i, separators)) |j| {
|
||||||
writer.writeAll(path[i .. j]) catch unreachable;
|
writer.writeAll(path[i..j]) catch unreachable;
|
||||||
switch (std.fs.path.sep) {
|
switch (std.fs.path.sep) {
|
||||||
std.fs.path.sep_windows => writer.writeAll("\\\\") catch unreachable,
|
std.fs.path.sep_windows => writer.writeAll("\\\\") catch unreachable,
|
||||||
std.fs.path.sep_posix => writer.writeByte(std.fs.path.sep_posix) catch unreachable,
|
std.fs.path.sep_posix => writer.writeByte(std.fs.path.sep_posix) catch unreachable,
|
||||||
else => unreachable
|
else => unreachable,
|
||||||
}
|
}
|
||||||
|
|
||||||
i = j + 1;
|
i = j + 1;
|
||||||
@@ -54,8 +54,8 @@ pub const ResourceGenStep = struct {
|
|||||||
const shader_out_path = self.shader_step.add(source);
|
const shader_out_path = self.shader_step.add(source);
|
||||||
var writer = self.resources.writer();
|
var writer = self.resources.writer();
|
||||||
|
|
||||||
writer.print("pub const {s} = @embedFile(\"", .{ name }) catch unreachable;
|
writer.print("pub const {s} = @embedFile(\"", .{name}) catch unreachable;
|
||||||
self.renderPath(shader_out_path, writer);
|
renderPath(shader_out_path, writer);
|
||||||
writer.writeAll("\");\n") catch unreachable;
|
writer.writeAll("\");\n") catch unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ pub fn build(b: *Builder) void {
|
|||||||
triangle_exe.setTarget(target);
|
triangle_exe.setTarget(target);
|
||||||
triangle_exe.setBuildMode(mode);
|
triangle_exe.setBuildMode(mode);
|
||||||
triangle_exe.install();
|
triangle_exe.install();
|
||||||
triangle_exe.linkSystemLibrary("c");
|
triangle_exe.linkLibC();
|
||||||
triangle_exe.linkSystemLibrary("glfw");
|
triangle_exe.linkSystemLibrary("glfw");
|
||||||
|
|
||||||
const vk_xml_path = b.option([]const u8, "vulkan-registry", "Override the to the Vulkan registry") orelse "examples/vk.xml";
|
const vk_xml_path = b.option([]const u8, "vulkan-registry", "Override the to the Vulkan registry") orelse "examples/vk.xml";
|
||||||
|
|||||||
@@ -3,85 +3,80 @@ const vk = @import("vulkan");
|
|||||||
const c = @import("c.zig");
|
const c = @import("c.zig");
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
const required_device_extensions = [_][]const u8{
|
const required_device_extensions = [_][]const u8{vk.extension_info.khr_swapchain.name};
|
||||||
vk.extension_info.khr_swapchain.name
|
|
||||||
};
|
|
||||||
|
|
||||||
const BaseDispatch = struct {
|
const BaseDispatch = vk.BaseWrapper(.{
|
||||||
vkCreateInstance: vk.PfnCreateInstance,
|
.CreateInstance,
|
||||||
usingnamespace vk.BaseWrapper(@This());
|
});
|
||||||
};
|
|
||||||
|
|
||||||
const InstanceDispatch = struct {
|
const InstanceDispatch = vk.InstanceWrapper(.{
|
||||||
vkDestroyInstance: vk.PfnDestroyInstance,
|
.DestroyInstance,
|
||||||
vkCreateDevice: vk.PfnCreateDevice,
|
.CreateDevice,
|
||||||
vkDestroySurfaceKHR: vk.PfnDestroySurfaceKHR,
|
.DestroySurfaceKHR,
|
||||||
vkEnumeratePhysicalDevices: vk.PfnEnumeratePhysicalDevices,
|
.EnumeratePhysicalDevices,
|
||||||
vkGetPhysicalDeviceProperties: vk.PfnGetPhysicalDeviceProperties,
|
.GetPhysicalDeviceProperties,
|
||||||
vkEnumerateDeviceExtensionProperties: vk.PfnEnumerateDeviceExtensionProperties,
|
.EnumerateDeviceExtensionProperties,
|
||||||
vkGetPhysicalDeviceSurfaceFormatsKHR: vk.PfnGetPhysicalDeviceSurfaceFormatsKHR,
|
.GetPhysicalDeviceSurfaceFormatsKHR,
|
||||||
vkGetPhysicalDeviceSurfacePresentModesKHR: vk.PfnGetPhysicalDeviceSurfacePresentModesKHR,
|
.GetPhysicalDeviceSurfacePresentModesKHR,
|
||||||
vkGetPhysicalDeviceSurfaceCapabilitiesKHR: vk.PfnGetPhysicalDeviceSurfaceCapabilitiesKHR,
|
.GetPhysicalDeviceSurfaceCapabilitiesKHR,
|
||||||
vkGetPhysicalDeviceQueueFamilyProperties: vk.PfnGetPhysicalDeviceQueueFamilyProperties,
|
.GetPhysicalDeviceQueueFamilyProperties,
|
||||||
vkGetPhysicalDeviceSurfaceSupportKHR: vk.PfnGetPhysicalDeviceSurfaceSupportKHR,
|
.GetPhysicalDeviceSurfaceSupportKHR,
|
||||||
vkGetPhysicalDeviceMemoryProperties: vk.PfnGetPhysicalDeviceMemoryProperties,
|
.GetPhysicalDeviceMemoryProperties,
|
||||||
vkGetDeviceProcAddr: vk.PfnGetDeviceProcAddr,
|
.GetDeviceProcAddr,
|
||||||
usingnamespace vk.InstanceWrapper(@This());
|
});
|
||||||
};
|
|
||||||
|
|
||||||
const DeviceDispatch = struct {
|
const DeviceDispatch = vk.DeviceWrapper(.{
|
||||||
vkDestroyDevice: vk.PfnDestroyDevice,
|
.DestroyDevice,
|
||||||
vkGetDeviceQueue: vk.PfnGetDeviceQueue,
|
.GetDeviceQueue,
|
||||||
vkCreateSemaphore: vk.PfnCreateSemaphore,
|
.CreateSemaphore,
|
||||||
vkCreateFence: vk.PfnCreateFence,
|
.CreateFence,
|
||||||
vkCreateImageView: vk.PfnCreateImageView,
|
.CreateImageView,
|
||||||
vkDestroyImageView: vk.PfnDestroyImageView,
|
.DestroyImageView,
|
||||||
vkDestroySemaphore: vk.PfnDestroySemaphore,
|
.DestroySemaphore,
|
||||||
vkDestroyFence: vk.PfnDestroyFence,
|
.DestroyFence,
|
||||||
vkGetSwapchainImagesKHR: vk.PfnGetSwapchainImagesKHR,
|
.GetSwapchainImagesKHR,
|
||||||
vkCreateSwapchainKHR: vk.PfnCreateSwapchainKHR,
|
.CreateSwapchainKHR,
|
||||||
vkDestroySwapchainKHR: vk.PfnDestroySwapchainKHR,
|
.DestroySwapchainKHR,
|
||||||
vkAcquireNextImageKHR: vk.PfnAcquireNextImageKHR,
|
.AcquireNextImageKHR,
|
||||||
vkDeviceWaitIdle: vk.PfnDeviceWaitIdle,
|
.DeviceWaitIdle,
|
||||||
vkWaitForFences: vk.PfnWaitForFences,
|
.WaitForFences,
|
||||||
vkResetFences: vk.PfnResetFences,
|
.ResetFences,
|
||||||
vkQueueSubmit: vk.PfnQueueSubmit,
|
.QueueSubmit,
|
||||||
vkQueuePresentKHR: vk.PfnQueuePresentKHR,
|
.QueuePresentKHR,
|
||||||
vkCreateCommandPool: vk.PfnCreateCommandPool,
|
.CreateCommandPool,
|
||||||
vkDestroyCommandPool: vk.PfnDestroyCommandPool,
|
.DestroyCommandPool,
|
||||||
vkAllocateCommandBuffers: vk.PfnAllocateCommandBuffers,
|
.AllocateCommandBuffers,
|
||||||
vkFreeCommandBuffers: vk.PfnFreeCommandBuffers,
|
.FreeCommandBuffers,
|
||||||
vkQueueWaitIdle: vk.PfnQueueWaitIdle,
|
.QueueWaitIdle,
|
||||||
vkCreateShaderModule: vk.PfnCreateShaderModule,
|
.CreateShaderModule,
|
||||||
vkDestroyShaderModule: vk.PfnDestroyShaderModule,
|
.DestroyShaderModule,
|
||||||
vkCreatePipelineLayout: vk.PfnCreatePipelineLayout,
|
.CreatePipelineLayout,
|
||||||
vkDestroyPipelineLayout: vk.PfnDestroyPipelineLayout,
|
.DestroyPipelineLayout,
|
||||||
vkCreateRenderPass: vk.PfnCreateRenderPass,
|
.CreateRenderPass,
|
||||||
vkDestroyRenderPass: vk.PfnDestroyRenderPass,
|
.DestroyRenderPass,
|
||||||
vkCreateGraphicsPipelines: vk.PfnCreateGraphicsPipelines,
|
.CreateGraphicsPipelines,
|
||||||
vkDestroyPipeline: vk.PfnDestroyPipeline,
|
.DestroyPipeline,
|
||||||
vkCreateFramebuffer: vk.PfnCreateFramebuffer,
|
.CreateFramebuffer,
|
||||||
vkDestroyFramebuffer: vk.PfnDestroyFramebuffer,
|
.DestroyFramebuffer,
|
||||||
vkBeginCommandBuffer: vk.PfnBeginCommandBuffer,
|
.BeginCommandBuffer,
|
||||||
vkEndCommandBuffer: vk.PfnEndCommandBuffer,
|
.EndCommandBuffer,
|
||||||
vkAllocateMemory: vk.PfnAllocateMemory,
|
.AllocateMemory,
|
||||||
vkFreeMemory: vk.PfnFreeMemory,
|
.FreeMemory,
|
||||||
vkCreateBuffer: vk.PfnCreateBuffer,
|
.CreateBuffer,
|
||||||
vkDestroyBuffer: vk.PfnDestroyBuffer,
|
.DestroyBuffer,
|
||||||
vkGetBufferMemoryRequirements: vk.PfnGetBufferMemoryRequirements,
|
.GetBufferMemoryRequirements,
|
||||||
vkMapMemory: vk.PfnMapMemory,
|
.MapMemory,
|
||||||
vkUnmapMemory: vk.PfnUnmapMemory,
|
.UnmapMemory,
|
||||||
vkBindBufferMemory: vk.PfnBindBufferMemory,
|
.BindBufferMemory,
|
||||||
vkCmdBeginRenderPass: vk.PfnCmdBeginRenderPass,
|
.CmdBeginRenderPass,
|
||||||
vkCmdEndRenderPass: vk.PfnCmdEndRenderPass,
|
.CmdEndRenderPass,
|
||||||
vkCmdBindPipeline: vk.PfnCmdBindPipeline,
|
.CmdBindPipeline,
|
||||||
vkCmdDraw: vk.PfnCmdDraw,
|
.CmdDraw,
|
||||||
vkCmdSetViewport: vk.PfnCmdSetViewport,
|
.CmdSetViewport,
|
||||||
vkCmdSetScissor: vk.PfnCmdSetScissor,
|
.CmdSetScissor,
|
||||||
vkCmdBindVertexBuffers: vk.PfnCmdBindVertexBuffers,
|
.CmdBindVertexBuffers,
|
||||||
vkCmdCopyBuffer: vk.PfnCmdCopyBuffer,
|
.CmdCopyBuffer,
|
||||||
usingnamespace vk.DeviceWrapper(@This());
|
});
|
||||||
};
|
|
||||||
|
|
||||||
pub const GraphicsContext = struct {
|
pub const GraphicsContext = struct {
|
||||||
vkb: BaseDispatch,
|
vkb: BaseDispatch,
|
||||||
@@ -107,9 +102,9 @@ pub const GraphicsContext = struct {
|
|||||||
|
|
||||||
const app_info = vk.ApplicationInfo{
|
const app_info = vk.ApplicationInfo{
|
||||||
.p_application_name = app_name,
|
.p_application_name = app_name,
|
||||||
.application_version = vk.makeVersion(0, 0, 0),
|
.application_version = vk.makeApiVersion(0, 0, 0, 0),
|
||||||
.p_engine_name = app_name,
|
.p_engine_name = app_name,
|
||||||
.engine_version = vk.makeVersion(0, 0, 0),
|
.engine_version = vk.makeApiVersion(0, 0, 0, 0),
|
||||||
.api_version = vk.API_VERSION_1_2,
|
.api_version = vk.API_VERSION_1_2,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -125,14 +120,14 @@ pub const GraphicsContext = struct {
|
|||||||
self.vki = try InstanceDispatch.load(self.instance, c.glfwGetInstanceProcAddress);
|
self.vki = try InstanceDispatch.load(self.instance, c.glfwGetInstanceProcAddress);
|
||||||
errdefer self.vki.destroyInstance(self.instance, null);
|
errdefer self.vki.destroyInstance(self.instance, null);
|
||||||
|
|
||||||
self.surface = try createSurface(self.vki, self.instance, window);
|
self.surface = try createSurface(self.instance, window);
|
||||||
errdefer self.vki.destroySurfaceKHR(self.instance, self.surface, null);
|
errdefer self.vki.destroySurfaceKHR(self.instance, self.surface, null);
|
||||||
|
|
||||||
const candidate = try pickPhysicalDevice(self.vki, self.instance, allocator, self.surface);
|
const candidate = try pickPhysicalDevice(self.vki, self.instance, allocator, self.surface);
|
||||||
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);
|
||||||
@@ -151,11 +146,11 @@ pub const GraphicsContext = struct {
|
|||||||
|
|
||||||
pub fn deviceName(self: GraphicsContext) []const u8 {
|
pub fn deviceName(self: GraphicsContext) []const u8 {
|
||||||
const len = std.mem.indexOfScalar(u8, &self.props.device_name, 0).?;
|
const len = std.mem.indexOfScalar(u8, &self.props.device_name, 0).?;
|
||||||
return self.props.device_name[0 .. len];
|
return self.props.device_name[0..len];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn findMemoryTypeIndex(self: GraphicsContext, memory_type_bits: u32, flags: vk.MemoryPropertyFlags) !u32 {
|
pub fn findMemoryTypeIndex(self: GraphicsContext, memory_type_bits: u32, flags: vk.MemoryPropertyFlags) !u32 {
|
||||||
for (self.mem_props.memory_types[0 .. self.mem_props.memory_type_count]) |mem_type, i| {
|
for (self.mem_props.memory_types[0..self.mem_props.memory_type_count]) |mem_type, i| {
|
||||||
if (memory_type_bits & (@as(u32, 1) << @truncate(u5, i)) != 0 and mem_type.property_flags.contains(flags)) {
|
if (memory_type_bits & (@as(u32, 1) << @truncate(u5, i)) != 0 and mem_type.property_flags.contains(flags)) {
|
||||||
return @truncate(u32, i);
|
return @truncate(u32, i);
|
||||||
}
|
}
|
||||||
@@ -184,7 +179,7 @@ pub const Queue = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fn createSurface(vki: InstanceDispatch, instance: vk.Instance, window: *c.GLFWwindow) !vk.SurfaceKHR {
|
fn createSurface(instance: vk.Instance, window: *c.GLFWwindow) !vk.SurfaceKHR {
|
||||||
var surface: vk.SurfaceKHR = undefined;
|
var surface: vk.SurfaceKHR = undefined;
|
||||||
if (c.glfwCreateWindowSurface(instance, window, null, &surface) != .success) {
|
if (c.glfwCreateWindowSurface(instance, window, null, &surface) != .success) {
|
||||||
return error.SurfaceInitFailed;
|
return error.SurfaceInitFailed;
|
||||||
@@ -207,7 +202,7 @@ fn initializeCandidate(vki: InstanceDispatch, candidate: DeviceCandidate) !vk.De
|
|||||||
.queue_family_index = candidate.queues.present_family,
|
.queue_family_index = candidate.queues.present_family,
|
||||||
.queue_count = 1,
|
.queue_count = 1,
|
||||||
.p_queue_priorities = &priority,
|
.p_queue_priorities = &priority,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const queue_count: u32 = if (candidate.queues.graphics_family == candidate.queues.present_family)
|
const queue_count: u32 = if (candidate.queues.graphics_family == candidate.queues.present_family)
|
||||||
@@ -281,19 +276,14 @@ fn checkSuitable(
|
|||||||
return DeviceCandidate{
|
return DeviceCandidate{
|
||||||
.pdev = pdev,
|
.pdev = pdev,
|
||||||
.props = props,
|
.props = props,
|
||||||
.queues = allocation
|
.queues = allocation,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn allocateQueues(
|
fn allocateQueues(vki: InstanceDispatch, pdev: vk.PhysicalDevice, allocator: *Allocator, surface: vk.SurfaceKHR) !?QueueAllocation {
|
||||||
vki: InstanceDispatch,
|
|
||||||
pdev: vk.PhysicalDevice,
|
|
||||||
allocator: *Allocator,
|
|
||||||
surface: vk.SurfaceKHR
|
|
||||||
) !?QueueAllocation {
|
|
||||||
var family_count: u32 = undefined;
|
var family_count: u32 = undefined;
|
||||||
vki.getPhysicalDeviceQueueFamilyProperties(pdev, &family_count, null);
|
vki.getPhysicalDeviceQueueFamilyProperties(pdev, &family_count, null);
|
||||||
|
|
||||||
@@ -307,7 +297,7 @@ fn allocateQueues(
|
|||||||
for (families) |properties, i| {
|
for (families) |properties, i| {
|
||||||
const family = @intCast(u32, i);
|
const family = @intCast(u32, i);
|
||||||
|
|
||||||
if (graphics_family == null and properties.queue_flags.contains(.{.graphics_bit = true})) {
|
if (graphics_family == null and properties.queue_flags.graphics_bit) {
|
||||||
graphics_family = family;
|
graphics_family = family;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,7 +309,7 @@ fn allocateQueues(
|
|||||||
if (graphics_family != null and present_family != null) {
|
if (graphics_family != null and present_family != null) {
|
||||||
return QueueAllocation{
|
return QueueAllocation{
|
||||||
.graphics_family = graphics_family.?,
|
.graphics_family = graphics_family.?,
|
||||||
.present_family = present_family.?
|
.present_family = present_family.?,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -352,7 +342,7 @@ fn checkExtensionSupport(
|
|||||||
for (required_device_extensions) |ext| {
|
for (required_device_extensions) |ext| {
|
||||||
for (propsv) |props| {
|
for (propsv) |props| {
|
||||||
const len = std.mem.indexOfScalar(u8, &props.extension_name, 0).?;
|
const len = std.mem.indexOfScalar(u8, &props.extension_name, 0).?;
|
||||||
const prop_ext_name = props.extension_name[0 .. len];
|
const prop_ext_name = props.extension_name[0..len];
|
||||||
if (std.mem.eql(u8, ext, prop_ext_name)) {
|
if (std.mem.eql(u8, ext, prop_ext_name)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ pub const Swapchain = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const concurrent = gc.graphics_queue.family != gc.present_queue.family;
|
const concurrent = gc.graphics_queue.family != gc.present_queue.family;
|
||||||
const qfi = [_]u32{gc.graphics_queue.family, gc.present_queue.family};
|
const qfi = [_]u32{ gc.graphics_queue.family, gc.present_queue.family };
|
||||||
|
|
||||||
const handle = try gc.vkd.createSwapchainKHR(gc.dev, .{
|
const handle = try gc.vkd.createSwapchainKHR(gc.dev, .{
|
||||||
.flags = .{},
|
.flags = .{},
|
||||||
@@ -51,12 +51,12 @@ pub const Swapchain = struct {
|
|||||||
.image_color_space = surface_format.color_space,
|
.image_color_space = surface_format.color_space,
|
||||||
.image_extent = actual_extent,
|
.image_extent = actual_extent,
|
||||||
.image_array_layers = 1,
|
.image_array_layers = 1,
|
||||||
.image_usage = .{.color_attachment_bit = true, .transfer_dst_bit = true},
|
.image_usage = .{ .color_attachment_bit = true, .transfer_dst_bit = true },
|
||||||
.image_sharing_mode = if (concurrent) .concurrent else .exclusive,
|
.image_sharing_mode = if (concurrent) .concurrent else .exclusive,
|
||||||
.queue_family_index_count = qfi.len,
|
.queue_family_index_count = qfi.len,
|
||||||
.p_queue_family_indices = &qfi,
|
.p_queue_family_indices = &qfi,
|
||||||
.pre_transform = caps.current_transform,
|
.pre_transform = caps.current_transform,
|
||||||
.composite_alpha = .{.opaque_bit_khr = true},
|
.composite_alpha = .{ .opaque_bit_khr = true },
|
||||||
.present_mode = present_mode,
|
.present_mode = present_mode,
|
||||||
.clipped = vk.TRUE,
|
.clipped = vk.TRUE,
|
||||||
.old_swapchain = old_handle,
|
.old_swapchain = old_handle,
|
||||||
@@ -71,7 +71,7 @@ pub const Swapchain = struct {
|
|||||||
const swap_images = try initSwapchainImages(gc, handle, surface_format.format, allocator);
|
const swap_images = try initSwapchainImages(gc, handle, surface_format.format, allocator);
|
||||||
errdefer for (swap_images) |si| si.deinit(gc);
|
errdefer for (swap_images) |si| si.deinit(gc);
|
||||||
|
|
||||||
var next_image_acquired = try gc.vkd.createSemaphore(gc.dev, .{.flags = .{}}, null);
|
var next_image_acquired = try gc.vkd.createSemaphore(gc.dev, .{ .flags = .{} }, null);
|
||||||
errdefer gc.vkd.destroySemaphore(gc.dev, next_image_acquired, null);
|
errdefer gc.vkd.destroySemaphore(gc.dev, next_image_acquired, null);
|
||||||
|
|
||||||
const result = try gc.vkd.acquireNextImageKHR(gc.dev, handle, std.math.maxInt(u64), next_image_acquired, .null_handle);
|
const result = try gc.vkd.acquireNextImageKHR(gc.dev, handle, std.math.maxInt(u64), next_image_acquired, .null_handle);
|
||||||
@@ -147,7 +147,7 @@ pub const Swapchain = struct {
|
|||||||
try self.gc.vkd.resetFences(self.gc.dev, 1, @ptrCast([*]const vk.Fence, ¤t.frame_fence));
|
try self.gc.vkd.resetFences(self.gc.dev, 1, @ptrCast([*]const vk.Fence, ¤t.frame_fence));
|
||||||
|
|
||||||
// Step 2: Submit the command buffer
|
// Step 2: Submit the command buffer
|
||||||
const wait_stage = [_]vk.PipelineStageFlags{.{.top_of_pipe_bit = true}};
|
const wait_stage = [_]vk.PipelineStageFlags{.{ .top_of_pipe_bit = true }};
|
||||||
try self.gc.vkd.queueSubmit(self.gc.graphics_queue.handle, 1, &[_]vk.SubmitInfo{.{
|
try self.gc.vkd.queueSubmit(self.gc.graphics_queue.handle, 1, &[_]vk.SubmitInfo{.{
|
||||||
.wait_semaphore_count = 1,
|
.wait_semaphore_count = 1,
|
||||||
.p_wait_semaphores = @ptrCast([*]const vk.Semaphore, ¤t.image_acquired),
|
.p_wait_semaphores = @ptrCast([*]const vk.Semaphore, ¤t.image_acquired),
|
||||||
@@ -201,9 +201,9 @@ const SwapImage = struct {
|
|||||||
.image = image,
|
.image = image,
|
||||||
.view_type = .@"2d",
|
.view_type = .@"2d",
|
||||||
.format = format,
|
.format = format,
|
||||||
.components = .{.r = .identity, .g = .identity, .b = .identity, .a = .identity},
|
.components = .{ .r = .identity, .g = .identity, .b = .identity, .a = .identity },
|
||||||
.subresource_range = .{
|
.subresource_range = .{
|
||||||
.aspect_mask = .{.color_bit = true},
|
.aspect_mask = .{ .color_bit = true },
|
||||||
.base_mip_level = 0,
|
.base_mip_level = 0,
|
||||||
.level_count = 1,
|
.level_count = 1,
|
||||||
.base_array_layer = 0,
|
.base_array_layer = 0,
|
||||||
@@ -212,13 +212,13 @@ const SwapImage = struct {
|
|||||||
}, null);
|
}, null);
|
||||||
errdefer gc.vkd.destroyImageView(gc.dev, view, null);
|
errdefer gc.vkd.destroyImageView(gc.dev, view, null);
|
||||||
|
|
||||||
const image_acquired = try gc.vkd.createSemaphore(gc.dev, .{.flags = .{}}, null);
|
const image_acquired = try gc.vkd.createSemaphore(gc.dev, .{ .flags = .{} }, null);
|
||||||
errdefer gc.vkd.destroySemaphore(gc.dev, image_acquired, null);
|
errdefer gc.vkd.destroySemaphore(gc.dev, image_acquired, null);
|
||||||
|
|
||||||
const render_finished = try gc.vkd.createSemaphore(gc.dev, .{.flags = .{}}, null);
|
const render_finished = try gc.vkd.createSemaphore(gc.dev, .{ .flags = .{} }, null);
|
||||||
errdefer gc.vkd.destroySemaphore(gc.dev, image_acquired, null);
|
errdefer gc.vkd.destroySemaphore(gc.dev, image_acquired, null);
|
||||||
|
|
||||||
const frame_fence = try gc.vkd.createFence(gc.dev, .{.flags = .{.signaled_bit = true}}, null);
|
const frame_fence = try gc.vkd.createFence(gc.dev, .{ .flags = .{ .signaled_bit = true } }, null);
|
||||||
errdefer gc.vkd.destroyFence(gc.dev, frame_fence, null);
|
errdefer gc.vkd.destroyFence(gc.dev, frame_fence, null);
|
||||||
|
|
||||||
return SwapImage{
|
return SwapImage{
|
||||||
@@ -254,7 +254,7 @@ fn initSwapchainImages(gc: *const GraphicsContext, swapchain: vk.SwapchainKHR, f
|
|||||||
errdefer allocator.free(images);
|
errdefer allocator.free(images);
|
||||||
|
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
errdefer for (swap_images[0 .. i]) |si| si.deinit(gc);
|
errdefer for (swap_images[0..i]) |si| si.deinit(gc);
|
||||||
|
|
||||||
for (images) |image| {
|
for (images) |image| {
|
||||||
swap_images[i] = try SwapImage.init(gc, image, format);
|
swap_images[i] = try SwapImage.init(gc, image, format);
|
||||||
|
|||||||
@@ -35,16 +35,16 @@ const Vertex = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const vertices = [_]Vertex{
|
const vertices = [_]Vertex{
|
||||||
.{.pos = .{0, -0.5}, .color = .{1, 0, 0}},
|
.{ .pos = .{ 0, -0.5 }, .color = .{ 1, 0, 0 } },
|
||||||
.{.pos = .{0.5, 0.5}, .color = .{0, 1, 0}},
|
.{ .pos = .{ 0.5, 0.5 }, .color = .{ 0, 1, 0 } },
|
||||||
.{.pos = .{-0.5, 0.5}, .color = .{0, 0, 1}},
|
.{ .pos = .{ -0.5, 0.5 }, .color = .{ 0, 0, 1 } },
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
if (c.glfwInit() != c.GLFW_TRUE) return error.GlfwInitFailed;
|
if (c.glfwInit() != c.GLFW_TRUE) return error.GlfwInitFailed;
|
||||||
defer c.glfwTerminate();
|
defer c.glfwTerminate();
|
||||||
|
|
||||||
var extent = vk.Extent2D{.width = 800, .height = 600};
|
var extent = vk.Extent2D{ .width = 800, .height = 600 };
|
||||||
|
|
||||||
c.glfwWindowHint(c.GLFW_CLIENT_API, c.GLFW_NO_API);
|
c.glfwWindowHint(c.GLFW_CLIENT_API, c.GLFW_NO_API);
|
||||||
const window = c.glfwCreateWindow(
|
const window = c.glfwCreateWindow(
|
||||||
@@ -52,7 +52,7 @@ pub fn main() !void {
|
|||||||
@intCast(c_int, extent.height),
|
@intCast(c_int, extent.height),
|
||||||
app_name,
|
app_name,
|
||||||
null,
|
null,
|
||||||
null
|
null,
|
||||||
) orelse return error.WindowInitFailed;
|
) orelse return error.WindowInitFailed;
|
||||||
defer c.glfwDestroyWindow(window);
|
defer c.glfwDestroyWindow(window);
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ pub fn main() !void {
|
|||||||
const gc = try GraphicsContext.init(allocator, app_name, window);
|
const gc = try GraphicsContext.init(allocator, app_name, window);
|
||||||
defer gc.deinit();
|
defer gc.deinit();
|
||||||
|
|
||||||
std.debug.print("Using device: {s}\n", .{ gc.deviceName() });
|
std.debug.print("Using device: {s}\n", .{gc.deviceName()});
|
||||||
|
|
||||||
var swapchain = try Swapchain.init(&gc, allocator, extent);
|
var swapchain = try Swapchain.init(&gc, allocator, extent);
|
||||||
defer swapchain.deinit();
|
defer swapchain.deinit();
|
||||||
@@ -78,7 +78,7 @@ pub fn main() !void {
|
|||||||
const render_pass = try createRenderPass(&gc, swapchain);
|
const render_pass = try createRenderPass(&gc, swapchain);
|
||||||
defer gc.vkd.destroyRenderPass(gc.dev, render_pass, null);
|
defer gc.vkd.destroyRenderPass(gc.dev, render_pass, null);
|
||||||
|
|
||||||
var pipeline = try createPipeline(&gc, extent, pipeline_layout, render_pass);
|
var pipeline = try createPipeline(&gc, pipeline_layout, render_pass);
|
||||||
defer gc.vkd.destroyPipeline(gc.dev, pipeline, null);
|
defer gc.vkd.destroyPipeline(gc.dev, pipeline, null);
|
||||||
|
|
||||||
var framebuffers = try createFramebuffers(&gc, allocator, render_pass, swapchain);
|
var framebuffers = try createFramebuffers(&gc, allocator, render_pass, swapchain);
|
||||||
@@ -93,18 +93,18 @@ pub fn main() !void {
|
|||||||
const buffer = try gc.vkd.createBuffer(gc.dev, .{
|
const buffer = try gc.vkd.createBuffer(gc.dev, .{
|
||||||
.flags = .{},
|
.flags = .{},
|
||||||
.size = @sizeOf(@TypeOf(vertices)),
|
.size = @sizeOf(@TypeOf(vertices)),
|
||||||
.usage = .{.transfer_dst_bit = true, .vertex_buffer_bit = true},
|
.usage = .{ .transfer_dst_bit = true, .vertex_buffer_bit = true },
|
||||||
.sharing_mode = .exclusive,
|
.sharing_mode = .exclusive,
|
||||||
.queue_family_index_count = 0,
|
.queue_family_index_count = 0,
|
||||||
.p_queue_family_indices = undefined,
|
.p_queue_family_indices = undefined,
|
||||||
}, null);
|
}, null);
|
||||||
defer gc.vkd.destroyBuffer(gc.dev, buffer, null);
|
defer gc.vkd.destroyBuffer(gc.dev, buffer, null);
|
||||||
const mem_reqs = gc.vkd.getBufferMemoryRequirements(gc.dev, buffer);
|
const mem_reqs = gc.vkd.getBufferMemoryRequirements(gc.dev, buffer);
|
||||||
const memory = try gc.allocate(mem_reqs, .{.device_local_bit = true});
|
const memory = try gc.allocate(mem_reqs, .{ .device_local_bit = true });
|
||||||
defer gc.vkd.freeMemory(gc.dev, memory, null);
|
defer gc.vkd.freeMemory(gc.dev, memory, null);
|
||||||
try gc.vkd.bindBufferMemory(gc.dev, buffer, memory, 0);
|
try gc.vkd.bindBufferMemory(gc.dev, buffer, memory, 0);
|
||||||
|
|
||||||
try uploadVertices(&gc, pool, buffer, memory);
|
try uploadVertices(&gc, pool, buffer);
|
||||||
|
|
||||||
var cmdbufs = try createCommandBuffers(
|
var cmdbufs = try createCommandBuffers(
|
||||||
&gc,
|
&gc,
|
||||||
@@ -114,7 +114,7 @@ pub fn main() !void {
|
|||||||
swapchain.extent,
|
swapchain.extent,
|
||||||
render_pass,
|
render_pass,
|
||||||
pipeline,
|
pipeline,
|
||||||
framebuffers
|
framebuffers,
|
||||||
);
|
);
|
||||||
defer destroyCommandBuffers(&gc, pool, allocator, cmdbufs);
|
defer destroyCommandBuffers(&gc, pool, allocator, cmdbufs);
|
||||||
|
|
||||||
@@ -146,30 +146,29 @@ pub fn main() !void {
|
|||||||
swapchain.extent,
|
swapchain.extent,
|
||||||
render_pass,
|
render_pass,
|
||||||
pipeline,
|
pipeline,
|
||||||
framebuffers
|
framebuffers,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
c.glfwSwapBuffers(window);
|
c.glfwSwapBuffers(window);
|
||||||
c.glfwPollEvents();
|
c.glfwPollEvents();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try swapchain.waitForAllFences();
|
try swapchain.waitForAllFences();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn uploadVertices(gc: *const GraphicsContext, pool: vk.CommandPool, buffer: vk.Buffer, memory: vk.DeviceMemory) !void {
|
fn uploadVertices(gc: *const GraphicsContext, pool: vk.CommandPool, buffer: vk.Buffer) !void {
|
||||||
const staging_buffer = try gc.vkd.createBuffer(gc.dev, .{
|
const staging_buffer = try gc.vkd.createBuffer(gc.dev, .{
|
||||||
.flags = .{},
|
.flags = .{},
|
||||||
.size = @sizeOf(@TypeOf(vertices)),
|
.size = @sizeOf(@TypeOf(vertices)),
|
||||||
.usage = .{.transfer_src_bit = true},
|
.usage = .{ .transfer_src_bit = true },
|
||||||
.sharing_mode = .exclusive,
|
.sharing_mode = .exclusive,
|
||||||
.queue_family_index_count = 0,
|
.queue_family_index_count = 0,
|
||||||
.p_queue_family_indices = undefined,
|
.p_queue_family_indices = undefined,
|
||||||
}, null);
|
}, null);
|
||||||
defer gc.vkd.destroyBuffer(gc.dev, staging_buffer, null);
|
defer gc.vkd.destroyBuffer(gc.dev, staging_buffer, null);
|
||||||
const mem_reqs = gc.vkd.getBufferMemoryRequirements(gc.dev, staging_buffer);
|
const mem_reqs = gc.vkd.getBufferMemoryRequirements(gc.dev, staging_buffer);
|
||||||
const staging_memory = try gc.allocate(mem_reqs, .{.host_visible_bit = true, .host_coherent_bit = true});
|
const staging_memory = try gc.allocate(mem_reqs, .{ .host_visible_bit = true, .host_coherent_bit = true });
|
||||||
defer gc.vkd.freeMemory(gc.dev, staging_memory, null);
|
defer gc.vkd.freeMemory(gc.dev, staging_memory, null);
|
||||||
try gc.vkd.bindBufferMemory(gc.dev, staging_buffer, staging_memory, 0);
|
try gc.vkd.bindBufferMemory(gc.dev, staging_buffer, staging_memory, 0);
|
||||||
|
|
||||||
@@ -196,7 +195,7 @@ fn copyBuffer(gc: *const GraphicsContext, pool: vk.CommandPool, dst: vk.Buffer,
|
|||||||
defer gc.vkd.freeCommandBuffers(gc.dev, pool, 1, @ptrCast([*]const vk.CommandBuffer, &cmdbuf));
|
defer gc.vkd.freeCommandBuffers(gc.dev, pool, 1, @ptrCast([*]const vk.CommandBuffer, &cmdbuf));
|
||||||
|
|
||||||
try gc.vkd.beginCommandBuffer(cmdbuf, .{
|
try gc.vkd.beginCommandBuffer(cmdbuf, .{
|
||||||
.flags = .{.one_time_submit_bit = true},
|
.flags = .{ .one_time_submit_bit = true },
|
||||||
.p_inheritance_info = null,
|
.p_inheritance_info = null,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -243,7 +242,7 @@ fn createCommandBuffers(
|
|||||||
errdefer gc.vkd.freeCommandBuffers(gc.dev, pool, @truncate(u32, cmdbufs.len), cmdbufs.ptr);
|
errdefer gc.vkd.freeCommandBuffers(gc.dev, pool, @truncate(u32, cmdbufs.len), cmdbufs.ptr);
|
||||||
|
|
||||||
const clear = vk.ClearValue{
|
const clear = vk.ClearValue{
|
||||||
.color = .{.float_32 = .{0, 0, 0, 1}},
|
.color = .{ .float_32 = .{ 0, 0, 0, 1 } },
|
||||||
};
|
};
|
||||||
|
|
||||||
const viewport = vk.Viewport{
|
const viewport = vk.Viewport{
|
||||||
@@ -256,7 +255,7 @@ fn createCommandBuffers(
|
|||||||
};
|
};
|
||||||
|
|
||||||
const scissor = vk.Rect2D{
|
const scissor = vk.Rect2D{
|
||||||
.offset = .{.x = 0, .y = 0},
|
.offset = .{ .x = 0, .y = 0 },
|
||||||
.extent = extent,
|
.extent = extent,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -273,7 +272,7 @@ fn createCommandBuffers(
|
|||||||
.render_pass = render_pass,
|
.render_pass = render_pass,
|
||||||
.framebuffer = framebuffers[i],
|
.framebuffer = framebuffers[i],
|
||||||
.render_area = .{
|
.render_area = .{
|
||||||
.offset = .{.x = 0, .y = 0},
|
.offset = .{ .x = 0, .y = 0 },
|
||||||
.extent = extent,
|
.extent = extent,
|
||||||
},
|
},
|
||||||
.clear_value_count = 1,
|
.clear_value_count = 1,
|
||||||
@@ -297,17 +296,12 @@ fn destroyCommandBuffers(gc: *const GraphicsContext, pool: vk.CommandPool, alloc
|
|||||||
allocator.free(cmdbufs);
|
allocator.free(cmdbufs);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn createFramebuffers(
|
fn createFramebuffers(gc: *const GraphicsContext, allocator: *Allocator, render_pass: vk.RenderPass, swapchain: Swapchain) ![]vk.Framebuffer {
|
||||||
gc: *const GraphicsContext,
|
|
||||||
allocator: *Allocator,
|
|
||||||
render_pass: vk.RenderPass,
|
|
||||||
swapchain: Swapchain
|
|
||||||
) ![]vk.Framebuffer {
|
|
||||||
const framebuffers = try allocator.alloc(vk.Framebuffer, swapchain.swap_images.len);
|
const framebuffers = try allocator.alloc(vk.Framebuffer, swapchain.swap_images.len);
|
||||||
errdefer allocator.free(framebuffers);
|
errdefer allocator.free(framebuffers);
|
||||||
|
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
errdefer for (framebuffers[0 .. i]) |fb| gc.vkd.destroyFramebuffer(gc.dev, fb, null);
|
errdefer for (framebuffers[0..i]) |fb| gc.vkd.destroyFramebuffer(gc.dev, fb, null);
|
||||||
|
|
||||||
for (framebuffers) |*fb| {
|
for (framebuffers) |*fb| {
|
||||||
fb.* = try gc.vkd.createFramebuffer(gc.dev, .{
|
fb.* = try gc.vkd.createFramebuffer(gc.dev, .{
|
||||||
@@ -334,7 +328,7 @@ fn createRenderPass(gc: *const GraphicsContext, swapchain: Swapchain) !vk.Render
|
|||||||
const color_attachment = vk.AttachmentDescription{
|
const color_attachment = vk.AttachmentDescription{
|
||||||
.flags = .{},
|
.flags = .{},
|
||||||
.format = swapchain.surface_format.format,
|
.format = swapchain.surface_format.format,
|
||||||
.samples = .{.@"1_bit" = true},
|
.samples = .{ .@"1_bit" = true },
|
||||||
.load_op = .clear,
|
.load_op = .clear,
|
||||||
.store_op = .store,
|
.store_op = .store,
|
||||||
.stencil_load_op = .dont_care,
|
.stencil_load_op = .dont_care,
|
||||||
@@ -374,7 +368,6 @@ fn createRenderPass(gc: *const GraphicsContext, swapchain: Swapchain) !vk.Render
|
|||||||
|
|
||||||
fn createPipeline(
|
fn createPipeline(
|
||||||
gc: *const GraphicsContext,
|
gc: *const GraphicsContext,
|
||||||
extent: vk.Extent2D,
|
|
||||||
layout: vk.PipelineLayout,
|
layout: vk.PipelineLayout,
|
||||||
render_pass: vk.RenderPass,
|
render_pass: vk.RenderPass,
|
||||||
) !vk.Pipeline {
|
) !vk.Pipeline {
|
||||||
@@ -395,14 +388,14 @@ fn createPipeline(
|
|||||||
const pssci = [_]vk.PipelineShaderStageCreateInfo{
|
const pssci = [_]vk.PipelineShaderStageCreateInfo{
|
||||||
.{
|
.{
|
||||||
.flags = .{},
|
.flags = .{},
|
||||||
.stage = .{.vertex_bit = true},
|
.stage = .{ .vertex_bit = true },
|
||||||
.module = vert,
|
.module = vert,
|
||||||
.p_name = "main",
|
.p_name = "main",
|
||||||
.p_specialization_info = null,
|
.p_specialization_info = null,
|
||||||
},
|
},
|
||||||
.{
|
.{
|
||||||
.flags = .{},
|
.flags = .{},
|
||||||
.stage = .{.fragment_bit = true},
|
.stage = .{ .fragment_bit = true },
|
||||||
.module = frag,
|
.module = frag,
|
||||||
.p_name = "main",
|
.p_name = "main",
|
||||||
.p_specialization_info = null,
|
.p_specialization_info = null,
|
||||||
@@ -436,7 +429,7 @@ fn createPipeline(
|
|||||||
.depth_clamp_enable = vk.FALSE,
|
.depth_clamp_enable = vk.FALSE,
|
||||||
.rasterizer_discard_enable = vk.FALSE,
|
.rasterizer_discard_enable = vk.FALSE,
|
||||||
.polygon_mode = .fill,
|
.polygon_mode = .fill,
|
||||||
.cull_mode = .{.back_bit = true},
|
.cull_mode = .{ .back_bit = true },
|
||||||
.front_face = .clockwise,
|
.front_face = .clockwise,
|
||||||
.depth_bias_enable = vk.FALSE,
|
.depth_bias_enable = vk.FALSE,
|
||||||
.depth_bias_constant_factor = 0,
|
.depth_bias_constant_factor = 0,
|
||||||
@@ -447,7 +440,7 @@ fn createPipeline(
|
|||||||
|
|
||||||
const pmsci = vk.PipelineMultisampleStateCreateInfo{
|
const pmsci = vk.PipelineMultisampleStateCreateInfo{
|
||||||
.flags = .{},
|
.flags = .{},
|
||||||
.rasterization_samples = .{.@"1_bit" = true},
|
.rasterization_samples = .{ .@"1_bit" = true },
|
||||||
.sample_shading_enable = vk.FALSE,
|
.sample_shading_enable = vk.FALSE,
|
||||||
.min_sample_shading = 1,
|
.min_sample_shading = 1,
|
||||||
.p_sample_mask = null,
|
.p_sample_mask = null,
|
||||||
@@ -463,7 +456,7 @@ fn createPipeline(
|
|||||||
.src_alpha_blend_factor = .one,
|
.src_alpha_blend_factor = .one,
|
||||||
.dst_alpha_blend_factor = .zero,
|
.dst_alpha_blend_factor = .zero,
|
||||||
.alpha_blend_op = .add,
|
.alpha_blend_op = .add,
|
||||||
.color_write_mask = .{.r_bit = true, .g_bit = true, .b_bit = true, .a_bit = true},
|
.color_write_mask = .{ .r_bit = true, .g_bit = true, .b_bit = true, .a_bit = true },
|
||||||
};
|
};
|
||||||
|
|
||||||
const pcbsci = vk.PipelineColorBlendStateCreateInfo{
|
const pcbsci = vk.PipelineColorBlendStateCreateInfo{
|
||||||
@@ -472,10 +465,10 @@ fn createPipeline(
|
|||||||
.logic_op = .copy,
|
.logic_op = .copy,
|
||||||
.attachment_count = 1,
|
.attachment_count = 1,
|
||||||
.p_attachments = @ptrCast([*]const vk.PipelineColorBlendAttachmentState, &pcbas),
|
.p_attachments = @ptrCast([*]const vk.PipelineColorBlendAttachmentState, &pcbas),
|
||||||
.blend_constants = [_]f32{0, 0, 0, 0},
|
.blend_constants = [_]f32{ 0, 0, 0, 0 },
|
||||||
};
|
};
|
||||||
|
|
||||||
const dynstate = [_]vk.DynamicState{.viewport, .scissor};
|
const dynstate = [_]vk.DynamicState{ .viewport, .scissor };
|
||||||
const pdsci = vk.PipelineDynamicStateCreateInfo{
|
const pdsci = vk.PipelineDynamicStateCreateInfo{
|
||||||
.flags = .{},
|
.flags = .{},
|
||||||
.dynamic_state_count = dynstate.len,
|
.dynamic_state_count = dynstate.len,
|
||||||
@@ -506,7 +499,8 @@ fn createPipeline(
|
|||||||
_ = try gc.vkd.createGraphicsPipelines(
|
_ = try gc.vkd.createGraphicsPipelines(
|
||||||
gc.dev,
|
gc.dev,
|
||||||
.null_handle,
|
.null_handle,
|
||||||
1, @ptrCast([*]const vk.GraphicsPipelineCreateInfo, &gpci),
|
1,
|
||||||
|
@ptrCast([*]const vk.GraphicsPipelineCreateInfo, &gpci),
|
||||||
null,
|
null,
|
||||||
@ptrCast([*]vk.Pipeline, &pipeline),
|
@ptrCast([*]vk.Pipeline, &pipeline),
|
||||||
);
|
);
|
||||||
|
|||||||
2739
examples/vk.xml
2739
examples/vk.xml
File diff suppressed because it is too large
Load Diff
@@ -49,7 +49,7 @@ pub const ShaderCompileStep = struct {
|
|||||||
"shaders",
|
"shaders",
|
||||||
src,
|
src,
|
||||||
}) catch unreachable;
|
}) catch unreachable;
|
||||||
self.shaders.append(.{.source_path = src, .full_out_path = full_out_path}) catch unreachable;
|
self.shaders.append(.{ .source_path = src, .full_out_path = full_out_path }) catch unreachable;
|
||||||
return full_out_path;
|
return full_out_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,64 @@ const std = @import("std");
|
|||||||
const mem = std.mem;
|
const mem = std.mem;
|
||||||
const Allocator = mem.Allocator;
|
const Allocator = mem.Allocator;
|
||||||
|
|
||||||
|
pub fn isZigPrimitiveType(name: []const u8) bool {
|
||||||
|
if (name.len > 1 and (name[0] == 'u' or name[0] == 'i')) {
|
||||||
|
for (name[1..]) |c| {
|
||||||
|
switch (c) {
|
||||||
|
'0'...'9' => {},
|
||||||
|
else => return false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const primitives = [_][]const u8{
|
||||||
|
"void",
|
||||||
|
"comptime_float",
|
||||||
|
"comptime_int",
|
||||||
|
"bool",
|
||||||
|
"isize",
|
||||||
|
"usize",
|
||||||
|
"f16",
|
||||||
|
"f32",
|
||||||
|
"f64",
|
||||||
|
"f128",
|
||||||
|
"c_longdouble",
|
||||||
|
"noreturn",
|
||||||
|
"type",
|
||||||
|
"anyerror",
|
||||||
|
"c_short",
|
||||||
|
"c_ushort",
|
||||||
|
"c_int",
|
||||||
|
"c_uint",
|
||||||
|
"c_long",
|
||||||
|
"c_ulong",
|
||||||
|
"c_longlong",
|
||||||
|
"c_ulonglong",
|
||||||
|
};
|
||||||
|
|
||||||
|
for (primitives) |reserved| {
|
||||||
|
if (mem.eql(u8, reserved, name)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn needZigEscape(name: []const u8) bool {
|
||||||
|
return !std.zig.fmt.isValidId(name) or isZigPrimitiveType(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn writeIdentifier(out: anytype, id: []const u8) !void {
|
||||||
|
// https://github.com/ziglang/zig/issues/2897
|
||||||
|
if (isZigPrimitiveType(id)) {
|
||||||
|
try out.print("{s}_", .{id});
|
||||||
|
} else {
|
||||||
|
try out.print("{}", .{std.zig.fmtId(id)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub const CaseStyle = enum {
|
pub const CaseStyle = enum {
|
||||||
snake,
|
snake,
|
||||||
screaming_snake,
|
screaming_snake,
|
||||||
@@ -51,7 +109,7 @@ pub const SegmentIterator = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const end = self.nextBoundary();
|
const end = self.nextBoundary();
|
||||||
const word = self.text[self.offset .. end];
|
const word = self.text[self.offset..end];
|
||||||
self.offset = end;
|
self.offset = end;
|
||||||
return word;
|
return word;
|
||||||
}
|
}
|
||||||
@@ -128,7 +186,7 @@ pub const IdRenderer = struct {
|
|||||||
}
|
}
|
||||||
lower_first = false;
|
lower_first = false;
|
||||||
|
|
||||||
for (segment[i + 1..]) |c| {
|
for (segment[i + 1 ..]) |c| {
|
||||||
try self.text_cache.append(std.ascii.toLower(c));
|
try self.text_cache.append(std.ascii.toLower(c));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,14 +196,10 @@ pub const IdRenderer = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(self: IdRenderer, out: anytype, id: []const u8) !void {
|
|
||||||
try out.print("{z}", .{ id });
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn renderFmt(self: *IdRenderer, out: anytype, comptime fmt: []const u8, args: anytype) !void {
|
pub fn renderFmt(self: *IdRenderer, out: anytype, comptime fmt: []const u8, args: anytype) !void {
|
||||||
self.text_cache.items.len = 0;
|
self.text_cache.items.len = 0;
|
||||||
try std.fmt.format(self.text_cache.writer(), fmt, args);
|
try std.fmt.format(self.text_cache.writer(), fmt, args);
|
||||||
try out.print("{z}", .{ self.text_cache.items });
|
try writeIdentifier(out, self.text_cache.items);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn renderWithCase(self: *IdRenderer, out: anytype, case_style: CaseStyle, id: []const u8) !void {
|
pub fn renderWithCase(self: *IdRenderer, out: anytype, case_style: CaseStyle, id: []const u8) !void {
|
||||||
@@ -162,7 +216,7 @@ pub const IdRenderer = struct {
|
|||||||
.camel => try self.renderCamel(false, adjusted_id, tag),
|
.camel => try self.renderCamel(false, adjusted_id, tag),
|
||||||
}
|
}
|
||||||
|
|
||||||
try out.print("{z}", .{ self.text_cache.items });
|
try writeIdentifier(out, self.text_cache.items);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getAuthorTag(self: IdRenderer, id: []const u8) ?[]const u8 {
|
pub fn getAuthorTag(self: IdRenderer, id: []const u8) ?[]const u8 {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
pub const generateVk = @import("vulkan/generator.zig").generate;
|
pub const generateVk = @import("vulkan/generator.zig").generate;
|
||||||
pub const VkGenerateStep = @import("vulkan/build_integration.zig").GenerateStep;
|
pub const VkGenerateStep = @import("vulkan/build_integration.zig").GenerateStep;
|
||||||
pub const generateSpirv = @import("spirv/generator.zig").generate;
|
|
||||||
pub const ShaderCompileStep = @import("build_integration.zig").ShaderCompileStep;
|
pub const ShaderCompileStep = @import("build_integration.zig").ShaderCompileStep;
|
||||||
|
|
||||||
test "main" {
|
test "main" {
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ const usage = "Usage: {s} [-h|--help] <spec xml path> <output zig source>\n";
|
|||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
const stderr = std.io.getStdErr();
|
const stderr = std.io.getStdErr();
|
||||||
const stdout = std.io.getStdOut();
|
|
||||||
|
|
||||||
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||||
defer arena.deinit();
|
defer arena.deinit();
|
||||||
@@ -32,7 +31,7 @@ pub fn main() !void {
|
|||||||
\\
|
\\
|
||||||
\\
|
\\
|
||||||
++ usage,
|
++ usage,
|
||||||
.{ prog_name },
|
.{prog_name},
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
} else if (maybe_xml_path == null) {
|
} else if (maybe_xml_path == null) {
|
||||||
@@ -40,17 +39,17 @@ pub fn main() !void {
|
|||||||
} else if (maybe_out_path == null) {
|
} else if (maybe_out_path == null) {
|
||||||
maybe_out_path = arg;
|
maybe_out_path = arg;
|
||||||
} else {
|
} else {
|
||||||
try stderr.writer().print("Error: Superficial argument '{s}'\n", .{ arg });
|
try stderr.writer().print("Error: Superficial argument '{s}'\n", .{arg});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const xml_path = maybe_xml_path orelse {
|
const xml_path = maybe_xml_path orelse {
|
||||||
try stderr.writer().print("Error: Missing required argument <spec xml path>\n" ++ usage, .{ prog_name });
|
try stderr.writer().print("Error: Missing required argument <spec xml path>\n" ++ usage, .{prog_name});
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
const out_path = maybe_out_path orelse {
|
const out_path = maybe_out_path orelse {
|
||||||
try stderr.writer().print("Error: Missing required argument <output zig source>\n" ++ usage, .{ prog_name });
|
try stderr.writer().print("Error: Missing required argument <output zig source>\n" ++ usage, .{prog_name});
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -60,15 +59,22 @@ pub fn main() !void {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
const out_file = cwd.createFile(out_path, .{}) catch |err| {
|
|
||||||
try stderr.writer().print("Error: Failed to create output file '{s}' ({s})\n", .{ out_path, @errorName(err) });
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
defer out_file.close();
|
|
||||||
|
|
||||||
var out_buffer = std.ArrayList(u8).init(allocator);
|
var out_buffer = std.ArrayList(u8).init(allocator);
|
||||||
try generate(allocator, xml_src, out_buffer.writer());
|
try generate(allocator, xml_src, out_buffer.writer());
|
||||||
const tree = try std.zig.parse(allocator, out_buffer.items);
|
|
||||||
|
|
||||||
_ = try std.zig.render(allocator, out_file.writer(), tree);
|
const tree = try std.zig.parse(allocator, out_buffer.items);
|
||||||
|
const formatted = try tree.render(allocator);
|
||||||
|
defer allocator.free(formatted);
|
||||||
|
|
||||||
|
if (std.fs.path.dirname(out_path)) |dir| {
|
||||||
|
cwd.makePath(dir) catch |err| {
|
||||||
|
try stderr.writer().print("Error: Failed to create output directory '{s}' ({s})\n", .{ dir, @errorName(err) });
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
cwd.writeFile(out_path, formatted) catch |err| {
|
||||||
|
try stderr.writer().print("Error: Failed to write to output file '{s}' ({s})\n", .{ out_path, @errorName(err) });
|
||||||
|
return;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ pub const GenerateStep = struct {
|
|||||||
pub fn initFromSdk(builder: *Builder, sdk_path: []const u8, out_path: []const u8) *GenerateStep {
|
pub fn initFromSdk(builder: *Builder, sdk_path: []const u8, out_path: []const u8) *GenerateStep {
|
||||||
const spec_path = std.fs.path.join(
|
const spec_path = std.fs.path.join(
|
||||||
builder.allocator,
|
builder.allocator,
|
||||||
&[_][]const u8{sdk_path, "share/vulkan/registry/vk.xml"},
|
&[_][]const u8{ sdk_path, "share/vulkan/registry/vk.xml" },
|
||||||
) catch unreachable;
|
) catch unreachable;
|
||||||
|
|
||||||
return init(builder, spec_path, out_path);
|
return init(builder, spec_path, out_path);
|
||||||
@@ -65,16 +65,18 @@ pub const GenerateStep = struct {
|
|||||||
fn make(step: *Step) !void {
|
fn make(step: *Step) !void {
|
||||||
const self = @fieldParentPtr(GenerateStep, "step", step);
|
const self = @fieldParentPtr(GenerateStep, "step", step);
|
||||||
const cwd = std.fs.cwd();
|
const cwd = std.fs.cwd();
|
||||||
var out_buffer = std.ArrayList(u8).init(self.builder.allocator);
|
|
||||||
const spec = try cwd.readFileAlloc(self.builder.allocator, self.spec_path, std.math.maxInt(usize));
|
const spec = try cwd.readFileAlloc(self.builder.allocator, self.spec_path, std.math.maxInt(usize));
|
||||||
|
|
||||||
|
var out_buffer = std.ArrayList(u8).init(self.builder.allocator);
|
||||||
try generate(self.builder.allocator, spec, out_buffer.writer());
|
try generate(self.builder.allocator, spec, out_buffer.writer());
|
||||||
|
|
||||||
const tree = try std.zig.parse(self.builder.allocator, out_buffer.items);
|
const tree = try std.zig.parse(self.builder.allocator, out_buffer.items);
|
||||||
|
|
||||||
|
var formatted = try tree.render(self.builder.allocator);
|
||||||
|
|
||||||
const dir = path.dirname(self.package.path).?;
|
const dir = path.dirname(self.package.path).?;
|
||||||
try cwd.makePath(dir);
|
try cwd.makePath(dir);
|
||||||
const output_file = cwd.createFile(self.package.path, .{}) catch unreachable;
|
try cwd.writeFile(self.package.path, formatted);
|
||||||
defer output_file.close();
|
|
||||||
_ = try std.zig.render(self.builder.allocator, output_file.writer(), tree);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ pub const CTokenizer = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const token_text = self.source[start .. self.offset];
|
const token_text = self.source[start..self.offset];
|
||||||
|
|
||||||
const kind = if (mem.eql(u8, token_text, "typedef"))
|
const kind = if (mem.eql(u8, token_text, "typedef"))
|
||||||
Token.Kind.kw_typedef
|
Token.Kind.kw_typedef
|
||||||
@@ -83,7 +83,7 @@ pub const CTokenizer = struct {
|
|||||||
else
|
else
|
||||||
Token.Kind.id;
|
Token.Kind.id;
|
||||||
|
|
||||||
return .{.kind = kind, .text = token_text};
|
return .{ .kind = kind, .text = token_text };
|
||||||
}
|
}
|
||||||
|
|
||||||
fn int(self: *CTokenizer) Token {
|
fn int(self: *CTokenizer) Token {
|
||||||
@@ -100,7 +100,7 @@ pub const CTokenizer = struct {
|
|||||||
|
|
||||||
return .{
|
return .{
|
||||||
.kind = .int,
|
.kind = .int,
|
||||||
.text = self.source[start .. self.offset],
|
.text = self.source[start..self.offset],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +115,7 @@ pub const CTokenizer = struct {
|
|||||||
|
|
||||||
pub fn next(self: *CTokenizer) !?Token {
|
pub fn next(self: *CTokenizer) !?Token {
|
||||||
self.skipws();
|
self.skipws();
|
||||||
if (mem.startsWith(u8, self.source[self.offset ..], "//") or self.in_comment) {
|
if (mem.startsWith(u8, self.source[self.offset..], "//") or self.in_comment) {
|
||||||
const end = mem.indexOfScalarPos(u8, self.source, self.offset, '\n') orelse {
|
const end = mem.indexOfScalarPos(u8, self.source, self.offset, '\n') orelse {
|
||||||
self.offset = self.source.len;
|
self.offset = self.source.len;
|
||||||
self.in_comment = true;
|
self.in_comment = true;
|
||||||
@@ -143,15 +143,12 @@ pub const CTokenizer = struct {
|
|||||||
']' => kind = .rbracket,
|
']' => kind = .rbracket,
|
||||||
'(' => kind = .lparen,
|
'(' => kind = .lparen,
|
||||||
')' => kind = .rparen,
|
')' => kind = .rparen,
|
||||||
else => return error.UnexpectedCharacter
|
else => return error.UnexpectedCharacter,
|
||||||
}
|
}
|
||||||
|
|
||||||
const start = self.offset;
|
const start = self.offset;
|
||||||
_ = self.consumeNoEof();
|
_ = self.consumeNoEof();
|
||||||
return Token{
|
return Token{ .kind = kind, .text = self.source[start..self.offset] };
|
||||||
.kind = kind,
|
|
||||||
.text = self.source[start .. self.offset]
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -173,11 +170,11 @@ pub const XmlCTokenizer = struct {
|
|||||||
|
|
||||||
const text = elem.children.items[0].CharData;
|
const text = elem.children.items[0].CharData;
|
||||||
if (mem.eql(u8, elem.tag, "type")) {
|
if (mem.eql(u8, elem.tag, "type")) {
|
||||||
return Token{.kind = .type_name, .text = text};
|
return Token{ .kind = .type_name, .text = text };
|
||||||
} else if (mem.eql(u8, elem.tag, "enum")) {
|
} else if (mem.eql(u8, elem.tag, "enum")) {
|
||||||
return Token{.kind = .enum_name, .text = text};
|
return Token{ .kind = .enum_name, .text = text };
|
||||||
} else if (mem.eql(u8, elem.tag, "name")) {
|
} else if (mem.eql(u8, elem.tag, "name")) {
|
||||||
return Token{.kind = .name, .text = text};
|
return Token{ .kind = .name, .text = text };
|
||||||
} else if (mem.eql(u8, elem.tag, "comment")) {
|
} else if (mem.eql(u8, elem.tag, "comment")) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
@@ -206,7 +203,7 @@ pub const XmlCTokenizer = struct {
|
|||||||
|
|
||||||
if (self.it.next()) |child| {
|
if (self.it.next()) |child| {
|
||||||
switch (child.*) {
|
switch (child.*) {
|
||||||
.CharData => |cdata| self.ctok = CTokenizer{.source = cdata, .in_comment = in_comment},
|
.CharData => |cdata| self.ctok = CTokenizer{ .source = cdata, .in_comment = in_comment },
|
||||||
.Comment => {}, // xml comment
|
.Comment => {}, // xml comment
|
||||||
.Element => |elem| if (!in_comment) if (try elemToToken(elem)) |tok| return tok,
|
.Element => |elem| if (!in_comment) if (try elemToToken(elem)) |tok| return tok,
|
||||||
}
|
}
|
||||||
@@ -254,14 +251,14 @@ pub fn parseTypedef(allocator: *Allocator, xctok: *XmlCTokenizer) !registry.Decl
|
|||||||
|
|
||||||
return registry.Declaration{
|
return registry.Declaration{
|
||||||
.name = decl.name orelse return error.MissingTypeIdentifier,
|
.name = decl.name orelse return error.MissingTypeIdentifier,
|
||||||
.decl_type = .{.typedef = decl.decl_type},
|
.decl_type = .{ .typedef = decl.decl_type },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// MEMBER = DECLARATION (':' int)?
|
// MEMBER = DECLARATION (':' int)?
|
||||||
pub fn parseMember(allocator: *Allocator, xctok: *XmlCTokenizer) !registry.Container.Field {
|
pub fn parseMember(allocator: *Allocator, xctok: *XmlCTokenizer) !registry.Container.Field {
|
||||||
const decl = try parseDeclaration(allocator, xctok);
|
const decl = try parseDeclaration(allocator, xctok);
|
||||||
var field = registry.Container.Field {
|
var field = registry.Container.Field{
|
||||||
.name = decl.name orelse return error.MissingTypeIdentifier,
|
.name = decl.name orelse return error.MissingTypeIdentifier,
|
||||||
.field_type = decl.decl_type,
|
.field_type = decl.decl_type,
|
||||||
.bits = null,
|
.bits = null,
|
||||||
@@ -294,7 +291,7 @@ pub fn parseParamOrProto(allocator: *Allocator, xctok: *XmlCTokenizer) !registry
|
|||||||
}
|
}
|
||||||
return registry.Declaration{
|
return registry.Declaration{
|
||||||
.name = decl.name orelse return error.MissingTypeIdentifier,
|
.name = decl.name orelse return error.MissingTypeIdentifier,
|
||||||
.decl_type = .{.typedef = decl.decl_type},
|
.decl_type = .{ .typedef = decl.decl_type },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -333,7 +330,7 @@ fn parseDeclaration(allocator: *Allocator, xctok: *XmlCTokenizer) ParseError!Dec
|
|||||||
if (tok.kind != .type_name and tok.kind != .id) return error.InvalidSyntax;
|
if (tok.kind != .type_name and tok.kind != .id) return error.InvalidSyntax;
|
||||||
const type_name = tok.text;
|
const type_name = tok.text;
|
||||||
|
|
||||||
var type_info = TypeInfo{.name = type_name};
|
var type_info = TypeInfo{ .name = type_name };
|
||||||
|
|
||||||
// Parse pointers
|
// Parse pointers
|
||||||
type_info = try parsePointers(allocator, xctok, inner_is_const, type_info);
|
type_info = try parsePointers(allocator, xctok, inner_is_const, type_info);
|
||||||
@@ -365,7 +362,7 @@ fn parseDeclaration(allocator: *Allocator, xctok: *XmlCTokenizer) ParseError!Dec
|
|||||||
.array = .{
|
.array = .{
|
||||||
.size = array_size,
|
.size = array_size,
|
||||||
.child = child,
|
.child = child,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// update the inner_type pointer so it points to the proper
|
// update the inner_type pointer so it points to the proper
|
||||||
@@ -403,8 +400,8 @@ fn parseFnPtrSuffix(allocator: *Allocator, xctok: *XmlCTokenizer, return_type: T
|
|||||||
.return_type = return_type_heap,
|
.return_type = return_type_heap,
|
||||||
.success_codes = &[_][]const u8{},
|
.success_codes = &[_][]const u8{},
|
||||||
.error_codes = &[_][]const u8{},
|
.error_codes = &[_][]const u8{},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const first_param = try parseDeclaration(allocator, xctok);
|
const first_param = try parseDeclaration(allocator, xctok);
|
||||||
@@ -500,31 +497,31 @@ fn parseArrayDeclarator(xctok: *XmlCTokenizer) !?ArraySize {
|
|||||||
.int = std.fmt.parseInt(usize, size_tok.text, 10) catch |err| switch (err) {
|
.int = std.fmt.parseInt(usize, size_tok.text, 10) catch |err| switch (err) {
|
||||||
error.Overflow => return error.Overflow,
|
error.Overflow => return error.Overflow,
|
||||||
error.InvalidCharacter => unreachable,
|
error.InvalidCharacter => unreachable,
|
||||||
}
|
|
||||||
},
|
},
|
||||||
.enum_name => .{.alias = size_tok.text},
|
},
|
||||||
else => return error.InvalidSyntax
|
.enum_name => .{ .alias = size_tok.text },
|
||||||
|
else => return error.InvalidSyntax,
|
||||||
};
|
};
|
||||||
|
|
||||||
_ = try xctok.expect(.rbracket);
|
_ = try xctok.expect(.rbracket);
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parseVersion(xctok: *XmlCTokenizer) ![3][]const u8 {
|
pub fn parseVersion(xctok: *XmlCTokenizer) ![4][]const u8 {
|
||||||
_ = try xctok.expect(.hash);
|
_ = try xctok.expect(.hash);
|
||||||
const define = try xctok.expect(.id);
|
const define = try xctok.expect(.id);
|
||||||
if (!mem.eql(u8, define.text, "define")) {
|
if (!mem.eql(u8, define.text, "define")) {
|
||||||
return error.InvalidVersion;
|
return error.InvalidVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
const name = try xctok.expect(.name);
|
_ = try xctok.expect(.name);
|
||||||
const vk_make_version = try xctok.expect(.type_name);
|
const vk_make_version = try xctok.expect(.type_name);
|
||||||
if (!mem.eql(u8, vk_make_version.text, "VK_MAKE_VERSION")) {
|
if (!mem.eql(u8, vk_make_version.text, "VK_MAKE_API_VERSION")) {
|
||||||
return error.NotVersion;
|
return error.NotVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = try xctok.expect(.lparen);
|
_ = try xctok.expect(.lparen);
|
||||||
var version: [3][]const u8 = undefined;
|
var version: [4][]const u8 = undefined;
|
||||||
for (version) |*part, i| {
|
for (version) |*part, i| {
|
||||||
if (i != 0) {
|
if (i != 0) {
|
||||||
_ = try xctok.expect(.comma);
|
_ = try xctok.expect(.comma);
|
||||||
@@ -540,44 +537,38 @@ pub fn parseVersion(xctok: *XmlCTokenizer) ![3][]const u8 {
|
|||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn testTokenizer(tokenizer: anytype, expected_tokens: []const Token) void {
|
fn testTokenizer(tokenizer: anytype, expected_tokens: []const Token) !void {
|
||||||
for (expected_tokens) |expected| {
|
for (expected_tokens) |expected| {
|
||||||
const tok = (tokenizer.next() catch unreachable).?;
|
const tok = (tokenizer.next() catch unreachable).?;
|
||||||
testing.expectEqual(expected.kind, tok.kind);
|
try testing.expectEqual(expected.kind, tok.kind);
|
||||||
testing.expectEqualSlices(u8, expected.text, tok.text);
|
try testing.expectEqualSlices(u8, expected.text, tok.text);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tokenizer.next() catch unreachable) |_| unreachable;
|
if (tokenizer.next() catch unreachable) |_| unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
test "CTokenizer" {
|
test "CTokenizer" {
|
||||||
var ctok = CTokenizer {
|
var ctok = CTokenizer{ .source = "typedef ([const)]** VKAPI_PTR 123,;aaaa" };
|
||||||
.source = \\typedef ([const)]** VKAPI_PTR 123,;aaaa
|
|
||||||
};
|
|
||||||
|
|
||||||
testTokenizer(
|
try testTokenizer(&ctok, &[_]Token{
|
||||||
&ctok,
|
.{ .kind = .kw_typedef, .text = "typedef" },
|
||||||
&[_]Token{
|
.{ .kind = .lparen, .text = "(" },
|
||||||
.{.kind = .kw_typedef, .text = "typedef"},
|
.{ .kind = .lbracket, .text = "[" },
|
||||||
.{.kind = .lparen, .text = "("},
|
.{ .kind = .kw_const, .text = "const" },
|
||||||
.{.kind = .lbracket, .text = "["},
|
.{ .kind = .rparen, .text = ")" },
|
||||||
.{.kind = .kw_const, .text = "const"},
|
.{ .kind = .rbracket, .text = "]" },
|
||||||
.{.kind = .rparen, .text = ")"},
|
.{ .kind = .star, .text = "*" },
|
||||||
.{.kind = .rbracket, .text = "]"},
|
.{ .kind = .star, .text = "*" },
|
||||||
.{.kind = .star, .text = "*"},
|
.{ .kind = .kw_vkapi_ptr, .text = "VKAPI_PTR" },
|
||||||
.{.kind = .star, .text = "*"},
|
.{ .kind = .int, .text = "123" },
|
||||||
.{.kind = .kw_vkapi_ptr, .text = "VKAPI_PTR"},
|
.{ .kind = .comma, .text = "," },
|
||||||
.{.kind = .int, .text = "123"},
|
.{ .kind = .semicolon, .text = ";" },
|
||||||
.{.kind = .comma, .text = ","},
|
.{ .kind = .id, .text = "aaaa" },
|
||||||
.{.kind = .semicolon, .text = ";"},
|
});
|
||||||
.{.kind = .id, .text = "aaaa"},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test "XmlCTokenizer" {
|
test "XmlCTokenizer" {
|
||||||
const document = try xml.parse(
|
const document = try xml.parse(testing.allocator,
|
||||||
testing.allocator,
|
|
||||||
\\<root>// comment <name>commented name</name> <type>commented type</type> trailing
|
\\<root>// comment <name>commented name</name> <type>commented type</type> trailing
|
||||||
\\ typedef void (VKAPI_PTR *<name>PFN_vkVoidFunction</name>)(void);
|
\\ typedef void (VKAPI_PTR *<name>PFN_vkVoidFunction</name>)(void);
|
||||||
\\</root>
|
\\</root>
|
||||||
@@ -586,27 +577,23 @@ test "XmlCTokenizer" {
|
|||||||
|
|
||||||
var xctok = XmlCTokenizer.init(document.root);
|
var xctok = XmlCTokenizer.init(document.root);
|
||||||
|
|
||||||
testTokenizer(
|
try testTokenizer(&xctok, &[_]Token{
|
||||||
&xctok,
|
.{ .kind = .kw_typedef, .text = "typedef" },
|
||||||
&[_]Token{
|
.{ .kind = .id, .text = "void" },
|
||||||
.{.kind = .kw_typedef, .text = "typedef"},
|
.{ .kind = .lparen, .text = "(" },
|
||||||
.{.kind = .id, .text = "void"},
|
.{ .kind = .kw_vkapi_ptr, .text = "VKAPI_PTR" },
|
||||||
.{.kind = .lparen, .text = "("},
|
.{ .kind = .star, .text = "*" },
|
||||||
.{.kind = .kw_vkapi_ptr, .text = "VKAPI_PTR"},
|
.{ .kind = .name, .text = "PFN_vkVoidFunction" },
|
||||||
.{.kind = .star, .text = "*"},
|
.{ .kind = .rparen, .text = ")" },
|
||||||
.{.kind = .name, .text = "PFN_vkVoidFunction"},
|
.{ .kind = .lparen, .text = "(" },
|
||||||
.{.kind = .rparen, .text = ")"},
|
.{ .kind = .id, .text = "void" },
|
||||||
.{.kind = .lparen, .text = "("},
|
.{ .kind = .rparen, .text = ")" },
|
||||||
.{.kind = .id, .text = "void"},
|
.{ .kind = .semicolon, .text = ";" },
|
||||||
.{.kind = .rparen, .text = ")"},
|
});
|
||||||
.{.kind = .semicolon, .text = ";"},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test "parseTypedef" {
|
test "parseTypedef" {
|
||||||
const document = try xml.parse(
|
const document = try xml.parse(testing.allocator,
|
||||||
testing.allocator,
|
|
||||||
\\<root> // comment <name>commented name</name> trailing
|
\\<root> // comment <name>commented name</name> trailing
|
||||||
\\ typedef const struct <type>Python</type>* pythons[4];
|
\\ typedef const struct <type>Python</type>* pythons[4];
|
||||||
\\ // more comments
|
\\ // more comments
|
||||||
@@ -621,10 +608,10 @@ test "parseTypedef" {
|
|||||||
var xctok = XmlCTokenizer.init(document.root);
|
var xctok = XmlCTokenizer.init(document.root);
|
||||||
const decl = try parseTypedef(&arena.allocator, &xctok);
|
const decl = try parseTypedef(&arena.allocator, &xctok);
|
||||||
|
|
||||||
testing.expectEqualSlices(u8, "pythons", decl.name);
|
try testing.expectEqualSlices(u8, "pythons", decl.name);
|
||||||
const array = decl.decl_type.typedef.array;
|
const array = decl.decl_type.typedef.array;
|
||||||
testing.expectEqual(ArraySize{.int = 4}, array.size);
|
try testing.expectEqual(ArraySize{ .int = 4 }, array.size);
|
||||||
const ptr = array.child.pointer;
|
const ptr = array.child.pointer;
|
||||||
testing.expectEqual(true, ptr.is_const);
|
try testing.expectEqual(true, ptr.is_const);
|
||||||
testing.expectEqualSlices(u8, "Python", ptr.child.name);
|
try testing.expectEqualSlices(u8, "Python", ptr.child.name);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ const EnumFieldMerger = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn deinit(self: *EnumFieldMerger) void {
|
fn deinit(self: *EnumFieldMerger) void {
|
||||||
for (self.enum_extensions.items()) |*entry| {
|
for (self.enum_extensions.values()) |*value| {
|
||||||
entry.value.deinit(self.gpa);
|
value.deinit(self.gpa);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.field_set.deinit();
|
self.field_set.deinit();
|
||||||
@@ -40,10 +40,10 @@ const EnumFieldMerger = struct {
|
|||||||
fn putEnumExtension(self: *EnumFieldMerger, enum_name: []const u8, field: reg.Enum.Field) !void {
|
fn putEnumExtension(self: *EnumFieldMerger, enum_name: []const u8, field: reg.Enum.Field) !void {
|
||||||
const res = try self.enum_extensions.getOrPut(enum_name);
|
const res = try self.enum_extensions.getOrPut(enum_name);
|
||||||
if (!res.found_existing) {
|
if (!res.found_existing) {
|
||||||
res.entry.value = std.ArrayListUnmanaged(reg.Enum.Field){};
|
res.value_ptr.* = std.ArrayListUnmanaged(reg.Enum.Field){};
|
||||||
}
|
}
|
||||||
|
|
||||||
try res.entry.value.append(self.gpa, field);
|
try res.value_ptr.append(self.gpa, field);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn addRequires(self: *EnumFieldMerger, reqs: []const reg.Require) !void {
|
fn addRequires(self: *EnumFieldMerger, reqs: []const reg.Require) !void {
|
||||||
@@ -105,128 +105,6 @@ const EnumFieldMerger = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const TagFixerUpper = struct {
|
|
||||||
allocator: *Allocator,
|
|
||||||
registry: *reg.Registry,
|
|
||||||
names: std.StringHashMap(void),
|
|
||||||
id_renderer: *const IdRenderer,
|
|
||||||
|
|
||||||
fn init(allocator: *Allocator, registry: *reg.Registry, id_renderer: *const IdRenderer) TagFixerUpper {
|
|
||||||
return .{
|
|
||||||
.allocator = allocator,
|
|
||||||
.registry = registry,
|
|
||||||
.names = std.StringHashMap(void).init(allocator),
|
|
||||||
.id_renderer = id_renderer,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deinit(self: *TagFixerUpper) void {
|
|
||||||
self.names.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn insertName(self: *TagFixerUpper, name: []const u8) !void {
|
|
||||||
const tagless = self.id_renderer.stripAuthorTag(name);
|
|
||||||
const result = try self.names.getOrPut(name);
|
|
||||||
|
|
||||||
if (result.found_existing) {
|
|
||||||
return error.DuplicateDefinition;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extractNames(self: *TagFixerUpper) !void {
|
|
||||||
for (self.registry.decls) |decl| {
|
|
||||||
try self.insertName(decl.name);
|
|
||||||
|
|
||||||
switch (decl.decl_type) {
|
|
||||||
.enumeration => |enumeration| {
|
|
||||||
for (enumeration.fields) |field| {
|
|
||||||
try self.insertName(field.name);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fixAlias(self: *TagFixerUpper, name: *[]const u8) !void {
|
|
||||||
if (self.names.contains(name.*)) {
|
|
||||||
// The alias exists, everything is fine
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The alias does not exist, check if the tagless version exists
|
|
||||||
const tagless = self.id_renderer.stripAuthorTag(name.*);
|
|
||||||
if (self.names.contains(tagless)) {
|
|
||||||
// Fix up the name to the tagless version
|
|
||||||
name.* = tagless;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Neither original nor tagless version exists
|
|
||||||
return error.InvalidRegistry;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fixCommand(self: *TagFixerUpper, command: *reg.Command) !void {
|
|
||||||
for (command.params) |*param| {
|
|
||||||
try self.fixTypeInfo(¶m.param_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
try self.fixTypeInfo(command.return_type);
|
|
||||||
for (command.success_codes) |*code| {
|
|
||||||
try self.fixAlias(code);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (command.error_codes) |*code| {
|
|
||||||
try self.fixAlias(code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fixTypeInfo(self: *TagFixerUpper, type_info: *reg.TypeInfo) error{InvalidRegistry}!void {
|
|
||||||
switch (type_info.*) {
|
|
||||||
.name => |*name| try self.fixAlias(name),
|
|
||||||
.command_ptr => |*command| try self.fixCommand(command),
|
|
||||||
.pointer => |ptr| try self.fixTypeInfo(ptr.child),
|
|
||||||
.array => |arr| try self.fixTypeInfo(arr.child),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fixNames(self: *TagFixerUpper) !void {
|
|
||||||
for (self.registry.decls) |*decl| {
|
|
||||||
switch (decl.decl_type) {
|
|
||||||
.container => |*container| {
|
|
||||||
for (container.fields) |*field| {
|
|
||||||
try self.fixTypeInfo(&field.field_type);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.enumeration => |*enumeration| {
|
|
||||||
for (enumeration.fields) |*field| {
|
|
||||||
if (field.value == .alias) {
|
|
||||||
try self.fixAlias(&field.value.alias.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.bitmask => |*bitmask| {
|
|
||||||
if (bitmask.bits_enum) |*bits| {
|
|
||||||
try self.fixAlias(bits);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.command => |*command| try self.fixCommand(command),
|
|
||||||
.alias => |*alias| try self.fixAlias(&alias.name),
|
|
||||||
.typedef => |*type_info| try self.fixTypeInfo(type_info),
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fixup(self: *TagFixerUpper) !void {
|
|
||||||
// Extract all non-aliases
|
|
||||||
try self.extractNames();
|
|
||||||
|
|
||||||
// Fix aliases
|
|
||||||
try self.fixNames();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Generator = struct {
|
pub const Generator = struct {
|
||||||
gpa: *Allocator,
|
gpa: *Allocator,
|
||||||
reg_arena: std.heap.ArenaAllocator,
|
reg_arena: std.heap.ArenaAllocator,
|
||||||
@@ -252,17 +130,6 @@ pub const Generator = struct {
|
|||||||
self.reg_arena.deinit();
|
self.reg_arena.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn removePromotedExtensions(self: *Generator) void {
|
|
||||||
var write_index: usize = 0;
|
|
||||||
for (self.registry.extensions) |ext| {
|
|
||||||
if (ext.promoted_to == .none) {
|
|
||||||
self.registry.extensions[write_index] = ext;
|
|
||||||
write_index += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.registry.extensions.len = write_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn stripFlagBits(self: Generator, name: []const u8) []const u8 {
|
fn stripFlagBits(self: Generator, name: []const u8) []const u8 {
|
||||||
const tagless = self.id_renderer.stripAuthorTag(name);
|
const tagless = self.id_renderer.stripAuthorTag(name);
|
||||||
return tagless[0 .. tagless.len - "FlagBits".len];
|
return tagless[0 .. tagless.len - "FlagBits".len];
|
||||||
@@ -273,34 +140,6 @@ pub const Generator = struct {
|
|||||||
return tagless[0 .. tagless.len - "Flags".len];
|
return tagless[0 .. tagless.len - "Flags".len];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fixupBitmasks(self: *Generator) !void {
|
|
||||||
var bits = std.StringHashMap([]const u8).init(self.gpa);
|
|
||||||
defer bits.deinit();
|
|
||||||
|
|
||||||
for (self.registry.decls) |decl| {
|
|
||||||
if (decl.decl_type == .enumeration and decl.decl_type.enumeration.is_bitmask) {
|
|
||||||
try bits.put(self.stripFlagBits(decl.name), decl.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (self.registry.decls) |*decl| {
|
|
||||||
switch (decl.decl_type) {
|
|
||||||
.bitmask => |*bitmask| {
|
|
||||||
const base_name = self.stripFlags(decl.name);
|
|
||||||
|
|
||||||
if (bitmask.bits_enum) |bits_enum| {
|
|
||||||
if (bits.get(base_name) == null) {
|
|
||||||
bitmask.bits_enum = null;
|
|
||||||
}
|
|
||||||
} else if (bits.get(base_name)) |bits_enum| {
|
|
||||||
bitmask.bits_enum = bits_enum;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
else => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Solve `registry.declarations` according to `registry.extensions` and `registry.features`.
|
// Solve `registry.declarations` according to `registry.extensions` and `registry.features`.
|
||||||
fn mergeEnumFields(self: *Generator) !void {
|
fn mergeEnumFields(self: *Generator) !void {
|
||||||
var merger = EnumFieldMerger.init(self.gpa, &self.reg_arena.allocator, &self.registry);
|
var merger = EnumFieldMerger.init(self.gpa, &self.reg_arena.allocator, &self.registry);
|
||||||
@@ -308,10 +147,37 @@ pub const Generator = struct {
|
|||||||
try merger.merge();
|
try merger.merge();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fixupTags(self: *Generator) !void {
|
// https://github.com/KhronosGroup/Vulkan-Docs/pull/1556
|
||||||
var fixer_upper = TagFixerUpper.init(self.gpa, &self.registry, &self.id_renderer);
|
fn fixupBitFlags(self: *Generator) !void {
|
||||||
defer fixer_upper.deinit();
|
var seen_bits = std.StringArrayHashMap(void).init(&self.reg_arena.allocator);
|
||||||
try fixer_upper.fixup();
|
defer seen_bits.deinit();
|
||||||
|
|
||||||
|
for (self.registry.decls) |decl| {
|
||||||
|
const bitmask = switch (decl.decl_type) {
|
||||||
|
.bitmask => |bm| bm,
|
||||||
|
else => continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (bitmask.bits_enum) |bits_enum| {
|
||||||
|
try seen_bits.put(bits_enum, {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var i: usize = 0;
|
||||||
|
|
||||||
|
for (self.registry.decls) |decl| {
|
||||||
|
switch (decl.decl_type) {
|
||||||
|
.enumeration => |e| {
|
||||||
|
if (e.is_bitmask and seen_bits.get(decl.name) == null)
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
self.registry.decls[i] = decl;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.registry.decls.len = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(self: *Generator, writer: anytype) !void {
|
fn render(self: *Generator, writer: anytype) !void {
|
||||||
@@ -330,9 +196,7 @@ pub fn generate(allocator: *Allocator, spec_xml: []const u8, writer: anytype) !v
|
|||||||
var gen = try Generator.init(allocator, spec.root);
|
var gen = try Generator.init(allocator, spec.root);
|
||||||
defer gen.deinit();
|
defer gen.deinit();
|
||||||
|
|
||||||
gen.removePromotedExtensions();
|
|
||||||
try gen.mergeEnumFields();
|
try gen.mergeEnumFields();
|
||||||
try gen.fixupBitmasks();
|
try gen.fixupBitFlags();
|
||||||
try gen.fixupTags();
|
|
||||||
try gen.render(writer);
|
try gen.render(writer);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ fn parseTypes(allocator: *Allocator, out: []registry.Declaration, types_elem: *x
|
|||||||
} else if (mem.eql(u8, category, "funcpointer")) {
|
} else if (mem.eql(u8, category, "funcpointer")) {
|
||||||
break :blk try parseFuncPointer(allocator, ty);
|
break :blk try parseFuncPointer(allocator, ty);
|
||||||
} else if (mem.eql(u8, category, "enum")) {
|
} else if (mem.eql(u8, category, "enum")) {
|
||||||
break :blk (try parseEnumAlias(allocator, ty)) orelse continue;
|
break :blk (try parseEnumAlias(ty)) orelse continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
@@ -95,7 +95,7 @@ fn parseForeigntype(ty: *xml.Element) !registry.Declaration {
|
|||||||
|
|
||||||
return registry.Declaration{
|
return registry.Declaration{
|
||||||
.name = name,
|
.name = name,
|
||||||
.decl_type = .{.foreign = .{.depends = depends}},
|
.decl_type = .{ .foreign = .{ .depends = depends } },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,12 +104,27 @@ fn parseBitmaskType(ty: *xml.Element) !registry.Declaration {
|
|||||||
const alias = ty.getAttribute("alias") orelse return error.InvalidRegistry;
|
const alias = ty.getAttribute("alias") orelse return error.InvalidRegistry;
|
||||||
return registry.Declaration{
|
return registry.Declaration{
|
||||||
.name = name,
|
.name = name,
|
||||||
.decl_type = .{.alias = .{.name = alias, .target = .other_type}},
|
.decl_type = .{ .alias = .{ .name = alias, .target = .other_type } },
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
|
const flags_type = ty.getCharData("type") orelse return error.InvalidRegistry;
|
||||||
|
|
||||||
|
const bitwidth: u8 = if (mem.eql(u8, flags_type, "VkFlags"))
|
||||||
|
32
|
||||||
|
else if (mem.eql(u8, flags_type, "VkFlags64"))
|
||||||
|
64
|
||||||
|
else
|
||||||
|
return error.InvalidRegistry;
|
||||||
|
|
||||||
return registry.Declaration{
|
return registry.Declaration{
|
||||||
.name = ty.getCharData("name") orelse return error.InvalidRegistry,
|
.name = ty.getCharData("name") orelse return error.InvalidRegistry,
|
||||||
.decl_type = .{.bitmask = .{.bits_enum = ty.getAttribute("requires")}},
|
.decl_type = .{
|
||||||
|
.bitmask = .{
|
||||||
|
// Who knows why these are different fields
|
||||||
|
.bits_enum = ty.getAttribute("requires") orelse ty.getAttribute("bitvalues"),
|
||||||
|
.bitwidth = bitwidth,
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,7 +135,9 @@ fn parseHandleType(ty: *xml.Element) !registry.Declaration {
|
|||||||
const alias = ty.getAttribute("alias") orelse return error.InvalidRegistry;
|
const alias = ty.getAttribute("alias") orelse return error.InvalidRegistry;
|
||||||
return registry.Declaration{
|
return registry.Declaration{
|
||||||
.name = name,
|
.name = name,
|
||||||
.decl_type = .{.alias = .{.name = alias, .target = .other_type}},
|
.decl_type = .{
|
||||||
|
.alias = .{ .name = alias, .target = .other_type },
|
||||||
|
},
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
const name = ty.getCharData("name") orelse return error.InvalidRegistry;
|
const name = ty.getCharData("name") orelse return error.InvalidRegistry;
|
||||||
@@ -136,7 +153,7 @@ fn parseHandleType(ty: *xml.Element) !registry.Declaration {
|
|||||||
.handle = .{
|
.handle = .{
|
||||||
.parent = ty.getAttribute("parent"),
|
.parent = ty.getAttribute("parent"),
|
||||||
.is_dispatchable = dispatchable,
|
.is_dispatchable = dispatchable,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -152,7 +169,7 @@ fn parseBaseType(allocator: *Allocator, ty: *xml.Element) !registry.Declaration
|
|||||||
// macros, which is why this part is not built into the xml/c parser.
|
// macros, which is why this part is not built into the xml/c parser.
|
||||||
return registry.Declaration{
|
return registry.Declaration{
|
||||||
.name = name,
|
.name = name,
|
||||||
.decl_type = .{.external = {}},
|
.decl_type = .{ .external = {} },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -163,7 +180,9 @@ fn parseContainer(allocator: *Allocator, ty: *xml.Element, is_union: bool) !regi
|
|||||||
if (ty.getAttribute("alias")) |alias| {
|
if (ty.getAttribute("alias")) |alias| {
|
||||||
return registry.Declaration{
|
return registry.Declaration{
|
||||||
.name = name,
|
.name = name,
|
||||||
.decl_type = .{.alias = .{.name = alias, .target = .other_type}},
|
.decl_type = .{
|
||||||
|
.alias = .{ .name = alias, .target = .other_type },
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,18 +208,18 @@ fn parseContainer(allocator: *Allocator, ty: *xml.Element, is_union: bool) !regi
|
|||||||
it = ty.findChildrenByTag("member");
|
it = ty.findChildrenByTag("member");
|
||||||
for (members) |*member| {
|
for (members) |*member| {
|
||||||
const member_elem = it.next().?;
|
const member_elem = it.next().?;
|
||||||
try parsePointerMeta(.{.container = members}, &member.field_type, member_elem);
|
try parsePointerMeta(.{ .container = members }, &member.field_type, member_elem);
|
||||||
}
|
}
|
||||||
|
|
||||||
return registry.Declaration {
|
return registry.Declaration{
|
||||||
.name = name,
|
.name = name,
|
||||||
.decl_type = .{
|
.decl_type = .{
|
||||||
.container = .{
|
.container = .{
|
||||||
.stype = maybe_stype,
|
.stype = maybe_stype,
|
||||||
.fields = members,
|
.fields = members,
|
||||||
.is_union = is_union,
|
.is_union = is_union,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,7 +242,7 @@ fn lenToPointerSize(fields: Fields, len: []const u8) registry.Pointer.PointerSiz
|
|||||||
for (params) |*param| {
|
for (params) |*param| {
|
||||||
if (mem.eql(u8, param.name, len)) {
|
if (mem.eql(u8, param.name, len)) {
|
||||||
param.is_buffer_len = true;
|
param.is_buffer_len = true;
|
||||||
return .{.other_field = param.name};
|
return .{ .other_field = param.name };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -231,7 +250,7 @@ fn lenToPointerSize(fields: Fields, len: []const u8) registry.Pointer.PointerSiz
|
|||||||
for (members) |*member| {
|
for (members) |*member| {
|
||||||
if (mem.eql(u8, member.name, len)) {
|
if (mem.eql(u8, member.name, len)) {
|
||||||
member.is_buffer_len = true;
|
member.is_buffer_len = true;
|
||||||
return .{.other_field = member.name};
|
return .{ .other_field = member.name };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -278,12 +297,14 @@ fn parsePointerMeta(fields: Fields, type_info: *registry.TypeInfo, elem: *xml.El
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parseEnumAlias(allocator: *Allocator, elem: *xml.Element) !?registry.Declaration {
|
fn parseEnumAlias(elem: *xml.Element) !?registry.Declaration {
|
||||||
if (elem.getAttribute("alias")) |alias| {
|
if (elem.getAttribute("alias")) |alias| {
|
||||||
const name = elem.getAttribute("name") orelse return error.InvalidRegistry;
|
const name = elem.getAttribute("name") orelse return error.InvalidRegistry;
|
||||||
return registry.Declaration{
|
return registry.Declaration{
|
||||||
.name = name,
|
.name = name,
|
||||||
.decl_type = .{.alias = .{.name = alias, .target = .other_type}},
|
.decl_type = .{
|
||||||
|
.alias = .{ .name = alias, .target = .other_type },
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,7 +322,7 @@ fn parseEnums(allocator: *Allocator, out: []registry.Declaration, root: *xml.Ele
|
|||||||
|
|
||||||
out[i] = .{
|
out[i] = .{
|
||||||
.name = name,
|
.name = name,
|
||||||
.decl_type = .{.enumeration = try parseEnumFields(allocator, enums)},
|
.decl_type = .{ .enumeration = try parseEnumFields(allocator, enums) },
|
||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
@@ -317,6 +338,11 @@ fn parseEnumFields(allocator: *Allocator, elem: *xml.Element) !registry.Enum {
|
|||||||
return error.InvalidRegistry;
|
return error.InvalidRegistry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bitwidth = if (elem.getAttribute("bitwidth")) |bitwidth|
|
||||||
|
try std.fmt.parseInt(u8, bitwidth, 10)
|
||||||
|
else
|
||||||
|
32;
|
||||||
|
|
||||||
const fields = try allocator.alloc(registry.Enum.Field, elem.children.items.len);
|
const fields = try allocator.alloc(registry.Enum.Field, elem.children.items.len);
|
||||||
|
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
@@ -328,6 +354,7 @@ fn parseEnumFields(allocator: *Allocator, elem: *xml.Element) !registry.Enum {
|
|||||||
|
|
||||||
return registry.Enum{
|
return registry.Enum{
|
||||||
.fields = allocator.shrink(fields, i),
|
.fields = allocator.shrink(fields, i),
|
||||||
|
.bitwidth = bitwidth,
|
||||||
.is_bitmask = is_bitmask,
|
.is_bitmask = is_bitmask,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -351,14 +378,14 @@ fn parseEnumField(field: *xml.Element) !registry.Enum.Field {
|
|||||||
// tag. In the latter case its passed via the `ext_nr` parameter.
|
// tag. In the latter case its passed via the `ext_nr` parameter.
|
||||||
if (field.getAttribute("value")) |value| {
|
if (field.getAttribute("value")) |value| {
|
||||||
if (mem.startsWith(u8, value, "0x")) {
|
if (mem.startsWith(u8, value, "0x")) {
|
||||||
break :blk .{.bit_vector = try std.fmt.parseInt(i32, value[2..], 16)};
|
break :blk .{ .bit_vector = try std.fmt.parseInt(i32, value[2..], 16) };
|
||||||
} else {
|
} else {
|
||||||
break :blk .{.int = try std.fmt.parseInt(i32, value, 10)};
|
break :blk .{ .int = try std.fmt.parseInt(i32, value, 10) };
|
||||||
}
|
}
|
||||||
} else if (field.getAttribute("bitpos")) |bitpos| {
|
} else if (field.getAttribute("bitpos")) |bitpos| {
|
||||||
break :blk .{.bitpos = try std.fmt.parseInt(u5, bitpos, 10)};
|
break :blk .{ .bitpos = try std.fmt.parseInt(u6, bitpos, 10) };
|
||||||
} else if (field.getAttribute("alias")) |alias| {
|
} else if (field.getAttribute("alias")) |alias| {
|
||||||
break :blk .{.alias = .{.name = alias, .is_compat_alias = is_compat_alias}};
|
break :blk .{ .alias = .{ .name = alias, .is_compat_alias = is_compat_alias } };
|
||||||
} else {
|
} else {
|
||||||
return error.InvalidRegistry;
|
return error.InvalidRegistry;
|
||||||
}
|
}
|
||||||
@@ -401,7 +428,9 @@ fn parseCommand(allocator: *Allocator, elem: *xml.Element) !registry.Declaration
|
|||||||
const name = elem.getAttribute("name") orelse return error.InvalidRegistry;
|
const name = elem.getAttribute("name") orelse return error.InvalidRegistry;
|
||||||
return registry.Declaration{
|
return registry.Declaration{
|
||||||
.name = name,
|
.name = name,
|
||||||
.decl_type = .{.alias = .{.name = alias, .target = .other_command}}
|
.decl_type = .{
|
||||||
|
.alias = .{ .name = alias, .target = .other_command },
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -442,10 +471,10 @@ fn parseCommand(allocator: *Allocator, elem: *xml.Element) !registry.Declaration
|
|||||||
it = elem.findChildrenByTag("param");
|
it = elem.findChildrenByTag("param");
|
||||||
for (params) |*param| {
|
for (params) |*param| {
|
||||||
const param_elem = it.next().?;
|
const param_elem = it.next().?;
|
||||||
try parsePointerMeta(.{.command = params}, ¶m.param_type, param_elem);
|
try parsePointerMeta(.{ .command = params }, ¶m.param_type, param_elem);
|
||||||
}
|
}
|
||||||
|
|
||||||
return registry.Declaration {
|
return registry.Declaration{
|
||||||
.name = command_decl.name,
|
.name = command_decl.name,
|
||||||
.decl_type = .{
|
.decl_type = .{
|
||||||
.command = .{
|
.command = .{
|
||||||
@@ -453,8 +482,8 @@ fn parseCommand(allocator: *Allocator, elem: *xml.Element) !registry.Declaration
|
|||||||
.return_type = return_type,
|
.return_type = return_type,
|
||||||
.success_codes = success_codes,
|
.success_codes = success_codes,
|
||||||
.error_codes = error_codes,
|
.error_codes = error_codes,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -499,7 +528,7 @@ fn parseApiConstants(allocator: *Allocator, root: *xml.Element) ![]registry.ApiC
|
|||||||
|
|
||||||
constants[i] = .{
|
constants[i] = .{
|
||||||
.name = constant.getAttribute("name") orelse return error.InvalidRegistry,
|
.name = constant.getAttribute("name") orelse return error.InvalidRegistry,
|
||||||
.value = .{.expr = expr},
|
.value = .{ .expr = expr },
|
||||||
};
|
};
|
||||||
|
|
||||||
i += 1;
|
i += 1;
|
||||||
@@ -522,15 +551,13 @@ fn parseDefines(types: *xml.Element, out: []registry.ApiConstant) !usize {
|
|||||||
if (mem.eql(u8, name, "VK_HEADER_VERSION")) {
|
if (mem.eql(u8, name, "VK_HEADER_VERSION")) {
|
||||||
out[i] = .{
|
out[i] = .{
|
||||||
.name = name,
|
.name = name,
|
||||||
.value = .{.expr = mem.trim(u8, ty.children.items[2].CharData, " ")},
|
.value = .{ .expr = mem.trim(u8, ty.children.items[2].CharData, " ") },
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
var xctok = cparse.XmlCTokenizer.init(ty);
|
var xctok = cparse.XmlCTokenizer.init(ty);
|
||||||
out[i] = .{
|
out[i] = .{
|
||||||
.name = name,
|
.name = name,
|
||||||
.value = .{
|
.value = .{ .version = cparse.parseVersion(&xctok) catch continue },
|
||||||
.version = cparse.parseVersion(&xctok) catch continue
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
i += 1;
|
i += 1;
|
||||||
@@ -591,7 +618,7 @@ fn parseFeature(allocator: *Allocator, feature: *xml.Element) !registry.Feature
|
|||||||
return registry.Feature{
|
return registry.Feature{
|
||||||
.name = name,
|
.name = name,
|
||||||
.level = feature_level,
|
.level = feature_level,
|
||||||
.requires = allocator.shrink(requires, i)
|
.requires = allocator.shrink(requires, i),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -624,7 +651,10 @@ fn parseEnumExtension(elem: *xml.Element, parent_extnumber: ?u31) !?registry.Req
|
|||||||
return registry.Require.EnumExtension{
|
return registry.Require.EnumExtension{
|
||||||
.extends = extends,
|
.extends = extends,
|
||||||
.extnumber = actual_extnumber,
|
.extnumber = actual_extnumber,
|
||||||
.field = .{.name = name, .value = .{.int = value}},
|
.field = .{
|
||||||
|
.name = name,
|
||||||
|
.value = .{ .int = value },
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -687,7 +717,7 @@ fn parseRequire(allocator: *Allocator, require: *xml.Element, extnumber: ?u31) !
|
|||||||
return error.InvalidRegistry;
|
return error.InvalidRegistry;
|
||||||
}
|
}
|
||||||
|
|
||||||
break :blk try splitFeatureLevel(feature_level["VK_VERSION_".len ..], "_");
|
break :blk try splitFeatureLevel(feature_level["VK_VERSION_".len..], "_");
|
||||||
};
|
};
|
||||||
|
|
||||||
return registry.Require{
|
return registry.Require{
|
||||||
@@ -752,12 +782,11 @@ fn parseExtension(allocator: *Allocator, extension: *xml.Element) !registry.Exte
|
|||||||
const promoted_to: registry.Extension.Promotion = blk: {
|
const promoted_to: registry.Extension.Promotion = blk: {
|
||||||
const promotedto = extension.getAttribute("promotedto") orelse break :blk .none;
|
const promotedto = extension.getAttribute("promotedto") orelse break :blk .none;
|
||||||
if (mem.startsWith(u8, promotedto, "VK_VERSION_")) {
|
if (mem.startsWith(u8, promotedto, "VK_VERSION_")) {
|
||||||
const feature_level = try splitFeatureLevel(promotedto["VK_VERSION_".len ..], "_");
|
const feature_level = try splitFeatureLevel(promotedto["VK_VERSION_".len..], "_");
|
||||||
|
break :blk .{ .feature = feature_level };
|
||||||
break :blk .{.feature = feature_level};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break :blk .{.extension = promotedto};
|
break :blk .{ .extension = promotedto };
|
||||||
};
|
};
|
||||||
|
|
||||||
const number = blk: {
|
const number = blk: {
|
||||||
@@ -798,7 +827,7 @@ fn parseExtension(allocator: *Allocator, extension: *xml.Element) !registry.Exte
|
|||||||
.promoted_to = promoted_to,
|
.promoted_to = promoted_to,
|
||||||
.platform = platform,
|
.platform = platform,
|
||||||
.required_feature_level = requires_core,
|
.required_feature_level = requires_core,
|
||||||
.requires = allocator.shrink(requires, i)
|
.requires = allocator.shrink(requires, i),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ pub const Alias = struct {
|
|||||||
pub const ApiConstant = struct {
|
pub const ApiConstant = struct {
|
||||||
pub const Value = union(enum) {
|
pub const Value = union(enum) {
|
||||||
expr: []const u8,
|
expr: []const u8,
|
||||||
version: [3][]const u8,
|
version: [4][]const u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
@@ -71,13 +71,13 @@ pub const Container = struct {
|
|||||||
|
|
||||||
pub const Enum = struct {
|
pub const Enum = struct {
|
||||||
pub const Value = union(enum) {
|
pub const Value = union(enum) {
|
||||||
bitpos: u5, // 1 << bitpos
|
bitpos: u6, // 1 << bitpos
|
||||||
bit_vector: i32, // Combined flags & some vendor IDs
|
bit_vector: i32, // Combined flags & some vendor IDs
|
||||||
int: i32,
|
int: i32,
|
||||||
alias: struct {
|
alias: struct {
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
is_compat_alias: bool,
|
is_compat_alias: bool,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Field = struct {
|
pub const Field = struct {
|
||||||
@@ -86,11 +86,13 @@ pub const Enum = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fields: []Field,
|
fields: []Field,
|
||||||
|
bitwidth: u8,
|
||||||
is_bitmask: bool,
|
is_bitmask: bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Bitmask = struct {
|
pub const Bitmask = struct {
|
||||||
bits_enum: ?[]const u8,
|
bits_enum: ?[]const u8,
|
||||||
|
bitwidth: u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Handle = struct {
|
pub const Handle = struct {
|
||||||
@@ -116,7 +118,7 @@ pub const Pointer = struct {
|
|||||||
one,
|
one,
|
||||||
many, // The length is given by some complex expression, possibly involving another field
|
many, // The length is given by some complex expression, possibly involving another field
|
||||||
other_field: []const u8, // The length is given by some other field or parameter
|
other_field: []const u8, // The length is given by some other field or parameter
|
||||||
zero_terminated
|
zero_terminated,
|
||||||
};
|
};
|
||||||
|
|
||||||
is_const: bool,
|
is_const: bool,
|
||||||
|
|||||||
@@ -9,14 +9,17 @@ const IdRenderer = id_render.IdRenderer;
|
|||||||
|
|
||||||
const preamble =
|
const preamble =
|
||||||
\\
|
\\
|
||||||
\\// This file is generated from the Khronos Vulkan XML API registry
|
\\// This file is generated from the Khronos Vulkan XML API registry by vulkan-zig.
|
||||||
\\
|
\\
|
||||||
\\const std = @import("std");
|
\\const std = @import("std");
|
||||||
\\const builtin = @import("builtin");
|
\\const builtin = @import("builtin");
|
||||||
\\const root = @import("root");
|
\\const root = @import("root");
|
||||||
\\pub const vulkan_call_conv: builtin.CallingConvention = if (builtin.os.tag == .windows and builtin.cpu.arch == .i386)
|
\\
|
||||||
|
\\const GlobalScope = @This();
|
||||||
|
\\
|
||||||
|
\\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 builtin.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)
|
||||||
\\ // On Android 32-bit ARM targets, Vulkan functions use the "hardfloat"
|
\\ // On Android 32-bit ARM targets, Vulkan functions use the "hardfloat"
|
||||||
\\ // calling convention, i.e. float parameters are passed in registers. This
|
\\ // calling convention, i.e. float parameters are passed in registers. This
|
||||||
\\ // is true even if the rest of the application passes floats on the stack,
|
\\ // is true even if the rest of the application passes floats on the stack,
|
||||||
@@ -24,9 +27,9 @@ const preamble =
|
|||||||
\\ .AAPCSVFP
|
\\ .AAPCSVFP
|
||||||
\\ else
|
\\ else
|
||||||
\\ .C;
|
\\ .C;
|
||||||
\\pub fn FlagsMixin(comptime FlagsType: type) type {
|
\\pub fn FlagsMixin(comptime FlagsType: type, comptime Int: type) type {
|
||||||
\\ return struct {
|
\\ return struct {
|
||||||
\\ pub const IntType = Flags;
|
\\ pub const IntType = Int;
|
||||||
\\ pub fn toInt(self: FlagsType) IntType {
|
\\ pub fn toInt(self: FlagsType) IntType {
|
||||||
\\ return @bitCast(IntType, self);
|
\\ return @bitCast(IntType, self);
|
||||||
\\ }
|
\\ }
|
||||||
@@ -40,7 +43,7 @@ const preamble =
|
|||||||
\\ return fromInt(toInt(lhs) & toInt(rhs));
|
\\ return fromInt(toInt(lhs) & toInt(rhs));
|
||||||
\\ }
|
\\ }
|
||||||
\\ pub fn complement(self: FlagsType) FlagsType {
|
\\ pub fn complement(self: FlagsType) FlagsType {
|
||||||
\\ return fromInt(~toInt(lhs));
|
\\ return fromInt(~toInt(self));
|
||||||
\\ }
|
\\ }
|
||||||
\\ pub fn subtract(lhs: FlagsType, rhs: FlagsType) FlagsType {
|
\\ pub fn subtract(lhs: FlagsType, rhs: FlagsType) FlagsType {
|
||||||
\\ return fromInt(toInt(lhs) & toInt(rhs.complement()));
|
\\ return fromInt(toInt(lhs) & toInt(rhs.complement()));
|
||||||
@@ -50,54 +53,61 @@ const preamble =
|
|||||||
\\ }
|
\\ }
|
||||||
\\ };
|
\\ };
|
||||||
\\}
|
\\}
|
||||||
\\pub fn makeVersion(major: u10, minor: u10, patch: u12) u32 {
|
\\pub fn makeApiVersion(variant: u3, major: u7, minor: u10, patch: u12) u32 {
|
||||||
\\ return (@as(u32, major) << 22) | (@as(u32, minor) << 12) | patch;
|
\\ return (@as(u32, variant) << 29) | (@as(u32, major) << 22) | (@as(u32, minor) << 12) | patch;
|
||||||
\\}
|
\\}
|
||||||
\\pub fn versionMajor(version: u32) u10 {
|
\\pub fn apiVersionVariant(version: u32) u3 {
|
||||||
\\ return @truncate(u10, version >> 22);
|
\\ return @truncate(u3, version >> 29);
|
||||||
\\}
|
\\}
|
||||||
\\pub fn versionMinor(version: u32) u10 {
|
\\pub fn apiVersionMajor(version: u32) u7 {
|
||||||
|
\\ return @truncate(u7, version >> 22);
|
||||||
|
\\}
|
||||||
|
\\pub fn apiVersionMinor(version: u32) u10 {
|
||||||
\\ return @truncate(u10, version >> 12);
|
\\ return @truncate(u10, version >> 12);
|
||||||
\\}
|
\\}
|
||||||
\\pub fn versionPatch(version: u32) u12 {
|
\\pub fn apiVersionPatch(version: u32) u12 {
|
||||||
\\ return @truncate(u12, version);
|
\\ return @truncate(u12, version);
|
||||||
\\}
|
\\}
|
||||||
\\
|
\\
|
||||||
;
|
;
|
||||||
|
|
||||||
const builtin_types = std.ComptimeStringMap([]const u8, .{
|
const builtin_types = std.ComptimeStringMap([]const u8, .{
|
||||||
.{"void", @typeName(void)},
|
.{ "void", @typeName(void) },
|
||||||
.{"char", @typeName(u8)},
|
.{ "char", @typeName(u8) },
|
||||||
.{"float", @typeName(f32)},
|
.{ "float", @typeName(f32) },
|
||||||
.{"double", @typeName(f64)},
|
.{ "double", @typeName(f64) },
|
||||||
.{"uint8_t", @typeName(u8)},
|
.{ "uint8_t", @typeName(u8) },
|
||||||
.{"uint16_t", @typeName(u16)},
|
.{ "uint16_t", @typeName(u16) },
|
||||||
.{"uint32_t", @typeName(u32)},
|
.{ "uint32_t", @typeName(u32) },
|
||||||
.{"uint64_t", @typeName(u64)},
|
.{ "uint64_t", @typeName(u64) },
|
||||||
.{"int32_t", @typeName(i32)},
|
.{ "int8_t", @typeName(i8) },
|
||||||
.{"int64_t", @typeName(i64)},
|
.{ "int16_t", @typeName(i16) },
|
||||||
.{"size_t", @typeName(usize)},
|
.{ "int32_t", @typeName(i32) },
|
||||||
.{"int", @typeName(c_int)},
|
.{ "int64_t", @typeName(i64) },
|
||||||
|
.{ "size_t", @typeName(usize) },
|
||||||
|
.{ "int", @typeName(c_int) },
|
||||||
});
|
});
|
||||||
|
|
||||||
const foreign_types = std.ComptimeStringMap([]const u8, .{
|
const foreign_types = std.ComptimeStringMap([]const u8, .{
|
||||||
.{"Display", "opaque {}"},
|
.{ "Display", "opaque {}" },
|
||||||
.{"VisualID", @typeName(c_uint)},
|
.{ "VisualID", @typeName(c_uint) },
|
||||||
.{"Window", @typeName(c_ulong)},
|
.{ "Window", @typeName(c_ulong) },
|
||||||
.{"RROutput", @typeName(c_ulong)},
|
.{ "RROutput", @typeName(c_ulong) },
|
||||||
.{"wl_display", "opaque {}"},
|
.{ "wl_display", "opaque {}" },
|
||||||
.{"wl_surface", "opaque {}"},
|
.{ "wl_surface", "opaque {}" },
|
||||||
.{"HINSTANCE", "std.os.HINSTANCE"},
|
.{ "HINSTANCE", "std.os.HINSTANCE" },
|
||||||
.{"HWND", "*opaque {}"},
|
.{ "HWND", "*opaque {}" },
|
||||||
.{"HMONITOR", "*opaque {}"},
|
.{ "HMONITOR", "*opaque {}" },
|
||||||
.{"HANDLE", "std.os.HANDLE"},
|
.{ "HANDLE", "std.os.HANDLE" },
|
||||||
.{"SECURITY_ATTRIBUTES", "std.os.SECURITY_ATTRIBUTES"},
|
.{ "SECURITY_ATTRIBUTES", "std.os.SECURITY_ATTRIBUTES" },
|
||||||
.{"DWORD", "std.os.DWORD"},
|
.{ "DWORD", "std.os.DWORD" },
|
||||||
.{"LPCWSTR", "std.os.LPCWSTR"},
|
.{ "LPCWSTR", "std.os.LPCWSTR" },
|
||||||
.{"xcb_connection_t", "opaque {}"},
|
.{ "xcb_connection_t", "opaque {}" },
|
||||||
.{"xcb_visualid_t", @typeName(u32)},
|
.{ "xcb_visualid_t", @typeName(u32) },
|
||||||
.{"xcb_window_t", @typeName(u32)},
|
.{ "xcb_window_t", @typeName(u32) },
|
||||||
.{"zx_handle_t", @typeName(u32)},
|
.{ "zx_handle_t", @typeName(u32) },
|
||||||
|
.{ "_screen_context", "opaque {}" },
|
||||||
|
.{ "_screen_window", "opaque {}" },
|
||||||
});
|
});
|
||||||
|
|
||||||
fn eqlIgnoreCase(lhs: []const u8, rhs: []const u8) bool {
|
fn eqlIgnoreCase(lhs: []const u8, rhs: []const u8) bool {
|
||||||
@@ -115,7 +125,7 @@ fn eqlIgnoreCase(lhs: []const u8, rhs: []const u8) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn trimVkNamespace(id: []const u8) []const u8 {
|
pub fn trimVkNamespace(id: []const u8) []const u8 {
|
||||||
const prefixes = [_][]const u8{"VK_", "vk", "Vk", "PFN_vk"};
|
const prefixes = [_][]const u8{ "VK_", "vk", "Vk", "PFN_vk" };
|
||||||
for (prefixes) |prefix| {
|
for (prefixes) |prefix| {
|
||||||
if (mem.startsWith(u8, id, prefix)) {
|
if (mem.startsWith(u8, id, prefix)) {
|
||||||
return id[prefix.len..];
|
return id[prefix.len..];
|
||||||
@@ -129,15 +139,16 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
return struct {
|
return struct {
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
const WriteError = WriterType.Error;
|
const WriteError = WriterType.Error;
|
||||||
const RenderTypeInfoError = WriteError || error {
|
const RenderTypeInfoError = WriteError || std.fmt.ParseIntError || error{ OutOfMemory, InvalidRegistry };
|
||||||
OutOfMemory,
|
|
||||||
};
|
|
||||||
|
|
||||||
const BitflagName = struct {
|
const BitflagName = struct {
|
||||||
/// Name without FlagBits, so VkSurfaceTransformFlagBitsKHR
|
/// Name without FlagBits, so VkSurfaceTransformFlagBitsKHR
|
||||||
/// becomes VkSurfaceTransform
|
/// becomes VkSurfaceTransform
|
||||||
base_name: []const u8,
|
base_name: []const u8,
|
||||||
|
|
||||||
|
/// Optional flag bits revision, used in places like VkAccessFlagBits2KHR
|
||||||
|
revision: ?[]const u8,
|
||||||
|
|
||||||
/// Optional tag of the flag
|
/// Optional tag of the flag
|
||||||
tag: ?[]const u8,
|
tag: ?[]const u8,
|
||||||
};
|
};
|
||||||
@@ -187,7 +198,7 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
return error.InvalidRegistry;
|
return error.InvalidRegistry;
|
||||||
}
|
}
|
||||||
|
|
||||||
result.entry.value = &decl.decl_type;
|
result.value_ptr.* = &decl.decl_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Self{
|
return Self{
|
||||||
@@ -206,7 +217,7 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn writeIdentifier(self: Self, id: []const u8) !void {
|
fn writeIdentifier(self: Self, id: []const u8) !void {
|
||||||
try self.id_renderer.render(self.writer, id);
|
try id_render.writeIdentifier(self.writer, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn writeIdentifierWithCase(self: *Self, case: CaseStyle, id: []const u8) !void {
|
fn writeIdentifierWithCase(self: *Self, case: CaseStyle, id: []const u8) !void {
|
||||||
@@ -225,8 +236,8 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const rest = field_it.rest();
|
const rest = field_it.rest();
|
||||||
|
const field_segment = field_it.next() orelse return error.InvalidRegistry;
|
||||||
const enum_segment = enum_it.next() orelse return rest;
|
const enum_segment = enum_it.next() orelse return rest;
|
||||||
const field_segment = field_it.next() orelse return error.FieldNameEqualsEnumName;
|
|
||||||
|
|
||||||
if (!eqlIgnoreCase(enum_segment, field_segment)) {
|
if (!eqlIgnoreCase(enum_segment, field_segment)) {
|
||||||
return rest;
|
return rest;
|
||||||
@@ -234,16 +245,48 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extractBitflagName(self: Self, name: []const u8) ?BitflagName {
|
fn extractBitflagFieldName(bitflag_name: BitflagName, field_name: []const u8) ![]const u8 {
|
||||||
const tag = self.id_renderer.getAuthorTag(name);
|
var flag_it = id_render.SegmentIterator.init(bitflag_name.base_name);
|
||||||
const base_name = if (tag) |tag_name| name[0 .. name.len - tag_name.len] else name;
|
var field_it = id_render.SegmentIterator.init(field_name);
|
||||||
|
|
||||||
if (!mem.endsWith(u8, base_name, "FlagBits")) {
|
while (true) {
|
||||||
|
const rest = field_it.rest();
|
||||||
|
const field_segment = field_it.next() orelse return error.InvalidRegistry;
|
||||||
|
const flag_segment = flag_it.next() orelse {
|
||||||
|
if (bitflag_name.revision) |revision| {
|
||||||
|
if (mem.eql(u8, revision, field_segment))
|
||||||
|
return field_it.rest();
|
||||||
|
}
|
||||||
|
|
||||||
|
return rest;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!eqlIgnoreCase(flag_segment, field_segment)) {
|
||||||
|
return rest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extractBitflagName(self: Self, name: []const u8) !?BitflagName {
|
||||||
|
const tag = self.id_renderer.getAuthorTag(name);
|
||||||
|
const tagless_name = if (tag) |tag_name| name[0 .. name.len - tag_name.len] else name;
|
||||||
|
|
||||||
|
// Strip out the "version" number of a bitflag, like VkAccessFlagBits2KHR.
|
||||||
|
const base_name = std.mem.trimRight(u8, tagless_name, "0123456789");
|
||||||
|
|
||||||
|
const maybe_flag_bits_index = mem.lastIndexOf(u8, base_name, "FlagBits");
|
||||||
|
if (maybe_flag_bits_index == null) {
|
||||||
return null;
|
return null;
|
||||||
|
} else if (maybe_flag_bits_index != base_name.len - "FlagBits".len) {
|
||||||
|
// It is unlikely that a type that is not a flag bit would contain FlagBits,
|
||||||
|
// and more likely that we have missed something if FlagBits isn't the last
|
||||||
|
// part of base_name
|
||||||
|
return error.InvalidRegistry;
|
||||||
}
|
}
|
||||||
|
|
||||||
return BitflagName{
|
return BitflagName{
|
||||||
.base_name = base_name[0 .. base_name.len - "FlagBits".len],
|
.base_name = base_name[0 .. base_name.len - "FlagBits".len],
|
||||||
|
.revision = if (base_name.len != tagless_name.len) tagless_name[base_name.len..] else null,
|
||||||
.tag = tag,
|
.tag = tag,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -255,16 +298,6 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
return mem.endsWith(u8, base_name, "Flags");
|
return mem.endsWith(u8, base_name, "Flags");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn containerHasField(self: Self, container: *const reg.Container, field_name: []const u8) bool {
|
|
||||||
for (container.fields) |field| {
|
|
||||||
if (mem.eql(u8, field, field_name)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn isInOutPointer(self: Self, ptr: reg.Pointer) !bool {
|
fn isInOutPointer(self: Self, ptr: reg.Pointer) !bool {
|
||||||
if (ptr.child.* != .name) {
|
if (ptr.child.* != .name) {
|
||||||
return false;
|
return false;
|
||||||
@@ -333,7 +366,7 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
.name => |name| {
|
.name => |name| {
|
||||||
if (self.extractBitflagName(param.param_type.name) != null or self.isFlags(param.param_type.name)) {
|
if ((try self.extractBitflagName(name)) != null or self.isFlags(name)) {
|
||||||
return .bitflags;
|
return .bitflags;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -347,20 +380,20 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
return .other;
|
return .other;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn classifyCommandDispatch(self: Self, name: []const u8, command: reg.Command) CommandDispatchType {
|
fn classifyCommandDispatch(name: []const u8, command: reg.Command) CommandDispatchType {
|
||||||
const device_handles = std.ComptimeStringMap(void, .{
|
const device_handles = std.ComptimeStringMap(void, .{
|
||||||
.{"VkDevice", {}},
|
.{ "VkDevice", {} },
|
||||||
.{"VkCommandBuffer", {}},
|
.{ "VkCommandBuffer", {} },
|
||||||
.{"VkQueue", {}},
|
.{ "VkQueue", {} },
|
||||||
});
|
});
|
||||||
|
|
||||||
const override_functions = std.ComptimeStringMap(CommandDispatchType, .{
|
const override_functions = std.ComptimeStringMap(CommandDispatchType, .{
|
||||||
.{"vkGetInstanceProcAddr", .base},
|
.{ "vkGetInstanceProcAddr", .base },
|
||||||
.{"vkCreateInstance", .base},
|
.{ "vkCreateInstance", .base },
|
||||||
.{"vkEnumerateInstanceLayerProperties", .base},
|
.{ "vkEnumerateInstanceLayerProperties", .base },
|
||||||
.{"vkEnumerateInstanceExtensionProperties", .base},
|
.{ "vkEnumerateInstanceExtensionProperties", .base },
|
||||||
.{"vkEnumerateInstanceVersion", .base},
|
.{ "vkEnumerateInstanceVersion", .base },
|
||||||
.{"vkGetDeviceProcAddr", .instance},
|
.{ "vkGetDeviceProcAddr", .instance },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (override_functions.get(name)) |dispatch_type| {
|
if (override_functions.get(name)) |dispatch_type| {
|
||||||
@@ -382,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);
|
||||||
@@ -396,6 +430,37 @@ 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("pub 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.writer.print("{s},\n", .{trimVkNamespace(decl.name)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try self.writer.writeAll("};\n");
|
||||||
|
try self.writer.print(
|
||||||
|
\\fn {s}CommandToString(cmd: {s}Command) []const u8 {{
|
||||||
|
\\ return std.meta.tagName(cmd);
|
||||||
|
\\}}
|
||||||
|
, .{ dispatch_type_name, dispatch_type_name });
|
||||||
|
}
|
||||||
|
|
||||||
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| {
|
||||||
@@ -411,7 +476,7 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
switch (api_constant.value) {
|
switch (api_constant.value) {
|
||||||
.expr => |expr| try self.renderApiConstantExpr(expr),
|
.expr => |expr| try self.renderApiConstantExpr(expr),
|
||||||
.version => |version| {
|
.version => |version| {
|
||||||
try self.writer.writeAll("makeVersion(");
|
try self.writer.writeAll("makeApiVersion(");
|
||||||
for (version) |part, i| {
|
for (version) |part, i| {
|
||||||
if (i != 0) {
|
if (i != 0) {
|
||||||
try self.writer.writeAll(", ");
|
try self.writer.writeAll(", ");
|
||||||
@@ -431,7 +496,7 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
else
|
else
|
||||||
expr;
|
expr;
|
||||||
|
|
||||||
var tokenizer = cparse.CTokenizer{.source = adjusted_expr};
|
var tokenizer = cparse.CTokenizer{ .source = adjusted_expr };
|
||||||
var peeked: ?cparse.Token = null;
|
var peeked: ?cparse.Token = null;
|
||||||
while (true) {
|
while (true) {
|
||||||
const tok = peeked orelse (try tokenizer.next()) orelse break;
|
const tok = peeked orelse (try tokenizer.next()) orelse break;
|
||||||
@@ -467,10 +532,10 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
},
|
},
|
||||||
.dot => {
|
.dot => {
|
||||||
const decimal = (try tokenizer.next()) orelse return error.InvalidConstantExpr;
|
const decimal = (try tokenizer.next()) orelse return error.InvalidConstantExpr;
|
||||||
try self.writer.print("@as(f32, {s}.{s})", .{tok.text, decimal.text});
|
try self.writer.print("@as(f32, {s}.{s})", .{ tok.text, decimal.text });
|
||||||
|
|
||||||
const f = (try tokenizer.next()) orelse return error.InvalidConstantExpr;
|
const f = (try tokenizer.next()) orelse return error.InvalidConstantExpr;
|
||||||
if (f.kind != .id or !mem.eql(u8, f.text, "f")) {
|
if (f.kind != .id or f.text.len != 1 or (f.text[0] != 'f' and f.text[0] != 'F')) {
|
||||||
return error.InvalidApiConstant;
|
return error.InvalidApiConstant;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -495,10 +560,11 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
if (builtin_types.get(name)) |zig_name| {
|
if (builtin_types.get(name)) |zig_name| {
|
||||||
try self.writer.writeAll(zig_name);
|
try self.writer.writeAll(zig_name);
|
||||||
return;
|
return;
|
||||||
} else if (self.extractBitflagName(name)) |bitflag_name| {
|
} else if (try self.extractBitflagName(name)) |bitflag_name| {
|
||||||
try self.writeIdentifierFmt("{s}Flags{s}", .{
|
try self.writeIdentifierFmt("{s}Flags{s}{s}", .{
|
||||||
trimVkNamespace(bitflag_name.base_name),
|
trimVkNamespace(bitflag_name.base_name),
|
||||||
@as([]const u8, if (bitflag_name.tag) |tag| tag else "")
|
@as([]const u8, if (bitflag_name.revision) |revision| revision else ""),
|
||||||
|
@as([]const u8, if (bitflag_name.tag) |tag| tag else ""),
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
} else if (mem.startsWith(u8, name, "vk")) {
|
} else if (mem.startsWith(u8, name, "vk")) {
|
||||||
@@ -534,10 +600,11 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
|
|
||||||
blk: {
|
blk: {
|
||||||
if (param.param_type == .name) {
|
if (param.param_type == .name) {
|
||||||
if (self.extractBitflagName(param.param_type.name)) |bitflag_name| {
|
if (try self.extractBitflagName(param.param_type.name)) |bitflag_name| {
|
||||||
try self.writeIdentifierFmt("{s}Flags{s}", .{
|
try self.writeIdentifierFmt("{s}Flags{s}{s}", .{
|
||||||
trimVkNamespace(bitflag_name.base_name),
|
trimVkNamespace(bitflag_name.base_name),
|
||||||
@as([]const u8, if (bitflag_name.tag) |tag| tag else "")
|
@as([]const u8, if (bitflag_name.revision) |revision| revision else ""),
|
||||||
|
@as([]const u8, if (bitflag_name.tag) |tag| tag else ""),
|
||||||
});
|
});
|
||||||
try self.writer.writeAll(".IntType");
|
try self.writer.writeAll(".IntType");
|
||||||
break :blk;
|
break :blk;
|
||||||
@@ -660,7 +727,7 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try self.writer.writeAll(" = .");
|
try self.writer.writeAll(" = .");
|
||||||
try self.writeIdentifierWithCase(.snake, stype["VK_STRUCTURE_TYPE_".len ..]);
|
try self.writeIdentifierWithCase(.snake, stype["VK_STRUCTURE_TYPE_".len..]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -676,70 +743,90 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
|
|
||||||
try self.writer.writeAll("pub const ");
|
try self.writer.writeAll("pub const ");
|
||||||
try self.renderName(name);
|
try self.renderName(name);
|
||||||
try self.writer.writeAll(" = extern enum {");
|
try self.writer.writeAll(" = enum(c_int) {");
|
||||||
|
|
||||||
for (enumeration.fields) |field| {
|
for (enumeration.fields) |field| {
|
||||||
if (field.value == .alias) {
|
if (field.value == .alias)
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
try self.renderEnumFieldName(name, field.name);
|
try self.renderEnumFieldName(name, field.name);
|
||||||
|
|
||||||
switch (field.value) {
|
switch (field.value) {
|
||||||
.int => |int| try self.writer.print(" = {}, ", .{int}),
|
.int => |int| try self.writer.print(" = {}, ", .{int}),
|
||||||
.bitpos => |pos| try self.writer.print(" = 1 << {}, ", .{pos}),
|
.bitpos => |pos| try self.writer.print(" = 1 << {}, ", .{pos}),
|
||||||
.bit_vector => |value| try self.writer.print(" = 0x{X}, ", .{value}),
|
.bit_vector => |bv| try self.writer.print("= 0x{X}, ", .{bv}),
|
||||||
.alias => unreachable,
|
.alias => unreachable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try self.writer.writeAll("_,");
|
||||||
|
|
||||||
for (enumeration.fields) |field| {
|
for (enumeration.fields) |field| {
|
||||||
if (field.value != .alias or field.value.alias.is_compat_alias) {
|
if (field.value != .alias or field.value.alias.is_compat_alias)
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
try self.writer.writeAll("pub const ");
|
try self.writer.writeAll("pub const ");
|
||||||
try self.renderEnumFieldName(name, field.name);
|
try self.renderEnumFieldName(name, field.name);
|
||||||
try self.writer.writeAll(" = .");
|
try self.writer.writeAll(" = ");
|
||||||
|
try self.renderName(name);
|
||||||
|
try self.writer.writeByte('.');
|
||||||
try self.renderEnumFieldName(name, field.value.alias.name);
|
try self.renderEnumFieldName(name, field.value.alias.name);
|
||||||
try self.writer.writeAll(";");
|
try self.writer.writeAll(";\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
try self.writer.writeAll("};\n");
|
try self.writer.writeAll("};\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn bitmaskFlagsType(bitwidth: u8) ![]const u8 {
|
||||||
|
return switch (bitwidth) {
|
||||||
|
32 => "Flags",
|
||||||
|
64 => "Flags64",
|
||||||
|
else => return error.InvalidRegistry,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn renderUsingFlagsMixin(self: *Self, name: []const u8, bitwidth: u8) !void {
|
||||||
|
const flags_type = try bitmaskFlagsType(bitwidth);
|
||||||
|
try self.writer.writeAll("pub usingnamespace FlagsMixin(");
|
||||||
|
try self.renderName(name);
|
||||||
|
try self.writer.print(", {s});\n", .{flags_type});
|
||||||
|
}
|
||||||
|
|
||||||
fn renderBitmaskBits(self: *Self, name: []const u8, bits: reg.Enum) !void {
|
fn renderBitmaskBits(self: *Self, name: []const u8, bits: reg.Enum) !void {
|
||||||
try self.writer.writeAll("pub const ");
|
try self.writer.writeAll("pub const ");
|
||||||
try self.renderName(name);
|
try self.renderName(name);
|
||||||
try self.writer.writeAll(" = packed struct {");
|
try self.writer.writeAll(" = packed struct {");
|
||||||
|
|
||||||
|
const bitflag_name = (try self.extractBitflagName(name)) orelse return error.InvalidRegistry;
|
||||||
|
const flags_type = try bitmaskFlagsType(bits.bitwidth);
|
||||||
|
|
||||||
if (bits.fields.len == 0) {
|
if (bits.fields.len == 0) {
|
||||||
try self.writer.writeAll("_reserved_bits: Flags = 0,");
|
try self.writer.print("_reserved_bits: {s} = 0,", .{flags_type});
|
||||||
} else {
|
} else {
|
||||||
var flags_by_bitpos = [_]?[]const u8{null} ** 32;
|
var flags_by_bitpos = [_]?[]const u8{null} ** 64;
|
||||||
for (bits.fields) |field| {
|
for (bits.fields) |field| {
|
||||||
if (field.value == .bitpos) {
|
if (field.value == .bitpos) {
|
||||||
flags_by_bitpos[field.value.bitpos] = field.name;
|
flags_by_bitpos[field.value.bitpos] = field.name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (flags_by_bitpos) |opt_flag_name, bitpos| {
|
for (flags_by_bitpos[0..bits.bitwidth]) |maybe_flag_name, bitpos| {
|
||||||
if (opt_flag_name) |flag_name| {
|
if (maybe_flag_name) |flag_name| {
|
||||||
try self.renderEnumFieldName(name, flag_name);
|
const field_name = try extractBitflagFieldName(bitflag_name, flag_name);
|
||||||
|
try self.writeIdentifierWithCase(.snake, field_name);
|
||||||
} else {
|
} else {
|
||||||
try self.writer.print("_reserved_bit_{}", .{bitpos});
|
try self.writer.print("_reserved_bit_{}", .{bitpos});
|
||||||
}
|
}
|
||||||
|
|
||||||
try self.writer.writeAll(": bool ");
|
try self.writer.writeAll(": bool ");
|
||||||
if (bitpos == 0) { // Force alignment to integer boundaries
|
if (bitpos == 0) { // Force alignment to integer boundaries
|
||||||
try self.writer.writeAll("align(@alignOf(Flags)) ");
|
try self.writer.print("align(@alignOf({s})) ", .{flags_type});
|
||||||
}
|
}
|
||||||
try self.writer.writeAll("= false, ");
|
try self.writer.writeAll("= false, ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try self.writer.writeAll("pub usingnamespace FlagsMixin(");
|
try self.writer.writeAll("pub usingnamespace FlagsMixin(");
|
||||||
try self.renderName(name);
|
try self.renderName(name);
|
||||||
try self.writer.writeAll(");\n};\n");
|
try self.writer.print(", {s});\n}};\n", .{flags_type});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn renderBitmask(self: *Self, name: []const u8, bitmask: reg.Bitmask) !void {
|
fn renderBitmask(self: *Self, name: []const u8, bitmask: reg.Bitmask) !void {
|
||||||
@@ -747,19 +834,21 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
// The bits structure is generated by renderBitmaskBits, but that wont
|
// The bits structure is generated by renderBitmaskBits, but that wont
|
||||||
// output flags with no associated bits type.
|
// output flags with no associated bits type.
|
||||||
|
|
||||||
|
const flags_type = try bitmaskFlagsType(bitmask.bitwidth);
|
||||||
|
|
||||||
try self.writer.writeAll("pub const ");
|
try self.writer.writeAll("pub const ");
|
||||||
try self.renderName(name);
|
try self.renderName(name);
|
||||||
try self.writer.writeAll(
|
try self.writer.print(
|
||||||
\\ = packed struct {
|
\\ = packed struct {{
|
||||||
\\_reserved_bits: Flags = 0,
|
\\_reserved_bits: {s} = 0,
|
||||||
\\pub usingnamespace FlagsMixin(
|
\\pub usingnamespace FlagsMixin(
|
||||||
);
|
, .{flags_type});
|
||||||
try self.renderName(name);
|
try self.renderName(name);
|
||||||
try self.writer.writeAll(
|
try self.writer.print(
|
||||||
\\);
|
\\, {s});
|
||||||
\\};
|
\\}};
|
||||||
\\
|
\\
|
||||||
);
|
, .{flags_type});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -768,13 +857,13 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
|
|
||||||
try self.writer.writeAll("pub const ");
|
try self.writer.writeAll("pub const ");
|
||||||
try self.renderName(name);
|
try self.renderName(name);
|
||||||
try self.writer.print(" = extern enum({s}) {{null_handle = 0, _}};\n", .{backing_type});
|
try self.writer.print(" = enum({s}) {{null_handle = 0, _}};\n", .{backing_type});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn renderAlias(self: *Self, name: []const u8, alias: reg.Alias) !void {
|
fn renderAlias(self: *Self, name: []const u8, alias: reg.Alias) !void {
|
||||||
if (alias.target == .other_command) {
|
if (alias.target == .other_command) {
|
||||||
return;
|
return;
|
||||||
} else if (self.extractBitflagName(name) != null) {
|
} else if ((try self.extractBitflagName(name)) != null) {
|
||||||
// Don't make aliases of the bitflag names, as those are replaced by just the flags type
|
// Don't make aliases of the bitflag names, as those are replaced by just the flags type
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -849,32 +938,60 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
try self.writer.writeAll("pub const ");
|
try self.writer.writeAll("pub const ");
|
||||||
try self.writeIdentifierWithCase(.snake, trimVkNamespace(ext.name));
|
try self.writeIdentifierWithCase(.snake, trimVkNamespace(ext.name));
|
||||||
try self.writer.writeAll("= Info {\n");
|
try self.writer.writeAll("= Info {\n");
|
||||||
try self.writer.print(".name = \"{s}\", .version = {},", .{ext.name, ext.version});
|
try self.writer.print(".name = \"{s}\", .version = {},", .{ ext.name, ext.version });
|
||||||
try self.writer.writeAll("};\n");
|
try self.writer.writeAll("};\n");
|
||||||
}
|
}
|
||||||
try self.writer.writeAll("};\n");
|
try self.writer.writeAll("};\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
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_name = {s}CommandToString(cmd);
|
||||||
|
\\ const cmd_type_name = "Pfn" ++ cmd_name;
|
||||||
|
\\ const cmd_type = @field(GlobalScope, cmd_type_name);
|
||||||
|
\\ fields[i] = .{{
|
||||||
|
\\ .name = "vk" ++ cmd_name,
|
||||||
|
\\ .field_type = cmd_type,
|
||||||
|
\\ .default_value = 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);
|
||||||
|
|
||||||
for (self.registry.decls) |decl| {
|
for (self.registry.decls) |decl| {
|
||||||
if (decl.decl_type == .command) {
|
if (decl.decl_type == .command) {
|
||||||
const command = decl.decl_type.command;
|
const command = decl.decl_type.command;
|
||||||
if (self.classifyCommandDispatch(decl.name, command) == dispatch_type) {
|
if (classifyCommandDispatch(decl.name, command) == dispatch_type) {
|
||||||
try self.renderWrapper(decl.name, decl.decl_type.command);
|
try self.renderWrapper(decl.name, decl.decl_type.command);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -901,16 +1018,15 @@ 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.CommandLoadFailure;
|
||||||
\\ @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;
|
||||||
\\}}
|
\\}}
|
||||||
\\
|
\\
|
||||||
, .{params, loader_first_param}
|
, .{ params, loader_first_param });
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn derefName(name: []const u8) []const u8 {
|
fn derefName(name: []const u8) []const u8 {
|
||||||
@@ -939,7 +1055,8 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
.bitflags, // Special stuff handled in renderWrapperCall
|
.bitflags, // Special stuff handled in renderWrapperCall
|
||||||
.buffer_len,
|
.buffer_len,
|
||||||
.mut_buffer_len,
|
.mut_buffer_len,
|
||||||
.other => {
|
.other,
|
||||||
|
=> {
|
||||||
try self.writeIdentifierWithCase(.snake, param.name);
|
try self.writeIdentifierWithCase(.snake, param.name);
|
||||||
try self.writer.writeAll(": ");
|
try self.writer.writeAll(": ");
|
||||||
try self.renderTypeInfo(param.param_type);
|
try self.renderTypeInfo(param.param_type);
|
||||||
@@ -951,8 +1068,8 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
|
|
||||||
try self.writer.writeAll(") ");
|
try self.writer.writeAll(") ");
|
||||||
|
|
||||||
if (command.return_type.* == .name and mem.eql(u8, command.return_type.name, "VkResult")) {
|
if (command.error_codes.len > 0) {
|
||||||
try self.renderErrorSet(command.error_codes);
|
try self.renderErrorSetName(name);
|
||||||
try self.writer.writeByte('!');
|
try self.writer.writeByte('!');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -966,7 +1083,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("(");
|
||||||
|
|
||||||
@@ -987,10 +1104,7 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
try self.writeIdentifierWithCase(.snake, param.name);
|
try self.writeIdentifierWithCase(.snake, param.name);
|
||||||
try self.writer.writeAll(".toInt()");
|
try self.writer.writeAll(".toInt()");
|
||||||
},
|
},
|
||||||
.in_out_pointer,
|
.in_out_pointer, .buffer_len, .mut_buffer_len, .other => {
|
||||||
.buffer_len,
|
|
||||||
.mut_buffer_len,
|
|
||||||
.other => {
|
|
||||||
try self.writeIdentifierWithCase(.snake, param.name);
|
try self.writeIdentifierWithCase(.snake, param.name);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -1045,6 +1159,11 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
try self.writeIdentifierFmt("{s}Result", .{trimVkNamespace(command_name)});
|
try self.writeIdentifierFmt("{s}Result", .{trimVkNamespace(command_name)});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn renderErrorSetName(self: *Self, name: []const u8) !void {
|
||||||
|
try self.writeIdentifierWithCase(.title, trimVkNamespace(name));
|
||||||
|
try self.writer.writeAll("Error");
|
||||||
|
}
|
||||||
|
|
||||||
fn renderReturnStruct(self: *Self, command_name: []const u8, returns: []const ReturnValue) !void {
|
fn renderReturnStruct(self: *Self, command_name: []const u8, returns: []const ReturnValue) !void {
|
||||||
try self.writer.writeAll("pub const ");
|
try self.writer.writeAll("pub const ");
|
||||||
try self.renderReturnStructName(command_name);
|
try self.renderReturnStructName(command_name);
|
||||||
@@ -1054,16 +1173,13 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
try self.writer.writeAll(": ");
|
try self.writer.writeAll(": ");
|
||||||
try self.renderTypeInfo(ret.return_value_type);
|
try self.renderTypeInfo(ret.return_value_type);
|
||||||
try self.writer.writeAll(", ");
|
try self.writer.writeAll(", ");
|
||||||
|
|
||||||
}
|
}
|
||||||
try self.writer.writeAll("};\n");
|
try self.writer.writeAll("};\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn renderWrapper(self: *Self, name: []const u8, command: reg.Command) !void {
|
fn renderWrapper(self: *Self, name: []const u8, command: reg.Command) !void {
|
||||||
const returns_vk_result = command.return_type.* == .name
|
const returns_vk_result = command.return_type.* == .name and mem.eql(u8, command.return_type.name, "VkResult");
|
||||||
and mem.eql(u8, command.return_type.name, "VkResult");
|
const returns_void = command.return_type.* == .name and mem.eql(u8, command.return_type.name, "void");
|
||||||
const returns_void = command.return_type.* == .name
|
|
||||||
and mem.eql(u8, command.return_type.name, "void");
|
|
||||||
|
|
||||||
const returns = try self.extractReturns(command);
|
const returns = try self.extractReturns(command);
|
||||||
|
|
||||||
@@ -1071,6 +1187,14 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
try self.renderReturnStruct(name, returns);
|
try self.renderReturnStruct(name, returns);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (command.error_codes.len > 0) {
|
||||||
|
try self.writer.writeAll("pub const ");
|
||||||
|
try self.renderErrorSetName(name);
|
||||||
|
try self.writer.writeAll(" = ");
|
||||||
|
try self.renderErrorSet(command.error_codes);
|
||||||
|
try self.writer.writeAll(";\n");
|
||||||
|
}
|
||||||
|
|
||||||
try self.renderWrapperPrototype(name, command, returns);
|
try self.renderWrapperPrototype(name, command, returns);
|
||||||
|
|
||||||
if (returns.len == 1 and returns[0].origin == .inner_return_value) {
|
if (returns.len == 1 and returns[0].origin == .inner_return_value) {
|
||||||
@@ -1140,13 +1264,13 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
try self.writer.writeAll(") {\n");
|
try self.writer.writeAll(") {\n");
|
||||||
|
|
||||||
for (command.success_codes) |success| {
|
for (command.success_codes) |success| {
|
||||||
try self.writer.writeByte('.');
|
try self.writer.writeAll("Result.");
|
||||||
try self.renderEnumFieldName("VkResult", success);
|
try self.renderEnumFieldName("VkResult", success);
|
||||||
try self.writer.writeAll(" => {},");
|
try self.writer.writeAll(" => {},");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (command.error_codes) |err| {
|
for (command.error_codes) |err| {
|
||||||
try self.writer.writeByte('.');
|
try self.writer.writeAll("Result.");
|
||||||
try self.renderEnumFieldName("VkResult", err);
|
try self.renderEnumFieldName("VkResult", err);
|
||||||
try self.writer.writeAll(" => return error.");
|
try self.writer.writeAll(" => return error.");
|
||||||
try self.renderResultAsErrorName(err);
|
try self.renderResultAsErrorName(err);
|
||||||
@@ -1168,7 +1292,7 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
fn renderResultAsErrorName(self: *Self, name: []const u8) !void {
|
fn renderResultAsErrorName(self: *Self, name: []const u8) !void {
|
||||||
const error_prefix = "VK_ERROR_";
|
const error_prefix = "VK_ERROR_";
|
||||||
if (mem.startsWith(u8, name, error_prefix)) {
|
if (mem.startsWith(u8, name, error_prefix)) {
|
||||||
try self.writeIdentifierWithCase(.title, name[error_prefix.len ..]);
|
try self.writeIdentifierWithCase(.title, name[error_prefix.len..]);
|
||||||
} else {
|
} else {
|
||||||
// Apparently some commands (VkAcquireProfilingLockInfoKHR) return
|
// Apparently some commands (VkAcquireProfilingLockInfoKHR) return
|
||||||
// success codes as error...
|
// success codes as error...
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ const ArrayList = std.ArrayList;
|
|||||||
|
|
||||||
pub const Attribute = struct {
|
pub const Attribute = struct {
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
value: []const u8
|
value: []const u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Content = union(enum) {
|
pub const Content = union(enum) {
|
||||||
CharData: []const u8,
|
CharData: []const u8,
|
||||||
Comment: []const u8,
|
Comment: []const u8,
|
||||||
Element: *Element
|
Element: *Element,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Element = struct {
|
pub const Element = struct {
|
||||||
@@ -50,7 +50,7 @@ pub const Element = struct {
|
|||||||
|
|
||||||
return switch (child.children.items[0]) {
|
return switch (child.children.items[0]) {
|
||||||
.CharData => |char_data| char_data,
|
.CharData => |char_data| char_data,
|
||||||
else => null
|
else => null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ pub const Element = struct {
|
|||||||
pub fn findChildrenByTag(self: *Element, tag: []const u8) FindChildrenByTagIterator {
|
pub fn findChildrenByTag(self: *Element, tag: []const u8) FindChildrenByTagIterator {
|
||||||
return .{
|
return .{
|
||||||
.inner = self.elements(),
|
.inner = self.elements(),
|
||||||
.tag = tag
|
.tag = tag,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,7 +129,7 @@ pub const Element = struct {
|
|||||||
pub const XmlDecl = struct {
|
pub const XmlDecl = struct {
|
||||||
version: []const u8,
|
version: []const u8,
|
||||||
encoding: ?[]const u8,
|
encoding: ?[]const u8,
|
||||||
standalone: ?bool
|
standalone: ?bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Document = struct {
|
pub const Document = struct {
|
||||||
@@ -154,7 +154,7 @@ const ParseContext = struct {
|
|||||||
.source = source,
|
.source = source,
|
||||||
.offset = 0,
|
.offset = 0,
|
||||||
.line = 0,
|
.line = 0,
|
||||||
.column = 0
|
.column = 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,7 +211,7 @@ const ParseContext = struct {
|
|||||||
fn expectStr(self: *ParseContext, text: []const u8) !void {
|
fn expectStr(self: *ParseContext, text: []const u8) !void {
|
||||||
if (self.source.len < self.offset + text.len) {
|
if (self.source.len < self.offset + text.len) {
|
||||||
return error.UnexpectedEof;
|
return error.UnexpectedEof;
|
||||||
} else if (std.mem.startsWith(u8, self.source[self.offset ..], text)) {
|
} else if (std.mem.startsWith(u8, self.source[self.offset..], text)) {
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
while (i < text.len) : (i += 1) {
|
while (i < text.len) : (i += 1) {
|
||||||
_ = self.consumeNoEof();
|
_ = self.consumeNoEof();
|
||||||
@@ -232,7 +232,7 @@ const ParseContext = struct {
|
|||||||
ws = true;
|
ws = true;
|
||||||
_ = self.consumeNoEof();
|
_ = self.consumeNoEof();
|
||||||
},
|
},
|
||||||
else => break
|
else => break,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,62 +245,62 @@ const ParseContext = struct {
|
|||||||
|
|
||||||
fn currentLine(self: ParseContext) []const u8 {
|
fn currentLine(self: ParseContext) []const u8 {
|
||||||
var begin: usize = 0;
|
var begin: usize = 0;
|
||||||
if (mem.indexOfScalarPos(u8, self.source[0 .. self.offset], '\n')) |prev_nl| {
|
if (mem.lastIndexOfScalar(u8, self.source[0..self.offset], '\n')) |prev_nl| {
|
||||||
begin = prev_nl + 1;
|
begin = prev_nl + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
var end = mem.indexOfScalarPos(u8, self.source, self.offset, '\n') orelse self.source.len;
|
var end = mem.indexOfScalarPos(u8, self.source, self.offset, '\n') orelse self.source.len;
|
||||||
return self.source[begin .. end];
|
return self.source[begin..end];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
test "ParseContext" {
|
test "ParseContext" {
|
||||||
{
|
{
|
||||||
var ctx = ParseContext.init("I like pythons");
|
var ctx = ParseContext.init("I like pythons");
|
||||||
testing.expectEqual(@as(?u8, 'I'), ctx.peek());
|
try testing.expectEqual(@as(?u8, 'I'), ctx.peek());
|
||||||
testing.expectEqual(@as(u8, 'I'), ctx.consumeNoEof());
|
try testing.expectEqual(@as(u8, 'I'), ctx.consumeNoEof());
|
||||||
testing.expectEqual(@as(?u8, ' '), ctx.peek());
|
try testing.expectEqual(@as(?u8, ' '), ctx.peek());
|
||||||
testing.expectEqual(@as(u8, ' '), try ctx.consume());
|
try testing.expectEqual(@as(u8, ' '), try ctx.consume());
|
||||||
|
|
||||||
testing.expect(ctx.eat('l'));
|
try testing.expect(ctx.eat('l'));
|
||||||
testing.expectEqual(@as(?u8, 'i'), ctx.peek());
|
try testing.expectEqual(@as(?u8, 'i'), ctx.peek());
|
||||||
testing.expectEqual(false, ctx.eat('a'));
|
try testing.expectEqual(false, ctx.eat('a'));
|
||||||
testing.expectEqual(@as(?u8, 'i'), ctx.peek());
|
try testing.expectEqual(@as(?u8, 'i'), ctx.peek());
|
||||||
|
|
||||||
try ctx.expect('i');
|
try ctx.expect('i');
|
||||||
testing.expectEqual(@as(?u8, 'k'), ctx.peek());
|
try testing.expectEqual(@as(?u8, 'k'), ctx.peek());
|
||||||
testing.expectError(error.UnexpectedCharacter, ctx.expect('a'));
|
try testing.expectError(error.UnexpectedCharacter, ctx.expect('a'));
|
||||||
testing.expectEqual(@as(?u8, 'k'), ctx.peek());
|
try testing.expectEqual(@as(?u8, 'k'), ctx.peek());
|
||||||
|
|
||||||
testing.expect(ctx.eatStr("ke"));
|
try testing.expect(ctx.eatStr("ke"));
|
||||||
testing.expectEqual(@as(?u8, ' '), ctx.peek());
|
try testing.expectEqual(@as(?u8, ' '), ctx.peek());
|
||||||
|
|
||||||
testing.expect(ctx.eatWs());
|
try testing.expect(ctx.eatWs());
|
||||||
testing.expectEqual(@as(?u8, 'p'), ctx.peek());
|
try testing.expectEqual(@as(?u8, 'p'), ctx.peek());
|
||||||
testing.expectEqual(false, ctx.eatWs());
|
try testing.expectEqual(false, ctx.eatWs());
|
||||||
testing.expectEqual(@as(?u8, 'p'), ctx.peek());
|
try testing.expectEqual(@as(?u8, 'p'), ctx.peek());
|
||||||
|
|
||||||
testing.expectEqual(false, ctx.eatStr("aaaaaaaaa"));
|
try testing.expectEqual(false, ctx.eatStr("aaaaaaaaa"));
|
||||||
testing.expectEqual(@as(?u8, 'p'), ctx.peek());
|
try testing.expectEqual(@as(?u8, 'p'), ctx.peek());
|
||||||
|
|
||||||
testing.expectError(error.UnexpectedEof, ctx.expectStr("aaaaaaaaa"));
|
try testing.expectError(error.UnexpectedEof, ctx.expectStr("aaaaaaaaa"));
|
||||||
testing.expectEqual(@as(?u8, 'p'), ctx.peek());
|
try testing.expectEqual(@as(?u8, 'p'), ctx.peek());
|
||||||
testing.expectError(error.UnexpectedCharacter, ctx.expectStr("pytn"));
|
try testing.expectError(error.UnexpectedCharacter, ctx.expectStr("pytn"));
|
||||||
testing.expectEqual(@as(?u8, 'p'), ctx.peek());
|
try testing.expectEqual(@as(?u8, 'p'), ctx.peek());
|
||||||
try ctx.expectStr("python");
|
try ctx.expectStr("python");
|
||||||
testing.expectEqual(@as(?u8, 's'), ctx.peek());
|
try testing.expectEqual(@as(?u8, 's'), ctx.peek());
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var ctx = ParseContext.init("");
|
var ctx = ParseContext.init("");
|
||||||
testing.expectEqual(ctx.peek(), null);
|
try testing.expectEqual(ctx.peek(), null);
|
||||||
testing.expectError(error.UnexpectedEof, ctx.consume());
|
try testing.expectError(error.UnexpectedEof, ctx.consume());
|
||||||
testing.expectEqual(ctx.eat('p'), false);
|
try testing.expectEqual(ctx.eat('p'), false);
|
||||||
testing.expectError(error.UnexpectedEof, ctx.expect('p'));
|
try testing.expectError(error.UnexpectedEof, ctx.expect('p'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const ParseError = error {
|
pub const ParseError = error{
|
||||||
IllegalCharacter,
|
IllegalCharacter,
|
||||||
UnexpectedEof,
|
UnexpectedEof,
|
||||||
UnexpectedCharacter,
|
UnexpectedCharacter,
|
||||||
@@ -311,7 +311,7 @@ pub const ParseError = error {
|
|||||||
InvalidStandaloneValue,
|
InvalidStandaloneValue,
|
||||||
NonMatchingClosingTag,
|
NonMatchingClosingTag,
|
||||||
InvalidDocument,
|
InvalidDocument,
|
||||||
OutOfMemory
|
OutOfMemory,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn parse(backing_allocator: *Allocator, source: []const u8) !Document {
|
pub fn parse(backing_allocator: *Allocator, source: []const u8) !Document {
|
||||||
@@ -323,15 +323,20 @@ fn parseDocument(ctx: *ParseContext, backing_allocator: *Allocator) !Document {
|
|||||||
var doc = Document{
|
var doc = Document{
|
||||||
.arena = ArenaAllocator.init(backing_allocator),
|
.arena = ArenaAllocator.init(backing_allocator),
|
||||||
.xml_decl = null,
|
.xml_decl = null,
|
||||||
.root = undefined
|
.root = undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
errdefer doc.deinit();
|
errdefer doc.deinit();
|
||||||
|
|
||||||
|
try trySkipComments(ctx, &doc.arena.allocator);
|
||||||
|
|
||||||
doc.xml_decl = try tryParseProlog(ctx, &doc.arena.allocator);
|
doc.xml_decl = try tryParseProlog(ctx, &doc.arena.allocator);
|
||||||
_ = ctx.eatWs();
|
_ = ctx.eatWs();
|
||||||
|
try trySkipComments(ctx, &doc.arena.allocator);
|
||||||
|
|
||||||
doc.root = (try tryParseElement(ctx, &doc.arena.allocator)) orelse return error.InvalidDocument;
|
doc.root = (try tryParseElement(ctx, &doc.arena.allocator)) orelse return error.InvalidDocument;
|
||||||
_ = ctx.eatWs();
|
_ = ctx.eatWs();
|
||||||
|
try trySkipComments(ctx, &doc.arena.allocator);
|
||||||
|
|
||||||
if (ctx.peek() != null) return error.InvalidDocument;
|
if (ctx.peek() != null) return error.InvalidDocument;
|
||||||
|
|
||||||
@@ -351,7 +356,7 @@ fn parseAttrValue(ctx: *ParseContext, alloc: *Allocator) ![]const u8 {
|
|||||||
|
|
||||||
const end = ctx.offset - 1;
|
const end = ctx.offset - 1;
|
||||||
|
|
||||||
return try dupeAndUnescape(alloc, ctx.source[begin .. end]);
|
return try dupeAndUnescape(alloc, ctx.source[begin..end]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parseEqAttrValue(ctx: *ParseContext, alloc: *Allocator) ![]const u8 {
|
fn parseEqAttrValue(ctx: *ParseContext, alloc: *Allocator) ![]const u8 {
|
||||||
@@ -371,14 +376,14 @@ fn parseNameNoDupe(ctx: *ParseContext) ![]const u8 {
|
|||||||
switch (ch) {
|
switch (ch) {
|
||||||
' ', '\t', '\n', '\r' => break,
|
' ', '\t', '\n', '\r' => break,
|
||||||
'&', '"', '\'', '<', '>', '?', '=', '/' => break,
|
'&', '"', '\'', '<', '>', '?', '=', '/' => break,
|
||||||
else => _ = ctx.consumeNoEof()
|
else => _ = ctx.consumeNoEof(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const end = ctx.offset;
|
const end = ctx.offset;
|
||||||
if (begin == end) return error.InvalidName;
|
if (begin == end) return error.InvalidName;
|
||||||
|
|
||||||
return ctx.source[begin .. end];
|
return ctx.source[begin..end];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tryParseCharData(ctx: *ParseContext, alloc: *Allocator) !?[]const u8 {
|
fn tryParseCharData(ctx: *ParseContext, alloc: *Allocator) !?[]const u8 {
|
||||||
@@ -386,24 +391,24 @@ fn tryParseCharData(ctx: *ParseContext, alloc: *Allocator) !?[]const u8 {
|
|||||||
|
|
||||||
while (ctx.peek()) |ch| {
|
while (ctx.peek()) |ch| {
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
'<', '>' => break,
|
'<' => break,
|
||||||
else => _ = ctx.consumeNoEof()
|
else => _ = ctx.consumeNoEof(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const end = ctx.offset;
|
const end = ctx.offset;
|
||||||
if (begin == end) return null;
|
if (begin == end) return null;
|
||||||
|
|
||||||
return try dupeAndUnescape(alloc, ctx.source[begin .. end]);
|
return try dupeAndUnescape(alloc, ctx.source[begin..end]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parseContent(ctx: *ParseContext, alloc: *Allocator) ParseError!Content {
|
fn parseContent(ctx: *ParseContext, alloc: *Allocator) ParseError!Content {
|
||||||
if (try tryParseCharData(ctx, alloc)) |cd| {
|
if (try tryParseCharData(ctx, alloc)) |cd| {
|
||||||
return Content{.CharData = cd};
|
return Content{ .CharData = cd };
|
||||||
} else if (try tryParseComment(ctx, alloc)) |comment| {
|
} else if (try tryParseComment(ctx, alloc)) |comment| {
|
||||||
return Content{.Comment = comment};
|
return Content{ .Comment = comment };
|
||||||
} else if (try tryParseElement(ctx, alloc)) |elem| {
|
} else if (try tryParseElement(ctx, alloc)) |elem| {
|
||||||
return Content{.Element = elem};
|
return Content{ .Element = elem };
|
||||||
} else {
|
} else {
|
||||||
return error.UnexpectedCharacter;
|
return error.UnexpectedCharacter;
|
||||||
}
|
}
|
||||||
@@ -472,41 +477,41 @@ test "tryParseElement" {
|
|||||||
|
|
||||||
{
|
{
|
||||||
var ctx = ParseContext.init("<= a='b'/>");
|
var ctx = ParseContext.init("<= a='b'/>");
|
||||||
testing.expectEqual(@as(?*Element, null), try tryParseElement(&ctx, alloc));
|
try testing.expectEqual(@as(?*Element, null), try tryParseElement(&ctx, alloc));
|
||||||
testing.expectEqual(@as(?u8, '<'), ctx.peek());
|
try testing.expectEqual(@as(?u8, '<'), ctx.peek());
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var ctx = ParseContext.init("<python size='15' color = \"green\"/>");
|
var ctx = ParseContext.init("<python size='15' color = \"green\"/>");
|
||||||
const elem = try tryParseElement(&ctx, alloc);
|
const elem = try tryParseElement(&ctx, alloc);
|
||||||
testing.expectEqualSlices(u8, elem.?.tag, "python");
|
try testing.expectEqualSlices(u8, elem.?.tag, "python");
|
||||||
|
|
||||||
const size_attr = elem.?.attributes.items[0];
|
const size_attr = elem.?.attributes.items[0];
|
||||||
testing.expectEqualSlices(u8, size_attr.name, "size");
|
try testing.expectEqualSlices(u8, size_attr.name, "size");
|
||||||
testing.expectEqualSlices(u8, size_attr.value, "15");
|
try testing.expectEqualSlices(u8, size_attr.value, "15");
|
||||||
|
|
||||||
const color_attr = elem.?.attributes.items[1];
|
const color_attr = elem.?.attributes.items[1];
|
||||||
testing.expectEqualSlices(u8, color_attr.name, "color");
|
try testing.expectEqualSlices(u8, color_attr.name, "color");
|
||||||
testing.expectEqualSlices(u8, color_attr.value, "green");
|
try testing.expectEqualSlices(u8, color_attr.value, "green");
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var ctx = ParseContext.init("<python>test</python>");
|
var ctx = ParseContext.init("<python>test</python>");
|
||||||
const elem = try tryParseElement(&ctx, alloc);
|
const elem = try tryParseElement(&ctx, alloc);
|
||||||
testing.expectEqualSlices(u8, elem.?.tag, "python");
|
try testing.expectEqualSlices(u8, elem.?.tag, "python");
|
||||||
testing.expectEqualSlices(u8, elem.?.children.items[0].CharData, "test");
|
try testing.expectEqualSlices(u8, elem.?.children.items[0].CharData, "test");
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var ctx = ParseContext.init("<a>b<c/>d<e/>f<!--g--></a>");
|
var ctx = ParseContext.init("<a>b<c/>d<e/>f<!--g--></a>");
|
||||||
const elem = try tryParseElement(&ctx, alloc);
|
const elem = try tryParseElement(&ctx, alloc);
|
||||||
testing.expectEqualSlices(u8, elem.?.tag, "a");
|
try testing.expectEqualSlices(u8, elem.?.tag, "a");
|
||||||
testing.expectEqualSlices(u8, elem.?.children.items[0].CharData, "b");
|
try testing.expectEqualSlices(u8, elem.?.children.items[0].CharData, "b");
|
||||||
testing.expectEqualSlices(u8, elem.?.children.items[1].Element.tag, "c");
|
try testing.expectEqualSlices(u8, elem.?.children.items[1].Element.tag, "c");
|
||||||
testing.expectEqualSlices(u8, elem.?.children.items[2].CharData, "d");
|
try testing.expectEqualSlices(u8, elem.?.children.items[2].CharData, "d");
|
||||||
testing.expectEqualSlices(u8, elem.?.children.items[3].Element.tag, "e");
|
try testing.expectEqualSlices(u8, elem.?.children.items[3].Element.tag, "e");
|
||||||
testing.expectEqualSlices(u8, elem.?.children.items[4].CharData, "f");
|
try testing.expectEqualSlices(u8, elem.?.children.items[4].CharData, "f");
|
||||||
testing.expectEqualSlices(u8, elem.?.children.items[5].Comment, "g");
|
try testing.expectEqualSlices(u8, elem.?.children.items[5].Comment, "g");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -560,24 +565,30 @@ test "tryParseProlog" {
|
|||||||
|
|
||||||
{
|
{
|
||||||
var ctx = ParseContext.init("<?xmla version='aa'?>");
|
var ctx = ParseContext.init("<?xmla version='aa'?>");
|
||||||
testing.expectEqual(@as(?*XmlDecl, null), try tryParseProlog(&ctx, alloc));
|
try testing.expectEqual(@as(?*XmlDecl, null), try tryParseProlog(&ctx, alloc));
|
||||||
testing.expectEqual(@as(?u8, '<'), ctx.peek());
|
try testing.expectEqual(@as(?u8, '<'), ctx.peek());
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var ctx = ParseContext.init("<?xml version='aa'?>");
|
var ctx = ParseContext.init("<?xml version='aa'?>");
|
||||||
const decl = try tryParseProlog(&ctx, alloc);
|
const decl = try tryParseProlog(&ctx, alloc);
|
||||||
testing.expectEqualSlices(u8, "aa", decl.?.version);
|
try testing.expectEqualSlices(u8, "aa", decl.?.version);
|
||||||
testing.expectEqual(@as(?[]const u8, null), decl.?.encoding);
|
try testing.expectEqual(@as(?[]const u8, null), decl.?.encoding);
|
||||||
testing.expectEqual(@as(?bool, null), decl.?.standalone);
|
try testing.expectEqual(@as(?bool, null), decl.?.standalone);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var ctx = ParseContext.init("<?xml version=\"aa\" encoding = 'bbb' standalone \t = 'yes'?>");
|
var ctx = ParseContext.init("<?xml version=\"aa\" encoding = 'bbb' standalone \t = 'yes'?>");
|
||||||
const decl = try tryParseProlog(&ctx, alloc);
|
const decl = try tryParseProlog(&ctx, alloc);
|
||||||
testing.expectEqualSlices(u8, "aa", decl.?.version);
|
try testing.expectEqualSlices(u8, "aa", decl.?.version);
|
||||||
testing.expectEqualSlices(u8, "bbb", decl.?.encoding.?);
|
try testing.expectEqualSlices(u8, "bbb", decl.?.encoding.?);
|
||||||
testing.expectEqual(@as(?bool, true), decl.?.standalone.?);
|
try testing.expectEqual(@as(?bool, true), decl.?.standalone.?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trySkipComments(ctx: *ParseContext, alloc: *Allocator) !void {
|
||||||
|
while (try tryParseComment(ctx, alloc)) |_| {
|
||||||
|
_ = ctx.eatWs();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -590,21 +601,18 @@ fn tryParseComment(ctx: *ParseContext, alloc: *Allocator) !?[]const u8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const end = ctx.offset - "-->".len;
|
const end = ctx.offset - "-->".len;
|
||||||
return try mem.dupe(alloc, u8, ctx.source[begin .. end]);
|
return try mem.dupe(alloc, u8, ctx.source[begin..end]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unescapeEntity(text: []const u8) !u8 {
|
fn unescapeEntity(text: []const u8) !u8 {
|
||||||
const EntitySubstition = struct {
|
const EntitySubstition = struct { text: []const u8, replacement: u8 };
|
||||||
text: []const u8,
|
|
||||||
replacement: u8
|
|
||||||
};
|
|
||||||
|
|
||||||
const entities = [_]EntitySubstition{
|
const entities = [_]EntitySubstition{
|
||||||
.{.text = "<", .replacement = '<'},
|
.{ .text = "<", .replacement = '<' },
|
||||||
.{.text = ">", .replacement = '>'},
|
.{ .text = ">", .replacement = '>' },
|
||||||
.{.text = "&", .replacement = '&'},
|
.{ .text = "&", .replacement = '&' },
|
||||||
.{.text = "'", .replacement = '\''},
|
.{ .text = "'", .replacement = '\'' },
|
||||||
.{.text = """, .replacement = '"'}
|
.{ .text = """, .replacement = '"' },
|
||||||
};
|
};
|
||||||
|
|
||||||
for (entities) |entity| {
|
for (entities) |entity| {
|
||||||
@@ -622,7 +630,7 @@ fn dupeAndUnescape(alloc: *Allocator, text: []const u8) ![]const u8 {
|
|||||||
while (i < text.len) : (j += 1) {
|
while (i < text.len) : (j += 1) {
|
||||||
if (text[i] == '&') {
|
if (text[i] == '&') {
|
||||||
const entity_end = 1 + (mem.indexOfScalarPos(u8, text, i, ';') orelse return error.InvalidEntity);
|
const entity_end = 1 + (mem.indexOfScalarPos(u8, text, i, ';') orelse return error.InvalidEntity);
|
||||||
str[j] = try unescapeEntity(text[i .. entity_end]);
|
str[j] = try unescapeEntity(text[i..entity_end]);
|
||||||
i = entity_end;
|
i = entity_end;
|
||||||
} else {
|
} else {
|
||||||
str[j] = text[i];
|
str[j] = text[i];
|
||||||
@@ -638,10 +646,19 @@ test "dupeAndUnescape" {
|
|||||||
defer arena.deinit();
|
defer arena.deinit();
|
||||||
var alloc = &arena.allocator;
|
var alloc = &arena.allocator;
|
||||||
|
|
||||||
testing.expectEqualSlices(u8, "test", try dupeAndUnescape(alloc, "test"));
|
try testing.expectEqualSlices(u8, "test", try dupeAndUnescape(alloc, "test"));
|
||||||
testing.expectEqualSlices(u8, "a<b&c>d\"e'f<", try dupeAndUnescape(alloc, "a<b&c>d"e'f<"));
|
try testing.expectEqualSlices(u8, "a<b&c>d\"e'f<", try dupeAndUnescape(alloc, "a<b&c>d"e'f<"));
|
||||||
testing.expectError(error.InvalidEntity, dupeAndUnescape(alloc, "python&"));
|
try testing.expectError(error.InvalidEntity, dupeAndUnescape(alloc, "python&"));
|
||||||
testing.expectError(error.InvalidEntity, dupeAndUnescape(alloc, "python&&"));
|
try testing.expectError(error.InvalidEntity, dupeAndUnescape(alloc, "python&&"));
|
||||||
testing.expectError(error.InvalidEntity, dupeAndUnescape(alloc, "python&test;"));
|
try testing.expectError(error.InvalidEntity, dupeAndUnescape(alloc, "python&test;"));
|
||||||
testing.expectError(error.InvalidEntity, dupeAndUnescape(alloc, "python&boa"));
|
try testing.expectError(error.InvalidEntity, dupeAndUnescape(alloc, "python&boa"));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "Top level comments" {
|
||||||
|
var arena = std.heap.ArenaAllocator.init(testing.allocator);
|
||||||
|
defer arena.deinit();
|
||||||
|
var alloc = &arena.allocator;
|
||||||
|
|
||||||
|
const doc = try parse(alloc, "<?xml version='aa'?><!--comment--><python color='green'/><!--another comment-->");
|
||||||
|
try testing.expectEqualSlices(u8, "python", doc.root.tag);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user