19 Commits

Author SHA1 Message Date
Robin Voetter
4b7b9a8b94 Merge pull request #205 from DialecticalMaterialist/update-zig
Update zig
2025-09-04 14:40:25 +02:00
DialecticalMaterialist
4066c2c526 Update zig 2025-09-04 14:15:52 +02:00
Robin Voetter
c9c4dae703 Merge pull request #204 from zacoons/debugMessages
Debug messages
2025-08-31 22:41:24 +02:00
zac
ecf97034c4 add debug messenger to graphics context 2025-08-30 18:27:22 +10:00
zac
3c7d4021e9 change extension_names to use vk.extensions over hardcoded string 2025-08-30 18:24:00 +10:00
Robin Voetter
f879074293 Merge pull request #203 from Snektron/vkbool32-enum
replace vk.Bool32 with enum
2025-08-26 23:07:48 +02:00
Robin Voetter
571c59180d replace vk.Bool32 with enum 2025-08-26 23:06:23 +02:00
Robin Voetter
8322c9593f Merge pull request #201 from i-pj/fix-issue-200
Format function signature
2025-08-24 21:05:12 +02:00
Parth Jain
33407b8e4e update format function 2025-08-24 14:04:13 +05:30
Robin Voetter
93bc6f1ac3 Merge pull request #198 from flut2/update-zig-1
update to 0.15.0-dev.1518+749f10af4
2025-08-15 10:37:02 +02:00
flut2
bed9e2d224 update to 0.15.0-dev.1518+749f10af4 2025-08-15 06:48:14 +01:00
Robin Voetter
ed429842b8 Merge pull request #196 from mozbeel/android-fix
isARM -> isArm (Android)
2025-08-01 00:12:29 +02:00
mozbeel
cb618dea81 Fixed isARM -> isArm 2025-08-01 00:03:25 +02:00
Robin Voetter
e492b17810 Merge pull request #195 from Snektron/fix-windowresize
fix window extent after creating
2025-07-20 11:47:10 +02:00
Robin Voetter
39565ca4c3 fix window extent after creating
The requested dimensions of a newly created window may be different
from the actual dimensions as the window system may enforce additional
restrictions. This happens, for example, when using Wayland with a
scaling factor of 3 (the default width of 800 is not divisible by 3).

To fix it, just re-query the actual framebuffer size after creating the window.
The GLFW documentation also suggests to do this:

> The created window, framebuffer and context may differ from what you requested,
> as not all parameters and hints are hard constraints. This includes the size of
> the window, especially for full screen windows. To query the actual attributes
> of the created window, framebuffer and context, see glfwGetWindowAttrib,
> glfwGetWindowSize and glfwGetFramebufferSize.

-- https://www.glfw.org/docs/3.3/group__window.html#ga3555a418df92ad53f917597fe2f64aeb

This should be a better fix for #192
2025-07-20 09:19:47 +02:00
Robin Voetter
f99c5994dc Merge pull request #193 from alichraghi/master
update to latest zig
2025-07-19 13:30:49 +02:00
Robin Voetter
2a1dba26ff Add workaround for incorrect StdVideoH265HrdParameters encoding
See https://github.com/KhronosGroup/Vulkan-Docs/issues/2557
2025-07-19 13:26:20 +02:00
Robin Voetter
9e6c4640bf fix std.process.fatal usage in src/main.zig + fix tree rendering 2025-07-19 12:59:39 +02:00
Ali Cheraghi
0fd576a7e5 update to latest zig 2025-07-16 17:20:05 +03:30
15 changed files with 1924 additions and 1850 deletions

View File

@@ -7,15 +7,19 @@ pub fn build(b: *std.Build) void {
const maybe_video = b.option(std.Build.LazyPath, "video", "Set the path to the Vulkan Video registry (video.xml)");
const test_step = b.step("test", "Run all the tests");
const root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
// Using the package manager, this artifact can be obtained by the user
// through `b.dependency(<name in build.zig.zon>, .{}).artifact("vulkan-zig-generator")`.
// with that, the user need only `.addArg("path/to/vk.xml")`, and then obtain
// a file source to the generated code with `.addOutputArg("vk.zig")`
const generator_exe = b.addExecutable(.{
.name = "vulkan-zig-generator",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
.root_module = root_module,
});
b.installArtifact(generator_exe);
@@ -47,16 +51,16 @@ pub fn build(b: *std.Build) void {
// It does not need to run anyway.
const ref_all_decls_test = b.addObject(.{
.name = "ref-all-decls-test",
.root_module = b.createModule(.{
.root_source_file = b.path("test/ref_all_decls.zig"),
.target = target,
.optimize = optimize,
}),
});
ref_all_decls_test.root_module.addImport("vulkan", vk_zig_module);
test_step.dependOn(&ref_all_decls_test.step);
}
const test_target = b.addTest(.{
.root_source_file = b.path("src/main.zig"),
});
const test_target = b.addTest(.{ .root_module = root_module });
test_step.dependOn(&b.addRunArtifact(test_target).step);
}

View File

@@ -2,7 +2,7 @@
.name = .vulkan,
.fingerprint = 0xbe155a03c72db6af,
.version = "0.0.0",
.minimum_zig_version = "0.14.0-dev.1359+e9a00ba7f",
.minimum_zig_version = "0.15.0-dev.1518+749f10af4",
.paths = .{
"build.zig",
"LICENSE",

View File

@@ -12,10 +12,14 @@ pub fn build(b: *std.Build) void {
const triangle_exe = b.addExecutable(.{
.name = "triangle",
.root_module = b.createModule(.{
.root_source_file = b.path("triangle.zig"),
.target = target,
.link_libc = true,
.optimize = optimize,
}),
// TODO: Remove this once x86_64 is stable
.use_llvm = true,
});
b.installArtifact(triangle_exe);
triangle_exe.linkSystemLibrary("glfw");
@@ -33,17 +37,18 @@ pub fn build(b: *std.Build) void {
if (use_zig_shaders) {
const spirv_target = b.resolveTargetQuery(.{
.cpu_arch = .spirv64,
.cpu_arch = .spirv32,
.os_tag = .vulkan,
.cpu_model = .{ .explicit = &std.Target.spirv.cpu.vulkan_v1_2 },
.cpu_features_add = std.Target.spirv.featureSet(&.{.int64}),
.ofmt = .spirv,
});
const vert_spv = b.addObject(.{
.name = "vertex_shader",
.root_module = b.createModule(.{
.root_source_file = b.path("shaders/vertex.zig"),
.target = spirv_target,
}),
.use_llvm = false,
});
triangle_exe.root_module.addAnonymousImport(
@@ -53,8 +58,10 @@ pub fn build(b: *std.Build) void {
const frag_spv = b.addObject(.{
.name = "fragment_shader",
.root_module = b.createModule(.{
.root_source_file = b.path("shaders/fragment.zig"),
.target = spirv_target,
}),
.use_llvm = false,
});
triangle_exe.root_module.addAnonymousImport(

View File

@@ -35,6 +35,7 @@ pub const GraphicsContext = struct {
vkb: BaseWrapper,
instance: Instance,
debug_messenger: vk.DebugUtilsMessengerEXT,
surface: vk.SurfaceKHR,
pdev: vk.PhysicalDevice,
props: vk.PhysicalDeviceProperties,
@@ -49,16 +50,17 @@ pub const GraphicsContext = struct {
self.allocator = allocator;
self.vkb = BaseWrapper.load(c.glfwGetInstanceProcAddress);
var extension_names = std.ArrayList([*:0]const u8).init(allocator);
defer extension_names.deinit();
// these extensions are to support vulkan in mac os
var extension_names: std.ArrayList([*:0]const u8) = .empty;
defer extension_names.deinit(allocator);
try extension_names.append(allocator, vk.extensions.ext_debug_utils.name);
// the following extensions are to support vulkan in mac os
// see https://github.com/glfw/glfw/issues/2335
try extension_names.append("VK_KHR_portability_enumeration");
try extension_names.append("VK_KHR_get_physical_device_properties2");
try extension_names.append(allocator, vk.extensions.khr_portability_enumeration.name);
try extension_names.append(allocator, vk.extensions.khr_get_physical_device_properties_2.name);
var glfw_exts_count: u32 = 0;
const glfw_exts = c.glfwGetRequiredInstanceExtensions(&glfw_exts_count);
try extension_names.appendSlice(@ptrCast(glfw_exts[0..glfw_exts_count]));
try extension_names.appendSlice(allocator, @ptrCast(glfw_exts[0..glfw_exts_count]));
const instance = try self.vkb.createInstance(&.{
.p_application_info = &.{
@@ -81,6 +83,22 @@ pub const GraphicsContext = struct {
self.instance = Instance.init(instance, vki);
errdefer self.instance.destroyInstance(null);
self.debug_messenger = try self.instance.createDebugUtilsMessengerEXT(&.{
.message_severity = .{
//.verbose_bit_ext = true,
//.info_bit_ext = true,
.warning_bit_ext = true,
.error_bit_ext = true,
},
.message_type = .{
.general_bit_ext = true,
.validation_bit_ext = true,
.performance_bit_ext = true,
},
.pfn_user_callback = &debugUtilsMessengerCallback,
.p_user_data = null,
}, null);
self.surface = try createSurface(self.instance, window);
errdefer self.instance.destroySurfaceKHR(self.surface, null);
@@ -107,6 +125,7 @@ pub const GraphicsContext = struct {
pub fn deinit(self: GraphicsContext) void {
self.dev.destroyDevice(null);
self.instance.destroySurfaceKHR(self.surface, null);
self.instance.destroyDebugUtilsMessengerEXT(self.debug_messenger, null);
self.instance.destroyInstance(null);
// Don't forget to free the tables to prevent a memory leak.
@@ -196,6 +215,17 @@ const QueueAllocation = struct {
present_family: u32,
};
fn debugUtilsMessengerCallback(severity: vk.DebugUtilsMessageSeverityFlagsEXT, msg_type: vk.DebugUtilsMessageTypeFlagsEXT, callback_data: ?*const vk.DebugUtilsMessengerCallbackDataEXT, _: ?*anyopaque) callconv(.c) vk.Bool32 {
const severity_str = if (severity.verbose_bit_ext) "verbose" else if (severity.info_bit_ext) "info" else if (severity.warning_bit_ext) "warning" else if (severity.error_bit_ext) "error" else "unknown";
const type_str = if (msg_type.general_bit_ext) "general" else if (msg_type.validation_bit_ext) "validation" else if (msg_type.performance_bit_ext) "performance" else if (msg_type.device_address_binding_bit_ext) "device addr" else "unknown";
const message: [*c]const u8 = if (callback_data) |cb_data| cb_data.p_message else "NO MESSAGE!";
std.debug.print("[{s}][{s}]. Message:\n {s}\n", .{ severity_str, type_str, message });
return .false;
}
fn pickPhysicalDevice(
instance: Instance,
allocator: Allocator,
@@ -253,7 +283,7 @@ fn allocateQueues(instance: Instance, pdev: vk.PhysicalDevice, allocator: Alloca
graphics_family = family;
}
if (present_family == null and (try instance.getPhysicalDeviceSurfaceSupportKHR(pdev, family, surface)) == vk.TRUE) {
if (present_family == null and (try instance.getPhysicalDeviceSurfaceSupportKHR(pdev, family, surface)) == .true) {
present_family = family;
}
}

View File

@@ -8,6 +8,5 @@ export fn main() callconv(.spirv_fragment) void {
gpu.location(&v_color, 0);
gpu.location(&f_color, 0);
const temp: @Vector(4, f32) = .{ v_color[0], v_color[1], v_color[2], 1.0 };
f_color = temp;
f_color = .{ v_color[0], v_color[1], v_color[2], 1.0 };
}

View File

@@ -60,7 +60,7 @@ pub const Swapchain = struct {
.pre_transform = caps.current_transform,
.composite_alpha = .{ .opaque_bit_khr = true },
.present_mode = present_mode,
.clipped = vk.TRUE,
.clipped = .true,
.old_swapchain = old_handle,
}, null) catch {
return error.SwapchainCreationFailed;
@@ -261,7 +261,7 @@ const SwapImage = struct {
}
fn waitForFence(self: SwapImage, gc: *const GraphicsContext) !void {
_ = try gc.dev.waitForFences(1, @ptrCast(&self.frame_fence), vk.TRUE, std.math.maxInt(u64));
_ = try gc.dev.waitForFences(1, @ptrCast(&self.frame_fence), .true, std.math.maxInt(u64));
}
};

View File

@@ -63,6 +63,23 @@ pub fn main() !void {
) orelse return error.WindowInitFailed;
defer c.glfwDestroyWindow(window);
// According to the GLFW docs:
//
// > Window systems put limits on window sizes. Very large or very small window dimensions
// > may be overridden by the window system on creation. Check the actual size after creation.
// -- https://www.glfw.org/docs/3.3/group__window.html#ga3555a418df92ad53f917597fe2f64aeb
//
// This happens in practice, for example, when using Wayland with a scaling factor that is not a
// divisor of the initial window size (see https://github.com/Snektron/vulkan-zig/pull/192).
// To fix it, just fetch the actual size here, after the windowing system has had the time to
// update the window.
extent.width, extent.height = blk: {
var w: c_int = undefined;
var h: c_int = undefined;
c.glfwGetFramebufferSize(window, &w, &h);
break :blk .{ @intCast(w), @intCast(h) };
};
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
@@ -123,6 +140,7 @@ pub fn main() !void {
);
defer destroyCommandBuffers(&gc, pool, allocator, cmdbufs);
var state: Swapchain.PresentState = .optimal;
while (c.glfwWindowShouldClose(window) == c.GLFW_FALSE) {
var w: c_int = undefined;
var h: c_int = undefined;
@@ -136,11 +154,6 @@ pub fn main() !void {
const cmdbuf = cmdbufs[swapchain.image_index];
const state = swapchain.present(cmdbuf) catch |err| switch (err) {
error.OutOfDateKHR => Swapchain.PresentState.suboptimal,
else => |narrow| return narrow,
};
if (state == .suboptimal or extent.width != @as(u32, @intCast(w)) or extent.height != @as(u32, @intCast(h))) {
extent.width = @intCast(w);
extent.height = @intCast(h);
@@ -161,6 +174,10 @@ pub fn main() !void {
framebuffers,
);
}
state = swapchain.present(cmdbuf) catch |err| switch (err) {
error.OutOfDateKHR => Swapchain.PresentState.suboptimal,
else => |narrow| return narrow,
};
c.glfwPollEvents();
}
@@ -397,7 +414,7 @@ fn createPipeline(
const piasci = vk.PipelineInputAssemblyStateCreateInfo{
.topology = .triangle_list,
.primitive_restart_enable = vk.FALSE,
.primitive_restart_enable = .false,
};
const pvsci = vk.PipelineViewportStateCreateInfo{
@@ -408,12 +425,12 @@ fn createPipeline(
};
const prsci = vk.PipelineRasterizationStateCreateInfo{
.depth_clamp_enable = vk.FALSE,
.rasterizer_discard_enable = vk.FALSE,
.depth_clamp_enable = .false,
.rasterizer_discard_enable = .false,
.polygon_mode = .fill,
.cull_mode = .{ .back_bit = true },
.front_face = .clockwise,
.depth_bias_enable = vk.FALSE,
.depth_bias_enable = .false,
.depth_bias_constant_factor = 0,
.depth_bias_clamp = 0,
.depth_bias_slope_factor = 0,
@@ -422,14 +439,14 @@ fn createPipeline(
const pmsci = vk.PipelineMultisampleStateCreateInfo{
.rasterization_samples = .{ .@"1_bit" = true },
.sample_shading_enable = vk.FALSE,
.sample_shading_enable = .false,
.min_sample_shading = 1,
.alpha_to_coverage_enable = vk.FALSE,
.alpha_to_one_enable = vk.FALSE,
.alpha_to_coverage_enable = .false,
.alpha_to_one_enable = .false,
};
const pcbas = vk.PipelineColorBlendAttachmentState{
.blend_enable = vk.FALSE,
.blend_enable = .false,
.src_color_blend_factor = .one,
.dst_color_blend_factor = .zero,
.color_blend_op = .add,
@@ -440,7 +457,7 @@ fn createPipeline(
};
const pcbsci = vk.PipelineColorBlendStateCreateInfo{
.logic_op_enable = vk.FALSE,
.logic_op_enable = .false,
.logic_op = .copy,
.attachment_count = 1,
.p_attachments = @ptrCast(&pcbas),

View File

@@ -52,13 +52,8 @@ pub fn isZigPrimitiveType(name: []const u8) bool {
return false;
}
pub fn writeIdentifier(writer: anytype, id: []const u8) !void {
// https://github.com/ziglang/zig/issues/2897
if (isZigPrimitiveType(id)) {
try writer.print("@\"{}\"", .{std.zig.fmtEscapes(id)});
} else {
try writer.print("{}", .{std.zig.fmtId(id)});
}
pub fn writeIdentifier(w: *std.Io.Writer, id: []const u8) !void {
try w.print("{f}", .{std.zig.fmtId(id)});
}
pub const CaseStyle = enum {
@@ -126,12 +121,12 @@ pub const SegmentIterator = struct {
pub const IdRenderer = struct {
tags: []const []const u8,
text_cache: std.ArrayList(u8),
text_cache: std.Io.Writer.Allocating,
pub fn init(allocator: Allocator, tags: []const []const u8) IdRenderer {
return .{
.tags = tags,
.text_cache = std.ArrayList(u8).init(allocator),
.text_cache = .init(allocator),
};
}
@@ -147,19 +142,19 @@ pub const IdRenderer = struct {
if (first) {
first = false;
} else {
try self.text_cache.append('_');
try self.text_cache.writer.writeByte('_');
}
for (segment) |c| {
try self.text_cache.append(if (screaming) std.ascii.toUpper(c) else std.ascii.toLower(c));
try self.text_cache.writer.writeByte(if (screaming) std.ascii.toUpper(c) else std.ascii.toLower(c));
}
}
if (tag) |name| {
try self.text_cache.append('_');
try self.text_cache.writer.writeByte('_');
for (name) |c| {
try self.text_cache.append(if (screaming) std.ascii.toUpper(c) else std.ascii.toLower(c));
try self.text_cache.writer.writeByte(if (screaming) std.ascii.toUpper(c) else std.ascii.toLower(c));
}
}
}
@@ -171,7 +166,7 @@ pub const IdRenderer = struct {
while (it.next()) |segment| {
var i: usize = 0;
while (i < segment.len and std.ascii.isDigit(segment[i])) {
try self.text_cache.append(segment[i]);
try self.text_cache.writer.writeByte(segment[i]);
i += 1;
}
@@ -180,34 +175,34 @@ pub const IdRenderer = struct {
}
if (i == 0 and lower_first) {
try self.text_cache.append(std.ascii.toLower(segment[i]));
try self.text_cache.writer.writeByte(std.ascii.toLower(segment[i]));
} else {
try self.text_cache.append(std.ascii.toUpper(segment[i]));
try self.text_cache.writer.writeByte(std.ascii.toUpper(segment[i]));
}
lower_first = false;
for (segment[i + 1 ..]) |c| {
try self.text_cache.append(std.ascii.toLower(c));
try self.text_cache.writer.writeByte(std.ascii.toLower(c));
}
}
if (tag) |name| {
try self.text_cache.appendSlice(name);
try self.text_cache.writer.writeAll(name);
}
}
pub fn renderFmt(self: *IdRenderer, out: anytype, comptime fmt: []const u8, args: anytype) !void {
self.text_cache.items.len = 0;
try std.fmt.format(self.text_cache.writer(), fmt, args);
try writeIdentifier(out, self.text_cache.items);
pub fn renderFmt(self: *IdRenderer, out: *std.Io.Writer, comptime fmt: []const u8, args: anytype) !void {
_ = self.text_cache.writer.consumeAll();
try self.text_cache.writer.print(fmt, args);
try writeIdentifier(out, self.text_cache.writer.buffered());
}
pub fn renderWithCase(self: *IdRenderer, out: anytype, case_style: CaseStyle, id: []const u8) !void {
pub fn renderWithCase(self: *IdRenderer, out: *std.Io.Writer, case_style: CaseStyle, id: []const u8) !void {
const tag = self.getAuthorTag(id);
// The trailing underscore doesn't need to be removed here as its removed by the SegmentIterator.
const adjusted_id = if (tag) |name| id[0 .. id.len - name.len] else id;
self.text_cache.items.len = 0;
_ = self.text_cache.writer.consumeAll();
switch (case_style) {
.snake => try self.renderSnake(false, adjusted_id, tag),
@@ -216,7 +211,7 @@ pub const IdRenderer = struct {
.camel => try self.renderCamel(false, adjusted_id, tag),
}
try writeIdentifier(out, self.text_cache.items);
try writeIdentifier(out, self.text_cache.writer.buffered());
}
pub fn getAuthorTag(self: IdRenderer, id: []const u8) ?[]const u8 {

View File

@@ -1,4 +1,5 @@
const std = @import("std");
const generator = @import("vulkan/generator.zig");
fn invalidUsage(prog_name: []const u8, comptime fmt: []const u8, args: anytype) noreturn {
@@ -8,27 +9,32 @@ fn invalidUsage(prog_name: []const u8, comptime fmt: []const u8, args: anytype)
}
fn reportParseErrors(tree: std.zig.Ast) !void {
const stderr = std.io.getStdErr().writer();
var buf: [1024]u8 = undefined;
var stderr = std.fs.File.stderr().writer(&buf);
const w = &stderr.interface;
for (tree.errors) |err| {
const loc = tree.tokenLocation(0, err.token);
try stderr.print("(vulkan-zig error):{}:{}: error: ", .{ loc.line + 1, loc.column + 1 });
try tree.renderError(err, stderr);
try stderr.print("\n{s}\n", .{tree.source[loc.line_start..loc.line_end]});
try w.print("(vulkan-zig error):{}:{}: error: ", .{ loc.line + 1, loc.column + 1 });
try tree.renderError(err, w);
try w.print("\n{s}\n", .{tree.source[loc.line_start..loc.line_end]});
for (0..loc.column) |_| {
try stderr.writeAll(" ");
try w.writeAll(" ");
}
try stderr.writeAll("^\n");
try w.writeAll("^\n");
}
}
fn oomPanic() noreturn {
@panic("Out of memory");
}
pub fn main() !void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const allocator = arena.allocator();
var args = std.process.argsWithAllocator(allocator) catch |err| switch (err) {
error.OutOfMemory => @panic("OOM"),
error.OutOfMemory => oomPanic(),
};
const prog_name = args.next() orelse "vulkan-zig-generator";
@@ -41,7 +47,9 @@ pub fn main() !void {
while (args.next()) |arg| {
if (std.mem.eql(u8, arg, "--help") or std.mem.eql(u8, arg, "-h")) {
@setEvalBranchQuota(2000);
std.io.getStdOut().writer().print(
var buf: [1024]u8 = undefined;
var w = std.fs.File.stdout().writer(&buf);
w.interface.print(
\\Utility to generate a Zig binding from the Vulkan XML API registry.
\\
\\The most recent Vulkan XML API registry can be obtained from
@@ -60,8 +68,7 @@ pub fn main() !void {
,
.{prog_name},
) catch |err| {
std.log.err("failed to write to stdout: {s}", .{@errorName(err)});
std.process.exit(1);
std.process.fatal("failed to write to stdout: {s}", .{@errorName(err)});
};
return;
} else if (std.mem.eql(u8, arg, "-a") or std.mem.eql(u8, arg, "--api")) {
@@ -95,21 +102,24 @@ pub fn main() !void {
};
const cwd = std.fs.cwd();
const xml_src = cwd.readFileAlloc(allocator, xml_path, std.math.maxInt(usize)) catch |err| {
std.log.err("failed to open input file '{s}' ({s})", .{ xml_path, @errorName(err) });
std.process.exit(1);
const xml_src = cwd.readFileAlloc(xml_path, allocator, .unlimited) catch |err| {
std.process.fatal("failed to open input file '{s}' ({s})", .{ xml_path, @errorName(err) });
};
const maybe_video_xml_src = if (maybe_video_xml_path) |video_xml_path|
cwd.readFileAlloc(allocator, video_xml_path, std.math.maxInt(usize)) catch |err| {
std.log.err("failed to open input file '{s}' ({s})", .{ video_xml_path, @errorName(err) });
std.process.exit(1);
cwd.readFileAlloc(video_xml_path, allocator, .unlimited) catch |err| {
std.process.fatal("failed to open input file '{s}' ({s})", .{ video_xml_path, @errorName(err) });
}
else
null;
var out_buffer = std.ArrayList(u8).init(allocator);
generator.generate(allocator, api, xml_src, maybe_video_xml_src, out_buffer.writer()) catch |err| switch (err) {
var aw: std.Io.Writer.Allocating = .init(allocator);
generator.generate(allocator, api, xml_src, maybe_video_xml_src, &aw.writer) catch |err| {
if (debug) {
return err;
}
switch (err) {
error.InvalidXml => {
std.log.err("invalid vulkan registry - invalid xml", .{});
std.log.err("please check that the correct vk.xml file is passed", .{});
@@ -126,14 +136,16 @@ pub fn main() !void {
std.log.err("please make a bug report at https://github.com/Snektron/vulkan-zig/issues/", .{});
std.process.exit(1);
},
error.OutOfMemory => @panic("oom"),
error.OutOfMemory, error.WriteFailed => oomPanic(),
}
};
out_buffer.append(0) catch @panic("oom");
aw.writer.writeByte(0) catch oomPanic();
const src = out_buffer.items[0 .. out_buffer.items.len - 1 :0];
const buffered = aw.writer.buffered();
const src = buffered[0 .. buffered.len - 1 :0];
const tree = std.zig.Ast.parse(allocator, src, .zig) catch |err| switch (err) {
error.OutOfMemory => @panic("oom"),
error.OutOfMemory => oomPanic(),
};
const formatted = if (tree.errors.len > 0) blk: {
@@ -143,22 +155,20 @@ pub fn main() !void {
std.log.err("or run with --debug to write out unformatted source", .{});
reportParseErrors(tree) catch |err| {
std.log.err("failed to dump ast errors: {s}", .{@errorName(err)});
std.process.exit(1);
std.process.fatal("failed to dump ast errors: {s}", .{@errorName(err)});
};
if (debug) {
break :blk src;
}
std.process.exit(1);
} else tree.render(allocator) catch |err| switch (err) {
error.OutOfMemory => @panic("oom"),
} else tree.renderAlloc(allocator) catch |err| switch (err) {
error.OutOfMemory => oomPanic(),
};
if (std.fs.path.dirname(out_path)) |dir| {
cwd.makePath(dir) catch |err| {
std.log.err("failed to create output directory '{s}' ({s})", .{ dir, @errorName(err) });
std.process.exit(1);
std.process.fatal("failed to create output directory '{s}' ({s})", .{ dir, @errorName(err) });
};
}
@@ -166,8 +176,7 @@ pub fn main() !void {
.sub_path = out_path,
.data = formatted,
}) catch |err| {
std.log.err("failed to write to output file '{s}' ({s})", .{ out_path, @errorName(err) });
std.process.exit(1);
std.process.fatal("failed to write to output file '{s}' ({s})", .{ out_path, @errorName(err) });
};
}

View File

@@ -457,8 +457,8 @@ fn parseFnPtrSuffix(allocator: Allocator, xctok: *XmlCTokenizer, return_type: Ty
// There is no good way to estimate the number of parameters beforehand.
// Fortunately, there are usually a relatively low number of parameters to a function pointer,
// so an ArrayList backed by an arena allocator is good enough.
var params = std.ArrayList(registry.Command.Param).init(allocator);
try params.append(.{
var params: std.ArrayList(registry.Command.Param) = .empty;
try params.append(allocator, .{
.name = first_param.name.?,
.param_type = first_param.decl_type,
.is_buffer_len = false,
@@ -473,7 +473,7 @@ fn parseFnPtrSuffix(allocator: Allocator, xctok: *XmlCTokenizer, return_type: Ty
}
const decl = try parseDeclaration(allocator, xctok, ptrs_optional);
try params.append(.{
try params.append(allocator, .{
.name = decl.name orelse return error.MissingTypeIdentifier,
.param_type = decl.decl_type,
.is_buffer_len = false,
@@ -482,7 +482,7 @@ fn parseFnPtrSuffix(allocator: Allocator, xctok: *XmlCTokenizer, return_type: Ty
}
_ = try xctok.nextNoEof();
command_ptr.decl_type.command_ptr.params = try params.toOwnedSlice();
command_ptr.decl_type.command_ptr.params = try params.toOwnedSlice(allocator);
return command_ptr;
}

View File

@@ -32,7 +32,7 @@ const EnumFieldMerger = struct {
fn putEnumExtension(self: *EnumFieldMerger, enum_name: []const u8, field: reg.Enum.Field) !void {
const res = try self.enum_extensions.getOrPut(self.arena, enum_name);
if (!res.found_existing) {
res.value_ptr.* = std.ArrayListUnmanaged(reg.Enum.Field){};
res.value_ptr.* = .empty;
}
try res.value_ptr.append(self.arena, field);
@@ -186,7 +186,7 @@ pub const Generator = struct {
self.registry.decls.len = i;
}
fn render(self: *Generator, writer: anytype) !void {
fn render(self: *Generator, writer: *std.Io.Writer) !void {
try renderRegistry(writer, self.arena.allocator(), &self.registry, &self.id_renderer, self.have_video);
}
};
@@ -204,7 +204,7 @@ pub fn generate(
api: Api,
spec_xml: []const u8,
maybe_video_spec_xml: ?[]const u8,
writer: anytype,
writer: *std.Io.Writer,
) !void {
const spec = xml.parse(allocator, spec_xml) catch |err| switch (err) {
error.InvalidDocument,

View File

@@ -28,11 +28,11 @@ pub fn parseXml(
const allocator = arena.allocator();
var decls: std.ArrayListUnmanaged(registry.Declaration) = .{};
var api_constants: std.ArrayListUnmanaged(registry.ApiConstant) = .{};
var tags: std.ArrayListUnmanaged(registry.Tag) = .{};
var features: std.ArrayListUnmanaged(registry.Feature) = .{};
var extensions: std.ArrayListUnmanaged(registry.Extension) = .{};
var decls: std.ArrayList(registry.Declaration) = .empty;
var api_constants: std.ArrayList(registry.ApiConstant) = .empty;
var tags: std.ArrayList(registry.Tag) = .empty;
var features: std.ArrayList(registry.Feature) = .empty;
var extensions: std.ArrayList(registry.Extension) = .empty;
try parseDeclarations(allocator, root, api, &decls);
try parseApiConstants(allocator, root, api, &api_constants);
@@ -66,7 +66,7 @@ fn parseDeclarations(
allocator: Allocator,
root: *xml.Element,
api: registry.Api,
decls: *std.ArrayListUnmanaged(registry.Declaration),
decls: *std.ArrayList(registry.Declaration),
) !void {
const types_elem = root.findChildByTag("types") orelse return error.InvalidRegistry;
try decls.ensureUnusedCapacity(allocator, types_elem.children.len);
@@ -84,7 +84,7 @@ fn parseTypes(
allocator: Allocator,
types_elem: *xml.Element,
api: registry.Api,
decls: *std.ArrayListUnmanaged(registry.Declaration),
decls: *std.ArrayList(registry.Declaration),
) !void {
var it = types_elem.findChildrenByTag("type");
while (it.next()) |ty| {
@@ -429,7 +429,7 @@ fn parseEnums(
allocator: Allocator,
root: *xml.Element,
api: registry.Api,
decls: *std.ArrayListUnmanaged(registry.Declaration),
decls: *std.ArrayList(registry.Declaration),
) !void {
var it = root.findChildrenByTag("enums");
while (it.next()) |enums| {
@@ -519,7 +519,7 @@ fn parseCommands(
allocator: Allocator,
commands_elem: *xml.Element,
api: registry.Api,
decls: *std.ArrayListUnmanaged(registry.Declaration),
decls: *std.ArrayList(registry.Declaration),
) !void {
var it = commands_elem.findChildrenByTag("command");
while (it.next()) |elem| {
@@ -630,7 +630,7 @@ fn parseApiConstants(
allocator: Allocator,
root: *xml.Element,
api: registry.Api,
api_constants: *std.ArrayListUnmanaged(registry.ApiConstant),
api_constants: *std.ArrayList(registry.ApiConstant),
) !void {
const maybe_enums = blk: {
var it = root.findChildrenByTag("enums");
@@ -672,7 +672,7 @@ fn parseDefines(
allocator: Allocator,
types: *xml.Element,
api: registry.Api,
api_constants: *std.ArrayListUnmanaged(registry.ApiConstant),
api_constants: *std.ArrayList(registry.ApiConstant),
) !void {
var it = types.findChildrenByTag("type");
while (it.next()) |ty| {
@@ -703,7 +703,7 @@ fn parseDefines(
fn parseTags(
allocator: Allocator,
root: *xml.Element,
tags: *std.ArrayListUnmanaged(registry.Tag),
tags: *std.ArrayList(registry.Tag),
) !void {
var tags_elem = root.findChildByTag("tags") orelse return;
try tags.ensureUnusedCapacity(allocator, tags_elem.children.len);
@@ -717,7 +717,7 @@ fn parseTags(
}
}
fn parseFeatures(allocator: Allocator, root: *xml.Element, api: registry.Api, features: *std.ArrayListUnmanaged(registry.Feature)) !void {
fn parseFeatures(allocator: Allocator, root: *xml.Element, api: registry.Api, features: *std.ArrayList(registry.Feature)) !void {
var it = root.findChildrenByTag("feature");
while (it.next()) |feature| {
if (!requiredByApi(feature, api))
@@ -881,7 +881,7 @@ fn parseExtensions(
allocator: Allocator,
root: *xml.Element,
api: registry.Api,
extensions: *std.ArrayListUnmanaged(registry.Extension),
extensions: *std.ArrayList(registry.Extension),
) !void {
const extensions_elem = root.findChildByTag("extensions") orelse return error.InvalidRegistry;
try extensions.ensureUnusedCapacity(allocator, extensions_elem.children.len);

View File

@@ -1,11 +1,12 @@
const std = @import("std");
const reg = @import("registry.zig");
const id_render = @import("../id_render.zig");
const cparse = @import("c_parse.zig");
const mem = std.mem;
const Allocator = mem.Allocator;
const id_render = @import("../id_render.zig");
const CaseStyle = id_render.CaseStyle;
const IdRenderer = id_render.IdRenderer;
const cparse = @import("c_parse.zig");
const reg = @import("registry.zig");
const preamble =
\\// This file is generated from the Khronos Vulkan XML API registry by vulkan-zig.
@@ -17,15 +18,15 @@ const preamble =
\\const Allocator = std.mem.Allocator;
\\
\\pub const vulkan_call_conv: std.builtin.CallingConvention = if (builtin.os.tag == .windows and builtin.cpu.arch == .x86)
\\ .Stdcall
\\ .winapi
\\ 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"
\\ // 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,
\\ // as it does by default when compiling for the armeabi-v7a NDK ABI.
\\ .AAPCSVFP
\\ .arm_aapcs_vfp
\\ else
\\ .C;
\\ .c;
// Note: Keep in sync with flag_functions
\\pub fn FlagsMixin(comptime FlagsType: type) type {
\\ return struct {
@@ -58,8 +59,6 @@ const preamble =
\\ return struct {
\\ pub fn format(
\\ self: FlagsType,
\\ comptime _: []const u8,
\\ _: std.fmt.FormatOptions,
\\ writer: anytype,
\\ ) !void {
\\ try writer.writeAll(@typeName(FlagsType) ++ "{");
@@ -336,11 +335,9 @@ pub fn trimVkNamespace(id: []const u8) []const u8 {
return id;
}
fn Renderer(comptime WriterType: type) type {
return struct {
const Renderer = struct {
const Self = @This();
const WriteError = WriterType.Error;
const RenderTypeInfoError = WriteError || std.fmt.ParseIntError || error{ OutOfMemory, InvalidRegistry };
const RenderTypeInfoError = std.Io.Writer.Error || std.fmt.ParseIntError || error{ OutOfMemory, InvalidRegistry };
const BitflagName = struct {
/// Name without FlagBits, so VkSurfaceTransformFlagBitsKHR
@@ -374,7 +371,7 @@ fn Renderer(comptime WriterType: type) type {
},
};
writer: WriterType,
writer: *std.Io.Writer,
allocator: Allocator,
registry: *const reg.Registry,
id_renderer: *IdRenderer,
@@ -383,7 +380,7 @@ fn Renderer(comptime WriterType: type) type {
have_video: bool,
fn init(
writer: WriterType,
writer: *std.Io.Writer,
allocator: Allocator,
registry: *const reg.Registry,
id_renderer: *IdRenderer,
@@ -851,6 +848,10 @@ fn Renderer(comptime WriterType: type) type {
}
fn renderDecl(self: *Self, decl: reg.Declaration) !void {
if (try self.renderSpecial(decl.name)) {
return;
}
switch (decl.decl_type) {
.container => |container| try self.renderContainer(decl.name, container),
.enumeration => |enumeration| try self.renderEnumeration(decl.name, enumeration),
@@ -864,10 +865,27 @@ fn Renderer(comptime WriterType: type) type {
}
}
fn renderSpecialContainer(self: *Self, name: []const u8) !bool {
fn renderAssign(self: *Self, name: []const u8) !void {
try self.writer.writeAll("pub const ");
try self.renderName(name);
try self.writer.writeAll(" = ");
}
fn renderSpecial(self: *Self, name: []const u8) !bool {
const maybe_author = self.id_renderer.getAuthorTag(name);
const basename = self.id_renderer.stripAuthorTag(name);
if (std.mem.eql(u8, basename, "VkAccelerationStructureInstance")) {
if (std.mem.eql(u8, basename, "VkBool32")) {
try self.renderAssign(name);
try self.writer.writeAll(
\\enum(i32) {
\\ false,
\\ true,
\\ _,
\\};
\\
);
} else if (std.mem.eql(u8, basename, "VkAccelerationStructureInstance")) {
try self.renderAssign(name);
try self.writer.print(
\\extern struct {{
\\ transform: TransformMatrix{s},
@@ -885,8 +903,8 @@ fn Renderer(comptime WriterType: type) type {
,
.{maybe_author orelse ""},
);
return true;
} else if (std.mem.eql(u8, basename, "VkAccelerationStructureSRTMotionInstance")) {
try self.renderAssign(name);
try self.writer.print(
\\extern struct {{
\\ transform_t0: SRTData{0s},
@@ -905,8 +923,8 @@ fn Renderer(comptime WriterType: type) type {
,
.{maybe_author orelse ""},
);
return true;
} else if (std.mem.eql(u8, basename, "VkAccelerationStructureMatrixMotionInstance")) {
try self.renderAssign(name);
try self.writer.print(
\\extern struct {{
\\ transform_t0: TransformMatrix{0s},
@@ -925,8 +943,8 @@ fn Renderer(comptime WriterType: type) type {
,
.{maybe_author orelse ""},
);
return true;
} else if (std.mem.eql(u8, basename, "VkClusterAccelerationStructureBuildTriangleClusterInfo")) {
try self.renderAssign(name);
try self.writer.print(
\\extern struct {{
\\ cluster_id: u32,
@@ -952,8 +970,8 @@ fn Renderer(comptime WriterType: type) type {
,
.{maybe_author orelse ""},
);
return true;
} else if (std.mem.eql(u8, basename, "VkClusterAccelerationStructureBuildTriangleClusterTemplateInfo")) {
try self.renderAssign(name);
try self.writer.print(
\\extern struct {{
\\ cluster_id: u32,
@@ -980,8 +998,8 @@ fn Renderer(comptime WriterType: type) type {
,
.{maybe_author orelse ""},
);
return true;
} else if (std.mem.eql(u8, basename, "VkClusterAccelerationStructureInstantiateClusterInfo")) {
try self.renderAssign(name);
try self.writer.print(
\\extern struct {{
\\ cluster_id_offset: u32,
@@ -995,10 +1013,11 @@ fn Renderer(comptime WriterType: type) type {
,
.{maybe_author orelse ""},
);
return true;
} else {
return false;
}
return false;
return true;
}
fn renderSimpleBitContainer(self: *Self, container: reg.Container) !bool {
@@ -1054,10 +1073,6 @@ fn Renderer(comptime WriterType: type) type {
return;
}
if (try self.renderSpecialContainer(name)) {
return;
}
for (container.fields) |field| {
if (field.bits != null) {
return error.UnhandledBitfieldStruct;
@@ -1112,7 +1127,7 @@ fn Renderer(comptime WriterType: type) type {
try self.writer.writeAll(" = .");
try self.writeIdentifierWithCase(.snake, stype["VK_STRUCTURE_TYPE_".len..]);
} else if (field.field_type == .name and mem.eql(u8, "VkBool32", field.field_type.name) and isFeatureStruct(name, container.extends)) {
try self.writer.writeAll(" = FALSE");
try self.writer.writeAll(" = .false");
} else if (field.is_optional) {
if (field.field_type == .name) {
const field_type_name = field.field_type.name;
@@ -1123,6 +1138,8 @@ fn Renderer(comptime WriterType: type) type {
try self.writer.writeAll(" = .{}");
} else if (decl_type == .typedef and decl_type.typedef == .command_ptr) {
try self.writer.writeAll(" = null");
} else if (mem.eql(u8, "VkBool32", field.field_type.name)) {
try self.writer.writeAll(" = .false");
} else if ((decl_type == .typedef and builtin_types.has(decl_type.typedef.name)) or
(decl_type == .foreign and builtin_types.has(field_type_name)))
{
@@ -1812,12 +1829,13 @@ fn Renderer(comptime WriterType: type) type {
}
fn extractReturns(self: *Self, command: reg.Command) ![]const ReturnValue {
var returns = std.ArrayList(ReturnValue).init(self.allocator);
const allocator = self.allocator;
var returns: std.ArrayList(ReturnValue) = .empty;
if (command.return_type.* == .name) {
const return_name = command.return_type.name;
if (!mem.eql(u8, return_name, "void") and !mem.eql(u8, return_name, "VkResult")) {
try returns.append(.{
try returns.append(allocator, .{
.name = "return_value",
.return_value_type = command.return_type.*,
.origin = .inner_return_value,
@@ -1830,7 +1848,7 @@ fn Renderer(comptime WriterType: type) type {
return error.InvalidRegistry;
}
try returns.append(.{
try returns.append(allocator, .{
.name = "result",
.return_value_type = command.return_type.*,
.origin = .inner_return_value,
@@ -1841,7 +1859,7 @@ fn Renderer(comptime WriterType: type) type {
for (command.params) |param| {
if ((try self.classifyParam(param)) == .out_pointer) {
try returns.append(.{
try returns.append(allocator, .{
.name = derefName(param.name),
.return_value_type = param.param_type.pointer.child.*,
.origin = .parameter,
@@ -1849,7 +1867,7 @@ fn Renderer(comptime WriterType: type) type {
}
}
return try returns.toOwnedSlice();
return try returns.toOwnedSlice(allocator);
}
fn renderReturnStructName(self: *Self, command_name: []const u8) !void {
@@ -2115,16 +2133,15 @@ fn Renderer(comptime WriterType: type) type {
}
}
};
}
pub fn render(
writer: anytype,
writer: *std.Io.Writer,
allocator: Allocator,
registry: *const reg.Registry,
id_renderer: *IdRenderer,
have_video: bool,
) !void {
var renderer = try Renderer(@TypeOf(writer)).init(writer, allocator, registry, id_renderer, have_video);
var renderer = try Renderer.init(writer, allocator, registry, id_renderer, have_video);
defer renderer.deinit();
try renderer.render();
}

View File

@@ -439,15 +439,15 @@ fn parseElement(parser: *Parser, alloc: Allocator, comptime kind: ElementKind) !
},
};
var attributes = std.ArrayList(Attribute).init(alloc);
defer attributes.deinit();
var attributes: std.ArrayList(Attribute) = .empty;
defer attributes.deinit(alloc);
var children = std.ArrayList(Content).init(alloc);
defer children.deinit();
var children: std.ArrayList(Content) = .empty;
defer children.deinit(alloc);
while (parser.eatWs()) {
const attr = (try parseAttr(parser, alloc)) orelse break;
try attributes.append(attr);
try attributes.append(alloc, attr);
}
switch (kind) {
@@ -464,7 +464,7 @@ fn parseElement(parser: *Parser, alloc: Allocator, comptime kind: ElementKind) !
}
const content = try parseContent(parser, alloc);
try children.append(content);
try children.append(alloc, content);
}
const closing_tag = try parseNameNoDupe(parser);
@@ -481,8 +481,8 @@ fn parseElement(parser: *Parser, alloc: Allocator, comptime kind: ElementKind) !
const element = try alloc.create(Element);
element.* = .{
.tag = try alloc.dupe(u8, tag),
.attributes = try attributes.toOwnedSlice(),
.children = try children.toOwnedSlice(),
.attributes = try attributes.toOwnedSlice(alloc),
.children = try children.toOwnedSlice(alloc),
};
return element;
}

View File

@@ -21,9 +21,6 @@ pub const MTLTexture_id = u32;
pub const MTLSharedEvent_id = u32;
pub const IOSurfaceRef = u32;
// For some reason these types are exported in a different header, and not described in vk.xml.
// If we are not also generating these, the user will have to manually specify them.
pub usingnamespace if (!vk.have_vulkan_video) struct {
pub const StdVideoH264ProfileIdc = u32;
pub const StdVideoH264LevelIdc = u32;
pub const StdVideoH264ChromaFormatIdc = u32;
@@ -94,7 +91,6 @@ pub usingnamespace if (!vk.have_vulkan_video) struct {
pub const StdVideoEncodeH265ReferenceInfoFlags = u32;
pub const StdVideoEncodeH265ReferenceModificationFlags = u32;
pub const StdVideoEncodeAV1OperatingPointInfo = u32;
} else struct {};
comptime {
@setEvalBranchQuota(1000000);