Merge pull request #175 from Snektron/vulkan-video

Add vulkan video support
This commit is contained in:
Robin Voetter
2025-03-15 02:54:44 +01:00
committed by GitHub
11 changed files with 544 additions and 321 deletions

View File

@@ -31,11 +31,16 @@ jobs:
sudo apt install shaderc libglfw3 libglfw3-dev
- name: Fetch latest vk.xml
run: wget https://raw.githubusercontent.com/KhronosGroup/Vulkan-Docs/main/xml/vk.xml
run: |
wget https://raw.githubusercontent.com/KhronosGroup/Vulkan-Docs/main/xml/vk.xml
wget https://raw.githubusercontent.com/KhronosGroup/Vulkan-Docs/main/xml/video.xml
- name: Test and install with latest zig & latest vk.xml
run: zig build test install -Dregistry=$(pwd)/vk.xml
- name: Test and install with latest zig & latest vk.xml & latest video.xml
run: zig build test install -p zig-out-video -Dregistry=$(pwd)/vk.xml -Dvideo=$(pwd)/video.xml
- name: Build example with latest zig & vk.xml from dependency
run: zig build --build-file $(pwd)/examples/build.zig
@@ -46,5 +51,7 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: vk.zig
path: zig-out/src/vk.zig
path: |
zig-out/src/vk.zig
zig-out-video/src/vk.zig
if-no-files-found: error

View File

@@ -382,6 +382,19 @@ See [examples/build.zig](examples/build.zig) for a working example.
For more advanced shader compiler usage, one may consider a library such as [shader_compiler](https://github.com/Games-by-Mason/shader_compiler).
### Vulkan Video
Vulkan-zig also supports generating Vulkan Video bindings. To do this, one additionally pass `--video <video.xml>` to the generator, or pass `-Dvideo=<video.xml>` to build.zig. If using vulkan-zig via the Zig package manager, the following also works:
```zig
const vulkan_headers = b.dependency("vulkan_headers");
const vulkan = b.dependency("vulkan_zig", .{
.registry = vulkan_headers.path("registry/vk.xml"),
.video = vulkan_headers.path("registery/video.xml"),
}).module("vulkan-zig");
```
The Vulkan Video bindings are not generated by default. In this case, the relevant definitions must be supplied by the user. See [platform types](#platform-types) for how this is done.
## Limitations
* vulkan-zig has as of yet no functionality for selecting feature levels and extensions when generating bindings. This is because when an extension is promoted to Vulkan core, its fields and commands are renamed to lose the extensions author tag (for example, VkSemaphoreWaitFlagsKHR was renamed to VkSemaphoreWaitFlags when it was promoted from an extension to Vulkan 1.2 core). This leads to inconsistencies when only items from up to a certain feature level is included, as these promoted items then need to re-gain a tag.

View File

@@ -4,6 +4,7 @@ pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const maybe_registry = b.option(std.Build.LazyPath, "registry", "Set the path to the Vulkan registry (vk.xml)");
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");
// Using the package manager, this artifact can be obtained by the user
@@ -23,6 +24,11 @@ pub fn build(b: *std.Build) void {
if (maybe_registry) |registry| {
const vk_generate_cmd = b.addRunArtifact(generator_exe);
if (maybe_video) |video| {
vk_generate_cmd.addArg("--video");
vk_generate_cmd.addFileArg(video);
}
vk_generate_cmd.addFileArg(registry);
const vk_zig = vk_generate_cmd.addOutputFileArg("vk.zig");

View File

@@ -58,10 +58,10 @@ pub const GraphicsContext = struct {
const app_info = vk.ApplicationInfo{
.p_application_name = app_name,
.application_version = vk.makeApiVersion(0, 0, 0, 0),
.application_version = @bitCast(vk.makeApiVersion(0, 0, 0, 0)),
.p_engine_name = app_name,
.engine_version = vk.makeApiVersion(0, 0, 0, 0),
.api_version = vk.API_VERSION_1_2,
.engine_version = @bitCast(vk.makeApiVersion(0, 0, 0, 0)),
.api_version = @bitCast(vk.API_VERSION_1_2),
};
const instance = try self.vkb.createInstance(&.{

View File

@@ -22,7 +22,7 @@ fn reportParseErrors(tree: std.zig.Ast) !void {
}
}
pub fn main() void {
pub fn main() !void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const allocator = arena.allocator();
@@ -34,6 +34,7 @@ pub fn main() void {
var maybe_xml_path: ?[]const u8 = null;
var maybe_out_path: ?[]const u8 = null;
var maybe_video_xml_path: ?[]const u8 = null;
var debug: bool = false;
var api = generator.Api.vulkan;
@@ -52,7 +53,9 @@ pub fn main() void {
\\Options:
\\-h --help show this message and exit.
\\-a --api <api> Generate API for 'vulkan' or 'vulkansc'. Defaults to 'vulkan'.
\\--debug Write out unformatted source if does not parse correctly.
\\--debug Write out unformatted source if does not parse correctly.
\\--video <path> Also gnerate Vulkan Video API bindings from video.xml
\\ registry at <path>.
\\
,
.{prog_name},
@@ -68,12 +71,16 @@ pub fn main() void {
api = std.meta.stringToEnum(generator.Api, api_str) orelse {
invalidUsage(prog_name, "invalid api '{s}'", .{api_str});
};
} else if (std.mem.eql(u8, arg, "--debug")) {
debug = true;
} else if (std.mem.eql(u8, arg, "--video")) {
maybe_video_xml_path = args.next() orelse {
invalidUsage(prog_name, "{s} expects argument <path>", .{arg});
};
} else if (maybe_xml_path == null) {
maybe_xml_path = arg;
} else if (maybe_out_path == null) {
maybe_out_path = arg;
} else if (std.mem.eql(u8, arg, "--debug")) {
debug = true;
} else {
invalidUsage(prog_name, "superficial argument '{s}'", .{arg});
}
@@ -93,8 +100,16 @@ pub fn main() void {
std.process.exit(1);
};
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);
}
else
null;
var out_buffer = std.ArrayList(u8).init(allocator);
generator.generate(allocator, api, xml_src, out_buffer.writer()) catch |err| switch (err) {
generator.generate(allocator, api, xml_src, maybe_video_xml_src, out_buffer.writer()) catch |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", .{});

View File

@@ -90,10 +90,18 @@ pub const CTokenizer = struct {
const start = self.offset;
_ = self.consumeNoEof();
const hex = self.peek() == 'x';
if (hex) {
_ = self.consumeNoEof();
}
while (true) {
const c = self.peek() orelse break;
switch (c) {
switch (self.peek() orelse break) {
'0'...'9' => _ = self.consumeNoEof(),
'A'...'F', 'a'...'f' => {
if (!hex) break;
_ = self.consumeNoEof();
},
else => break,
}
}
@@ -164,7 +172,12 @@ pub const XmlCTokenizer = struct {
}
fn elemToToken(elem: *xml.Element) !?Token {
if (elem.children.len != 1 or elem.children[0] != .char_data) {
// Sometimes we encounter empty comment tags. Filter those out
// by early returning here, otherwise the next check will
// determine that the input is not valid XML.
if (mem.eql(u8, elem.tag, "comment")) {
return null;
} else if (elem.children.len != 1 or elem.children[0] != .char_data) {
return error.InvalidXml;
}
@@ -175,8 +188,6 @@ pub const XmlCTokenizer = struct {
return Token{ .kind = .enum_name, .text = text };
} else if (mem.eql(u8, elem.tag, "name")) {
return Token{ .kind = .name, .text = text };
} else if (mem.eql(u8, elem.tag, "comment")) {
return null;
} else {
return error.InvalidTag;
}
@@ -530,7 +541,10 @@ fn parseArrayDeclarator(xctok: *XmlCTokenizer) !?ArraySize {
error.InvalidCharacter => unreachable,
},
},
.enum_name => .{ .alias = size_tok.text },
// Sometimes, arrays are declared as `<type>T</type> <name>aa</name>[<enum>SIZE</enum>]`,
// and sometimes just as `<type>T</type> <name>aa</name>[SIZE]`, so we have to account
// for both `.enum_name` and `.id` here.
.enum_name, .id => .{ .alias = size_tok.text },
else => return error.InvalidSyntax,
};
@@ -538,7 +552,7 @@ fn parseArrayDeclarator(xctok: *XmlCTokenizer) !?ArraySize {
return size;
}
pub fn parseVersion(xctok: *XmlCTokenizer) ![4][]const u8 {
pub fn parseVersion(xctok: *XmlCTokenizer) !registry.ApiConstant.Value {
_ = try xctok.expect(.hash);
const define = try xctok.expect(.id);
if (!mem.eql(u8, define.text, "define")) {
@@ -547,12 +561,22 @@ pub fn parseVersion(xctok: *XmlCTokenizer) ![4][]const u8 {
_ = try xctok.expect(.name);
const vk_make_version = try xctok.expect(.type_name);
if (!mem.eql(u8, vk_make_version.text, "VK_MAKE_API_VERSION")) {
if (mem.eql(u8, vk_make_version.text, "VK_MAKE_API_VERSION")) {
return .{
.version = try parseVersionValues(xctok, 4),
};
} else if (mem.eql(u8, vk_make_version.text, "VK_MAKE_VIDEO_STD_VERSION")) {
return .{
.video_std_version = try parseVersionValues(xctok, 3),
};
} else {
return error.NotVersion;
}
}
fn parseVersionValues(xctok: *XmlCTokenizer, comptime count: usize) ![count][]const u8 {
_ = try xctok.expect(.lparen);
var version: [4][]const u8 = undefined;
var version: [count][]const u8 = undefined;
for (&version, 0..) |*part, i| {
if (i != 0) {
_ = try xctok.expect(.comma);

View File

@@ -10,11 +10,13 @@ const FeatureLevel = reg.FeatureLevel;
const EnumFieldMerger = struct {
const EnumExtensionMap = std.StringArrayHashMapUnmanaged(std.ArrayListUnmanaged(reg.Enum.Field));
const ApiConstantMap = std.StringArrayHashMapUnmanaged(reg.ApiConstant);
const FieldSet = std.StringArrayHashMapUnmanaged(void);
arena: Allocator,
registry: *reg.Registry,
enum_extensions: EnumExtensionMap,
api_constants: ApiConstantMap,
field_set: FieldSet,
fn init(arena: Allocator, registry: *reg.Registry) EnumFieldMerger {
@@ -22,6 +24,7 @@ const EnumFieldMerger = struct {
.arena = arena,
.registry = registry,
.enum_extensions = .{},
.api_constants = .{},
.field_set = .{},
};
}
@@ -38,7 +41,17 @@ const EnumFieldMerger = struct {
fn addRequires(self: *EnumFieldMerger, reqs: []const reg.Require) !void {
for (reqs) |req| {
for (req.extends) |enum_ext| {
try self.putEnumExtension(enum_ext.extends, enum_ext.field);
switch (enum_ext.value) {
.field => try self.putEnumExtension(enum_ext.extends, enum_ext.value.field),
.new_api_constant_expr => |expr| try self.api_constants.put(
self.arena,
enum_ext.extends,
.{
.name = enum_ext.extends,
.value = .{ .expr = expr },
},
),
}
}
}
}
@@ -76,6 +89,10 @@ const EnumFieldMerger = struct {
}
fn merge(self: *EnumFieldMerger) !void {
for (self.registry.api_constants) |api_constant| {
try self.api_constants.put(self.arena, api_constant.name, api_constant);
}
for (self.registry.features) |feature| {
try self.addRequires(feature.requires);
}
@@ -91,6 +108,8 @@ const EnumFieldMerger = struct {
try self.mergeEnumFields(decl.name, &decl.decl_type.enumeration);
}
}
self.registry.api_constants = self.api_constants.values();
}
};
@@ -98,9 +117,10 @@ pub const Generator = struct {
arena: std.heap.ArenaAllocator,
registry: reg.Registry,
id_renderer: IdRenderer,
have_video: bool,
fn init(allocator: Allocator, spec: *xml.Element, api: reg.Api) !Generator {
const result = try parseXml(allocator, spec, api);
fn init(allocator: Allocator, spec: *xml.Element, maybe_video_spec: ?*xml.Element, api: reg.Api) !Generator {
const result = try parseXml(allocator, spec, maybe_video_spec, api);
const tags = try allocator.alloc([]const u8, result.registry.tags.len);
for (tags, result.registry.tags) |*tag, registry_tag| tag.* = registry_tag.name;
@@ -109,6 +129,7 @@ pub const Generator = struct {
.arena = result.arena,
.registry = result.registry,
.id_renderer = IdRenderer.init(allocator, tags),
.have_video = maybe_video_spec != null,
};
}
@@ -166,7 +187,7 @@ pub const Generator = struct {
}
fn render(self: *Generator, writer: anytype) !void {
try renderRegistry(writer, self.arena.allocator(), &self.registry, &self.id_renderer);
try renderRegistry(writer, self.arena.allocator(), &self.registry, &self.id_renderer, self.have_video);
}
};
@@ -178,7 +199,7 @@ pub const Api = reg.Api;
/// and the resulting binding is written to `writer`. `allocator` will be used to allocate temporary
/// internal datastructures - mostly via an ArenaAllocator, but sometimes a hashmap uses this allocator
/// directly. `api` is the API to generate the bindings for, usually `.vulkan`.
pub fn generate(allocator: Allocator, api: Api, spec_xml: []const u8, writer: anytype) !void {
pub fn generate(allocator: Allocator, api: Api, spec_xml: []const u8, maybe_video_spec_xml: ?[]const u8, writer: anytype) !void {
const spec = xml.parse(allocator, spec_xml) catch |err| switch (err) {
error.InvalidDocument,
error.UnexpectedEof,
@@ -195,7 +216,26 @@ pub fn generate(allocator: Allocator, api: Api, spec_xml: []const u8, writer: an
};
defer spec.deinit();
var gen = Generator.init(allocator, spec.root, api) catch |err| switch (err) {
const maybe_video_spec_root = if (maybe_video_spec_xml) |video_spec_xml| blk: {
const video_spec = xml.parse(allocator, video_spec_xml) catch |err| switch (err) {
error.InvalidDocument,
error.UnexpectedEof,
error.UnexpectedCharacter,
error.IllegalCharacter,
error.InvalidEntity,
error.InvalidName,
error.InvalidStandaloneValue,
error.NonMatchingClosingTag,
error.UnclosedComment,
error.UnclosedValue,
=> return error.InvalidXml,
error.OutOfMemory => return error.OutOfMemory,
};
break :blk video_spec.root;
} else null;
var gen = Generator.init(allocator, spec.root, maybe_video_spec_root, api) catch |err| switch (err) {
error.InvalidXml,
error.InvalidCharacter,
error.Overflow,

View File

@@ -17,18 +17,43 @@ pub const ParseResult = struct {
}
};
pub fn parseXml(backing_allocator: Allocator, root: *xml.Element, api: registry.Api) !ParseResult {
pub fn parseXml(
backing_allocator: Allocator,
root: *xml.Element,
maybe_video_root: ?*xml.Element,
api: registry.Api,
) !ParseResult {
var arena = ArenaAllocator.init(backing_allocator);
errdefer arena.deinit();
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) = .{};
try parseDeclarations(allocator, root, api, &decls);
try parseApiConstants(allocator, root, api, &api_constants);
try parseTags(allocator, root, &tags);
try parseFeatures(allocator, root, api, &features);
try parseExtensions(allocator, root, api, &extensions);
if (maybe_video_root) |video_root| {
try parseDeclarations(allocator, video_root, api, &decls);
try parseApiConstants(allocator, video_root, api, &api_constants);
try parseTags(allocator, video_root, &tags);
try parseFeatures(allocator, video_root, api, &features);
try parseExtensions(allocator, video_root, api, &extensions);
}
const reg = registry.Registry{
.decls = try parseDeclarations(allocator, root, api),
.api_constants = try parseApiConstants(allocator, root, api),
.tags = try parseTags(allocator, root),
.features = try parseFeatures(allocator, root, api),
.extensions = try parseExtensions(allocator, root, api),
.decls = decls.items,
.api_constants = api_constants.items,
.tags = tags.items,
.features = features.items,
.extensions = extensions.items,
};
return ParseResult{
@@ -37,25 +62,33 @@ pub fn parseXml(backing_allocator: Allocator, root: *xml.Element, api: registry.
};
}
fn parseDeclarations(allocator: Allocator, root: *xml.Element, api: registry.Api) ![]registry.Declaration {
fn parseDeclarations(
allocator: Allocator,
root: *xml.Element,
api: registry.Api,
decls: *std.ArrayListUnmanaged(registry.Declaration),
) !void {
const types_elem = root.findChildByTag("types") orelse return error.InvalidRegistry;
const commands_elem = root.findChildByTag("commands") orelse return error.InvalidRegistry;
try decls.ensureUnusedCapacity(allocator, types_elem.children.len);
const decl_upper_bound = types_elem.children.len + commands_elem.children.len;
const decls = try allocator.alloc(registry.Declaration, decl_upper_bound);
try parseTypes(allocator, types_elem, api, decls);
try parseEnums(allocator, root, api, decls);
var count: usize = 0;
count += try parseTypes(allocator, decls, types_elem, api);
count += try parseEnums(allocator, decls[count..], root, api);
count += try parseCommands(allocator, decls[count..], commands_elem, api);
return decls[0..count];
if (root.findChildByTag("commands")) |commands_elem| {
try decls.ensureUnusedCapacity(allocator, commands_elem.children.len);
try parseCommands(allocator, commands_elem, api, decls);
}
}
fn parseTypes(allocator: Allocator, out: []registry.Declaration, types_elem: *xml.Element, api: registry.Api) !usize {
var i: usize = 0;
fn parseTypes(
allocator: Allocator,
types_elem: *xml.Element,
api: registry.Api,
decls: *std.ArrayListUnmanaged(registry.Declaration),
) !void {
var it = types_elem.findChildrenByTag("type");
while (it.next()) |ty| {
out[i] = blk: {
try decls.append(allocator, blk: {
if (!requiredByApi(ty, api))
continue;
@@ -80,12 +113,8 @@ fn parseTypes(allocator: Allocator, out: []registry.Declaration, types_elem: *xm
}
continue;
};
i += 1;
});
}
return i;
}
fn parseForeigntype(ty: *xml.Element) !registry.Declaration {
@@ -388,8 +417,7 @@ fn parseEnumAlias(elem: *xml.Element) !?registry.Declaration {
return null;
}
fn parseEnums(allocator: Allocator, out: []registry.Declaration, root: *xml.Element, api: registry.Api) !usize {
var i: usize = 0;
fn parseEnums(allocator: Allocator, root: *xml.Element, api: registry.Api, decls: *std.ArrayListUnmanaged(registry.Declaration)) !void {
var it = root.findChildrenByTag("enums");
while (it.next()) |enums| {
const name = enums.getAttribute("name") orelse return error.InvalidRegistry;
@@ -397,14 +425,11 @@ fn parseEnums(allocator: Allocator, out: []registry.Declaration, root: *xml.Elem
continue;
}
out[i] = .{
try decls.append(allocator, .{
.name = name,
.decl_type = .{ .enumeration = try parseEnumFields(allocator, enums, api) },
};
i += 1;
});
}
return i;
}
fn parseEnumFields(allocator: Allocator, elem: *xml.Element, api: registry.Api) !registry.Enum {
@@ -477,18 +502,19 @@ fn parseEnumField(field: *xml.Element) !registry.Enum.Field {
};
}
fn parseCommands(allocator: Allocator, out: []registry.Declaration, commands_elem: *xml.Element, api: registry.Api) !usize {
var i: usize = 0;
fn parseCommands(
allocator: Allocator,
commands_elem: *xml.Element,
api: registry.Api,
decls: *std.ArrayListUnmanaged(registry.Declaration),
) !void {
var it = commands_elem.findChildrenByTag("command");
while (it.next()) |elem| {
if (!requiredByApi(elem, api))
continue;
out[i] = try parseCommand(allocator, elem, api);
i += 1;
try decls.append(allocator, try parseCommand(allocator, elem, api));
}
return i;
}
fn splitCommaAlloc(allocator: Allocator, text: []const u8) ![][]const u8 {
@@ -587,8 +613,13 @@ fn parseCommand(allocator: Allocator, elem: *xml.Element, api: registry.Api) !re
};
}
fn parseApiConstants(allocator: Allocator, root: *xml.Element, api: registry.Api) ![]registry.ApiConstant {
var enums = blk: {
fn parseApiConstants(
allocator: Allocator,
root: *xml.Element,
api: registry.Api,
api_constants: *std.ArrayListUnmanaged(registry.ApiConstant),
) !void {
const maybe_enums = blk: {
var it = root.findChildrenByTag("enums");
while (it.next()) |child| {
const name = child.getAttribute("name") orelse continue;
@@ -597,52 +628,39 @@ fn parseApiConstants(allocator: Allocator, root: *xml.Element, api: registry.Api
}
}
return error.InvalidRegistry;
break :blk null;
};
var types = root.findChildByTag("types") orelse return error.InvalidRegistry;
const n_defines = blk: {
var n_defines: usize = 0;
var it = types.findChildrenByTag("type");
while (it.next()) |ty| {
if (ty.getAttribute("category")) |category| {
if (mem.eql(u8, category, "define")) {
n_defines += 1;
}
}
if (maybe_enums) |enums| {
var it = enums.findChildrenByTag("enum");
while (it.next()) |constant| {
if (!requiredByApi(constant, api))
continue;
const expr = if (constant.getAttribute("value")) |expr|
expr
else if (constant.getAttribute("alias")) |alias|
alias
else
return error.InvalidRegistry;
try api_constants.append(allocator, .{
.name = constant.getAttribute("name") orelse return error.InvalidRegistry,
.value = .{ .expr = expr },
});
}
break :blk n_defines;
};
const constants = try allocator.alloc(registry.ApiConstant, enums.children.len + n_defines);
var i: usize = 0;
var it = enums.findChildrenByTag("enum");
while (it.next()) |constant| {
if (!requiredByApi(constant, api))
continue;
const expr = if (constant.getAttribute("value")) |expr|
expr
else if (constant.getAttribute("alias")) |alias|
alias
else
return error.InvalidRegistry;
constants[i] = .{
.name = constant.getAttribute("name") orelse return error.InvalidRegistry,
.value = .{ .expr = expr },
};
i += 1;
}
i += try parseDefines(types, constants[i..], api);
return constants[0..i];
const types = root.findChildByTag("types") orelse return error.InvalidRegistry;
try parseDefines(allocator, types, api, api_constants);
}
fn parseDefines(types: *xml.Element, out: []registry.ApiConstant, api: registry.Api) !usize {
var i: usize = 0;
fn parseDefines(
allocator: Allocator,
types: *xml.Element,
api: registry.Api,
api_constants: *std.ArrayListUnmanaged(registry.ApiConstant),
) !void {
var it = types.findChildrenByTag("type");
while (it.next()) |ty| {
if (!requiredByApi(ty, api))
@@ -655,58 +673,45 @@ fn parseDefines(types: *xml.Element, out: []registry.ApiConstant, api: registry.
const name = ty.getCharData("name") orelse continue;
if (mem.eql(u8, name, "VK_HEADER_VERSION") or mem.eql(u8, name, "VKSC_API_VARIANT")) {
out[i] = .{
try api_constants.append(allocator, .{
.name = name,
.value = .{ .expr = mem.trim(u8, ty.children[2].char_data, " ") },
};
});
} else {
var xctok = cparse.XmlCTokenizer.init(ty);
out[i] = .{
try api_constants.append(allocator, .{
.name = name,
.value = .{ .version = cparse.parseVersion(&xctok) catch continue },
};
.value = cparse.parseVersion(&xctok) catch continue,
});
}
i += 1;
}
return i;
}
fn parseTags(allocator: Allocator, root: *xml.Element) ![]registry.Tag {
var tags_elem = root.findChildByTag("tags") orelse return error.InvalidRegistry;
const tags = try allocator.alloc(registry.Tag, tags_elem.children.len);
fn parseTags(
allocator: Allocator,
root: *xml.Element,
tags: *std.ArrayListUnmanaged(registry.Tag),
) !void {
var tags_elem = root.findChildByTag("tags") orelse return;
try tags.ensureUnusedCapacity(allocator, tags_elem.children.len);
var i: usize = 0;
var it = tags_elem.findChildrenByTag("tag");
while (it.next()) |tag| {
tags[i] = .{
tags.appendAssumeCapacity(.{
.name = tag.getAttribute("name") orelse return error.InvalidRegistry,
.author = tag.getAttribute("author") orelse return error.InvalidRegistry,
};
i += 1;
});
}
return tags[0..i];
}
fn parseFeatures(allocator: Allocator, root: *xml.Element, api: registry.Api) ![]registry.Feature {
fn parseFeatures(allocator: Allocator, root: *xml.Element, api: registry.Api, features: *std.ArrayListUnmanaged(registry.Feature)) !void {
var it = root.findChildrenByTag("feature");
var count: usize = 0;
while (it.next()) |_| count += 1;
const features = try allocator.alloc(registry.Feature, count);
var i: usize = 0;
it = root.findChildrenByTag("feature");
while (it.next()) |feature| {
if (!requiredByApi(feature, api))
continue;
features[i] = try parseFeature(allocator, feature, api);
i += 1;
try features.append(allocator, try parseFeature(allocator, feature, api));
}
return features[0..i];
}
fn parseFeature(allocator: Allocator, feature: *xml.Element, api: registry.Api) !registry.Feature {
@@ -736,11 +741,24 @@ fn parseFeature(allocator: Allocator, feature: *xml.Element, api: registry.Api)
fn parseEnumExtension(elem: *xml.Element, parent_extnumber: ?u31) !?registry.Require.EnumExtension {
// check for either _SPEC_VERSION or _EXTENSION_NAME
const extends = elem.getAttribute("extends") orelse return null;
const name = elem.getAttribute("name") orelse return error.InvalidRegistry;
if (std.mem.endsWith(u8, name, "_SPEC_VERSION") or std.mem.endsWith(u8, name, "_EXTENSION_NAME")) {
return null;
}
const extends = elem.getAttribute("extends") orelse {
const expr = elem.getAttribute("value") orelse return null;
// This adds a value to the 'API constants' set
return registry.Require.EnumExtension{
.extends = name,
.extnumber = null,
.value = .{ .new_api_constant_expr = expr },
};
};
if (elem.getAttribute("offset")) |offset_str| {
const offset = try std.fmt.parseInt(u31, offset_str, 10);
const name = elem.getAttribute("name") orelse return error.InvalidRegistry;
const extnumber = if (elem.getAttribute("extnumber")) |num|
try std.fmt.parseInt(u31, num, 10)
else
@@ -763,9 +781,11 @@ fn parseEnumExtension(elem: *xml.Element, parent_extnumber: ?u31) !?registry.Req
return registry.Require.EnumExtension{
.extends = extends,
.extnumber = actual_extnumber,
.field = .{
.name = name,
.value = .{ .int = value },
.value = .{
.field = .{
.name = name,
.value = .{ .int = value },
},
},
};
}
@@ -773,7 +793,7 @@ fn parseEnumExtension(elem: *xml.Element, parent_extnumber: ?u31) !?registry.Req
return registry.Require.EnumExtension{
.extends = extends,
.extnumber = parent_extnumber,
.field = try parseEnumField(elem),
.value = .{ .field = try parseEnumField(elem) },
};
}
@@ -844,11 +864,15 @@ fn parseRequire(allocator: Allocator, require: *xml.Element, extnumber: ?u31, ap
};
}
fn parseExtensions(allocator: Allocator, root: *xml.Element, api: registry.Api) ![]registry.Extension {
fn parseExtensions(
allocator: Allocator,
root: *xml.Element,
api: registry.Api,
extensions: *std.ArrayListUnmanaged(registry.Extension),
) !void {
const extensions_elem = root.findChildByTag("extensions") orelse return error.InvalidRegistry;
try extensions.ensureUnusedCapacity(allocator, extensions_elem.children.len);
const extensions = try allocator.alloc(registry.Extension, extensions_elem.children.len);
var i: usize = 0;
var it = extensions_elem.findChildrenByTag("extension");
while (it.next()) |extension| {
if (!requiredByApi(extension, api))
@@ -860,14 +884,11 @@ fn parseExtensions(allocator: Allocator, root: *xml.Element, api: registry.Api)
}
}
extensions[i] = try parseExtension(allocator, extension, api);
i += 1;
extensions.appendAssumeCapacity(try parseExtension(allocator, extension, api));
}
return extensions[0..i];
}
fn findExtVersion(extension: *xml.Element) !u32 {
fn findExtVersion(extension: *xml.Element) !registry.Extension.Version {
var req_it = extension.findChildrenByTag("require");
while (req_it.next()) |req| {
var enum_it = req.findChildrenByTag("enum");
@@ -875,17 +896,23 @@ fn findExtVersion(extension: *xml.Element) !u32 {
const name = e.getAttribute("name") orelse continue;
const value = e.getAttribute("value") orelse continue;
if (mem.endsWith(u8, name, "_SPEC_VERSION")) {
return try std.fmt.parseInt(u32, value, 10);
// Vulkan Video extensions are sometimes aliases.
// If we fail to parse it as integer, just assume that its an alias and return that.
const version = std.fmt.parseInt(u32, value, 10) catch return .{ .alias = value };
return .{ .int = version };
}
}
}
return error.InvalidRegistry;
return .unknown;
}
fn parseExtension(allocator: Allocator, extension: *xml.Element, api: registry.Api) !registry.Extension {
const name = extension.getAttribute("name") orelse return error.InvalidRegistry;
const platform = extension.getAttribute("platform");
const is_video = std.mem.startsWith(u8, name, "vulkan_video_");
const version = try findExtVersion(extension);
// For some reason there are two ways for an extension to state its required
@@ -907,11 +934,14 @@ fn parseExtension(allocator: Allocator, extension: *xml.Element, api: registry.A
};
const number = blk: {
// Vulkan Video extensions do not have numbers.
if (is_video) break :blk 0;
const number_str = extension.getAttribute("number") orelse return error.InvalidRegistry;
break :blk try std.fmt.parseInt(u31, number_str, 10);
};
const ext_type: ?registry.Extension.ExtensionType = blk: {
if (is_video) break :blk .video;
const ext_type_str = extension.getAttribute("type") orelse break :blk null;
if (mem.eql(u8, ext_type_str, "instance")) {
break :blk .instance;

View File

@@ -42,6 +42,7 @@ pub const ApiConstant = struct {
pub const Value = union(enum) {
expr: []const u8,
version: [4][]const u8,
video_std_version: [3][]const u8,
};
name: []const u8,
@@ -179,6 +180,7 @@ pub const Extension = struct {
pub const ExtensionType = enum {
instance,
device,
video,
};
pub const Promotion = union(enum) {
@@ -187,9 +189,15 @@ pub const Extension = struct {
extension: []const u8,
};
pub const Version = union(enum) {
int: u32,
alias: []const u8,
unknown,
};
name: []const u8,
number: u31,
version: u32,
version: Version,
extension_type: ?ExtensionType,
depends: []const []const u8, // Other extensions
promoted_to: Promotion,
@@ -200,9 +208,13 @@ pub const Extension = struct {
pub const Require = struct {
pub const EnumExtension = struct {
pub const Value = union(enum) {
field: Enum.Field,
new_api_constant_expr: []const u8,
};
extends: []const u8,
extnumber: ?u31,
field: Enum.Field,
value: Value,
};
extends: []EnumExtension,

View File

@@ -81,24 +81,18 @@ const preamble =
\\ }
\\ };
\\}
\\pub fn makeApiVersion(variant: u3, major: u7, minor: u10, patch: u12) u32 {
\\ return (@as(u32, variant) << 29) | (@as(u32, major) << 22) | (@as(u32, minor) << 12) | patch;
\\}
\\pub fn apiVersionVariant(version: u32) u3 {
\\ return @truncate(version >> 29);
\\}
\\pub fn apiVersionMajor(version: u32) u7 {
\\ return @truncate(version >> 22);
\\}
\\pub fn apiVersionMinor(version: u32) u10 {
\\ return @truncate(version >> 12);
\\}
\\pub fn apiVersionPatch(version: u32) u12 {
\\ return @truncate(version);
\\pub const Version = packed struct(u32) {
\\ patch: u12,
\\ minor: u10,
\\ major: u7,
\\ variant: u3,
\\};
\\pub fn makeApiVersion(variant: u3, major: u7, minor: u10, patch: u12) Version {
\\ return .{ .variant = variant, .major = major, .minor = minor, .patch = patch };
\\}
\\pub const ApiInfo = struct {
\\ name: [:0]const u8 = "custom",
\\ version: u32 = makeApiVersion(0, 0, 0, 0),
\\ version: Version = makeApiVersion(0, 0, 0, 0),
\\ base_commands: BaseCommandFlags = .{},
\\ instance_commands: InstanceCommandFlags = .{},
\\ device_commands: DeviceCommandFlags = .{},
@@ -375,25 +369,43 @@ fn Renderer(comptime WriterType: type) type {
allocator: Allocator,
registry: *const reg.Registry,
id_renderer: *IdRenderer,
declarations_by_name: std.StringHashMap(*const reg.DeclarationType),
decls_by_name: std.StringArrayHashMap(reg.DeclarationType),
structure_types: std.StringHashMap(void),
have_video: bool,
fn init(writer: WriterType, allocator: Allocator, registry: *const reg.Registry, id_renderer: *IdRenderer) !Self {
var declarations_by_name = std.StringHashMap(*const reg.DeclarationType).init(allocator);
errdefer declarations_by_name.deinit();
fn init(
writer: WriterType,
allocator: Allocator,
registry: *const reg.Registry,
id_renderer: *IdRenderer,
have_video: bool,
) !Self {
var decls_by_name = std.StringArrayHashMap(reg.DeclarationType).init(allocator);
errdefer decls_by_name.deinit();
for (registry.decls) |*decl| {
const result = try declarations_by_name.getOrPut(decl.name);
const result = try decls_by_name.getOrPut(decl.name);
if (result.found_existing) {
std.log.err("duplicate registry entry '{s}'", .{decl.name});
return error.InvalidRegistry;
// Allow overriding 'foreign' types. These are for example the Vulkan Video types
// declared as foreign type in the vk.xml, then defined in video.xml. Sometimes
// this also includes types like uint32_t, for these we don't really care.
// Just make sure to keep the non-foreign variant.
if (result.value_ptr.* == .foreign) {
result.value_ptr.* = decl.decl_type;
} else if (decl.decl_type == .foreign) {
// Foreign type trying to override a non-foreign one. Just keep the current
// one, and don't generate an error.
} else {
std.log.err("duplicate registry entry '{s}'", .{decl.name});
return error.InvalidRegistry;
}
} else {
result.value_ptr.* = decl.decl_type;
}
result.value_ptr.* = &decl.decl_type;
}
const vk_structure_type_decl = declarations_by_name.get("VkStructureType") orelse return error.InvalidRegistry;
const vk_structure_type = switch (vk_structure_type_decl.*) {
const vk_structure_type_decl = decls_by_name.get("VkStructureType") orelse return error.InvalidRegistry;
const vk_structure_type = switch (vk_structure_type_decl) {
.enumeration => |e| e,
else => return error.InvalidRegistry,
};
@@ -409,13 +421,14 @@ fn Renderer(comptime WriterType: type) type {
.allocator = allocator,
.registry = registry,
.id_renderer = id_renderer,
.declarations_by_name = declarations_by_name,
.decls_by_name = decls_by_name,
.structure_types = structure_types,
.have_video = have_video,
};
}
fn deinit(self: *Self) void {
self.declarations_by_name.deinit();
self.decls_by_name.deinit();
}
fn writeIdentifier(self: Self, id: []const u8) !void {
@@ -501,8 +514,8 @@ fn Renderer(comptime WriterType: type) type {
}
fn resolveDeclaration(self: Self, name: []const u8) ?reg.DeclarationType {
const decl = self.declarations_by_name.get(name) orelse return null;
return self.resolveAlias(decl.*) catch return null;
const decl = self.decls_by_name.get(name) orelse return null;
return self.resolveAlias(decl) catch return null;
}
fn resolveAlias(self: Self, start_decl: reg.DeclarationType) !reg.DeclarationType {
@@ -513,8 +526,7 @@ fn Renderer(comptime WriterType: type) type {
else => return decl,
};
const decl_ptr = self.declarations_by_name.get(name) orelse return error.InvalidRegistry;
decl = decl_ptr.*;
decl = self.decls_by_name.get(name) orelse return error.InvalidRegistry;
}
}
@@ -614,12 +626,17 @@ fn Renderer(comptime WriterType: type) type {
fn render(self: *Self) !void {
try self.writer.writeAll(preamble);
try self.writer.print("pub const have_vulkan_video = {};\n", .{self.have_video});
for (self.registry.api_constants) |api_constant| {
try self.renderApiConstant(api_constant);
}
for (self.registry.decls) |decl| {
try self.renderDecl(decl);
for (self.decls_by_name.keys(), self.decls_by_name.values()) |name, decl_type| {
try self.renderDecl(.{
.name = name,
.decl_type = decl_type,
});
}
try self.renderCommandPtrs();
@@ -636,8 +653,12 @@ fn Renderer(comptime WriterType: type) type {
switch (api_constant.value) {
.expr => |expr| try self.renderApiConstantExpr(expr),
.version => |version| {
inline .version, .video_std_version => |version, kind| {
try self.writer.writeAll("makeApiVersion(");
// For Vulkan Video, just re-use the API version and set the variant to 0.
if (kind == .video_std_version) {
try self.writer.writeAll("0, ");
}
for (version, 0..) |part, i| {
if (i != 0) {
try self.writer.writeAll(", ");
@@ -688,6 +709,7 @@ fn Renderer(comptime WriterType: type) type {
} else if (mem.eql(u8, suffix.text, "U")) {
try self.writer.print("@as(u32, {s})", .{tok.text});
} else {
std.debug.print("aaa {s}\n", .{suffix.text});
return error.InvalidApiConstant;
}
},
@@ -894,17 +916,6 @@ fn Renderer(comptime WriterType: type) type {
.{maybe_author orelse ""},
);
return true;
} else if (std.mem.eql(u8, basename, "VkClusterAccelerationStructureGeometryIndexAndGeometryFlags")) {
try self.writer.print(
\\packed struct(u32) {{
\\ geometry_index: u24,
\\ reserved: u5 = 0,
\\ geometry_flags: u3, // ClusterAccelerationStructureGeometryFlags{0s}
\\}};
,
.{maybe_author orelse ""},
);
return true;
} else if (std.mem.eql(u8, basename, "VkClusterAccelerationStructureBuildTriangleClusterInfo")) {
try self.writer.print(
\\extern struct {{
@@ -980,11 +991,50 @@ fn Renderer(comptime WriterType: type) type {
return false;
}
fn renderSimpleBitContainer(self: *Self, container: reg.Container) !bool {
var total_bits: usize = 0;
for (container.fields) |field| {
total_bits += field.bits orelse {
// C abi type - not a packed struct.
return false;
};
}
try self.writer.writeAll("packed struct(u32) {");
for (container.fields) |field| {
const bits = field.bits.?;
try self.writeIdentifierWithCase(.snake, field.name);
try self.writer.writeAll(": ");
// Default-zero fields that look like they are not used.
if (std.mem.eql(u8, field.name, "reserved")) {
try self.writer.print(" u{} = 0,\n", .{field.bits.?});
} else if (bits == 1) {
// Assume its a flag.
try self.writer.writeAll(" bool,\n");
} else {
try self.writer.print(" u{},\n", .{field.bits.?});
}
}
if (total_bits != 32) {
try self.writer.print("_reserved: u{} = 0,\n", .{32 - total_bits});
}
try self.writer.writeAll("};\n");
return true;
}
fn renderContainer(self: *Self, name: []const u8, container: reg.Container) !void {
try self.writer.writeAll("pub const ");
try self.renderName(name);
try self.writer.writeAll(" = ");
if (try self.renderSimpleBitContainer(container)) {
return;
}
if (try self.renderSpecialContainer(name)) {
return;
}
@@ -1231,7 +1281,9 @@ fn Renderer(comptime WriterType: type) type {
}
fn renderForeign(self: *Self, name: []const u8, foreign: reg.Foreign) !void {
if (mem.eql(u8, foreign.depends, "vk_platform")) {
if (mem.eql(u8, foreign.depends, "vk_platform") or
builtin_types.get(name) != null)
{
return; // Skip built-in types, they are handled differently
}
@@ -1262,18 +1314,18 @@ fn Renderer(comptime WriterType: type) type {
}
fn renderCommandPtrs(self: *Self) !void {
for (self.registry.decls) |decl| {
switch (decl.decl_type) {
for (self.decls_by_name.keys(), self.decls_by_name.values()) |name, decl_type| {
switch (decl_type) {
.command => {
try self.writer.writeAll("pub const ");
try self.renderCommandPtrName(decl.name);
try self.renderCommandPtrName(name);
try self.writer.writeAll(" = ");
try self.renderCommandPtr(decl.decl_type.command, false);
try self.renderCommandPtr(decl_type.command, false);
try self.writer.writeAll(";\n");
},
.alias => |alias| if (alias.target == .other_command) {
try self.writer.writeAll("pub const ");
try self.renderCommandPtrName(decl.name);
try self.renderCommandPtrName(name);
try self.writer.writeAll(" = ");
try self.renderCommandPtrName(alias.name);
try self.writer.writeAll(";\n");
@@ -1289,13 +1341,17 @@ fn Renderer(comptime WriterType: type) type {
\\
);
// The commands in a feature level are not pre-sorted based on if they are instance or device functions.
var base_commands = std.BufSet.init(self.allocator);
var base_commands = std.StringArrayHashMap(void).init(self.allocator);
defer base_commands.deinit();
var instance_commands = std.BufSet.init(self.allocator);
var instance_commands = std.StringArrayHashMap(void).init(self.allocator);
defer instance_commands.deinit();
var device_commands = std.BufSet.init(self.allocator);
var device_commands = std.StringArrayHashMap(void).init(self.allocator);
defer device_commands.deinit();
for (self.registry.features) |feature| {
base_commands.clearRetainingCapacity();
instance_commands.clearRetainingCapacity();
device_commands.clearRetainingCapacity();
try self.writer.writeAll("pub const ");
try self.writeIdentifierWithCase(.snake, trimVkNamespace(feature.name));
try self.writer.writeAll("= ApiInfo {\n");
@@ -1317,31 +1373,28 @@ fn Renderer(comptime WriterType: type) type {
};
const class = classifyCommandDispatch(command_name, command);
switch (class) {
.base => {
try base_commands.insert(command_name);
},
.instance => {
try instance_commands.insert(command_name);
},
.device => {
try device_commands.insert(command_name);
},
.base => try base_commands.put(command_name, {}),
.instance => try instance_commands.put(command_name, {}),
.device => try device_commands.put(command_name, {}),
}
}
}
// and write them out
// clear command lists for next iteration
try self.writer.writeAll(".base_commands = ");
try self.renderCommandFlags(&base_commands);
base_commands.hash_map.clearRetainingCapacity();
if (base_commands.count() != 0) {
try self.writer.writeAll(".base_commands = ");
try self.renderCommandFlags(base_commands.keys());
}
try self.writer.writeAll(".instance_commands = ");
try self.renderCommandFlags(&instance_commands);
instance_commands.hash_map.clearRetainingCapacity();
if (instance_commands.count() != 0) {
try self.writer.writeAll(".instance_commands = ");
try self.renderCommandFlags(instance_commands.keys());
}
try self.writer.writeAll(".device_commands = ");
try self.renderCommandFlags(&device_commands);
device_commands.hash_map.clearRetainingCapacity();
if (device_commands.count() != 0) {
try self.writer.writeAll(".device_commands = ");
try self.renderCommandFlags(device_commands.keys());
}
try self.writer.writeAll("};\n");
}
@@ -1355,17 +1408,35 @@ fn Renderer(comptime WriterType: type) type {
\\
);
// The commands in an extension are not pre-sorted based on if they are instance or device functions.
var base_commands = std.BufSet.init(self.allocator);
var base_commands = std.StringArrayHashMap(void).init(self.allocator);
defer base_commands.deinit();
var instance_commands = std.BufSet.init(self.allocator);
var instance_commands = std.StringArrayHashMap(void).init(self.allocator);
defer instance_commands.deinit();
var device_commands = std.BufSet.init(self.allocator);
var device_commands = std.StringArrayHashMap(void).init(self.allocator);
defer device_commands.deinit();
for (self.registry.extensions) |ext| {
base_commands.clearRetainingCapacity();
instance_commands.clearRetainingCapacity();
device_commands.clearRetainingCapacity();
try self.writer.writeAll("pub const ");
try self.writeIdentifierWithCase(.snake, trimVkNamespace(ext.name));
if (ext.extension_type == .video) {
// These are already in the right form, and the auto-casing style transformer
// is prone to messing up these names.
try self.writeIdentifier(trimVkNamespace(ext.name));
} else {
try self.writeIdentifierWithCase(.snake, trimVkNamespace(ext.name));
}
try self.writer.writeAll("= ApiInfo {\n");
try self.writer.print(".name = \"{s}\", .version = {},", .{ ext.name, ext.version });
try self.writer.print(".name = \"{s}\", .version = ", .{ext.name});
switch (ext.version) {
.int => |version| try self.writer.print("makeApiVersion(0, {}, 0, 0)", .{version}),
// This should be the same as in self.renderApiConstant.
// We assume that this is already a vk.Version type.
.alias => |alias| try self.renderName(alias),
.unknown => try self.writer.writeAll("makeApiVersion(0, 0, 0, 0)"),
}
try self.writer.writeByte(',');
// collect extension functions
for (ext.requires) |require| {
for (require.commands) |command_name| {
@@ -1379,42 +1450,38 @@ fn Renderer(comptime WriterType: type) type {
};
const class = classifyCommandDispatch(command_name, command);
switch (class) {
.base => {
try base_commands.insert(command_name);
},
.instance => {
try instance_commands.insert(command_name);
},
.device => {
try device_commands.insert(command_name);
},
.base => try base_commands.put(command_name, {}),
.instance => try instance_commands.put(command_name, {}),
.device => try device_commands.put(command_name, {}),
}
}
}
// and write them out
try self.writer.writeAll(".base_commands = ");
try self.renderCommandFlags(&base_commands);
base_commands.hash_map.clearRetainingCapacity();
if (base_commands.count() != 0) {
try self.writer.writeAll(".base_commands = ");
try self.renderCommandFlags(base_commands.keys());
}
try self.writer.writeAll(".instance_commands = ");
try self.renderCommandFlags(&instance_commands);
instance_commands.hash_map.clearRetainingCapacity();
if (instance_commands.count() != 0) {
try self.writer.writeAll(".instance_commands = ");
try self.renderCommandFlags(instance_commands.keys());
}
try self.writer.writeAll(".device_commands = ");
try self.renderCommandFlags(&device_commands);
device_commands.hash_map.clearRetainingCapacity();
if (device_commands.count() != 0) {
try self.writer.writeAll(".device_commands = ");
try self.renderCommandFlags(device_commands.keys());
}
try self.writer.writeAll("};\n");
}
try self.writer.writeAll("};\n");
}
fn renderCommandFlags(self: *Self, commands: *const std.BufSet) !void {
fn renderCommandFlags(self: *Self, commands: []const []const u8) !void {
try self.writer.writeAll(".{\n");
var iterator = commands.iterator();
while (iterator.next()) |command_name| {
for (commands) |command_name| {
try self.writer.writeAll(".");
try self.writeIdentifierWithCase(.camel, trimVkNamespace(command_name.*));
try self.writeIdentifierWithCase(.camel, trimVkNamespace(command_name));
try self.writer.writeAll(" = true, \n");
}
try self.writer.writeAll("},\n");
@@ -2226,8 +2293,14 @@ fn Renderer(comptime WriterType: type) type {
};
}
pub fn render(writer: anytype, allocator: Allocator, registry: *const reg.Registry, id_renderer: *IdRenderer) !void {
var renderer = try Renderer(@TypeOf(writer)).init(writer, allocator, registry, id_renderer);
pub fn render(
writer: anytype,
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);
defer renderer.deinit();
try renderer.render();
}

View File

@@ -22,76 +22,79 @@ 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.
pub const StdVideoH264ProfileIdc = u32;
pub const StdVideoH264LevelIdc = u32;
pub const StdVideoH264ChromaFormatIdc = u32;
pub const StdVideoH264PocType = u32;
pub const StdVideoH264SpsFlags = u32;
pub const StdVideoH264ScalingLists = u32;
pub const StdVideoH264SequenceParameterSetVui = u32;
pub const StdVideoH264AspectRatioIdc = u32;
pub const StdVideoH264HrdParameters = u32;
pub const StdVideoH264SpsVuiFlags = u32;
pub const StdVideoH264WeightedBipredIdc = u32;
pub const StdVideoH264PpsFlags = u32;
pub const StdVideoH264SliceType = u32;
pub const StdVideoH264CabacInitIdc = u32;
pub const StdVideoH264DisableDeblockingFilterIdc = u32;
pub const StdVideoH264PictureType = u32;
pub const StdVideoH264ModificationOfPicNumsIdc = u32;
pub const StdVideoH264MemMgmtControlOp = u32;
pub const StdVideoDecodeH264PictureInfo = u32;
pub const StdVideoDecodeH264ReferenceInfo = u32;
pub const StdVideoDecodeH264PictureInfoFlags = u32;
pub const StdVideoDecodeH264ReferenceInfoFlags = u32;
pub const StdVideoH264SequenceParameterSet = u32;
pub const StdVideoH264PictureParameterSet = u32;
pub const StdVideoH265ProfileIdc = u32;
pub const StdVideoH265VideoParameterSet = u32;
pub const StdVideoH265SequenceParameterSet = u32;
pub const StdVideoH265PictureParameterSet = u32;
pub const StdVideoH265DecPicBufMgr = u32;
pub const StdVideoH265HrdParameters = u32;
pub const StdVideoH265VpsFlags = u32;
pub const StdVideoH265LevelIdc = u32;
pub const StdVideoH265SpsFlags = u32;
pub const StdVideoH265ScalingLists = u32;
pub const StdVideoH265SequenceParameterSetVui = u32;
pub const StdVideoH265PredictorPaletteEntries = u32;
pub const StdVideoH265PpsFlags = u32;
pub const StdVideoH265SubLayerHrdParameters = u32;
pub const StdVideoH265HrdFlags = u32;
pub const StdVideoH265SpsVuiFlags = u32;
pub const StdVideoH265SliceType = u32;
pub const StdVideoH265PictureType = u32;
pub const StdVideoDecodeH265PictureInfo = u32;
pub const StdVideoDecodeH265ReferenceInfo = u32;
pub const StdVideoDecodeH265PictureInfoFlags = u32;
pub const StdVideoDecodeH265ReferenceInfoFlags = u32;
pub const StdVideoAV1Profile = u32;
pub const StdVideoAV1Level = u32;
pub const StdVideoAV1SequenceHeader = u32;
pub const StdVideoDecodeAV1PictureInfo = u32;
pub const StdVideoDecodeAV1ReferenceInfo = u32;
pub const StdVideoEncodeH264SliceHeader = u32;
pub const StdVideoEncodeH264PictureInfo = u32;
pub const StdVideoEncodeH264ReferenceInfo = u32;
pub const StdVideoEncodeH264SliceHeaderFlags = u32;
pub const StdVideoEncodeH264ReferenceListsInfo = u32;
pub const StdVideoEncodeH264PictureInfoFlags = u32;
pub const StdVideoEncodeH264ReferenceInfoFlags = u32;
pub const StdVideoEncodeH264RefMgmtFlags = u32;
pub const StdVideoEncodeH264RefListModEntry = u32;
pub const StdVideoEncodeH264RefPicMarkingEntry = u32;
pub const StdVideoEncodeH265PictureInfoFlags = u32;
pub const StdVideoEncodeH265PictureInfo = u32;
pub const StdVideoEncodeH265SliceSegmentHeader = u32;
pub const StdVideoEncodeH265ReferenceInfo = u32;
pub const StdVideoEncodeH265ReferenceListsInfo = u32;
pub const StdVideoEncodeH265SliceSegmentHeaderFlags = u32;
pub const StdVideoEncodeH265ReferenceInfoFlags = u32;
pub const StdVideoEncodeH265ReferenceModificationFlags = u32;
pub const StdVideoEncodeAV1OperatingPointInfo = u32;
// 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;
pub const StdVideoH264PocType = u32;
pub const StdVideoH264SpsFlags = u32;
pub const StdVideoH264ScalingLists = u32;
pub const StdVideoH264SequenceParameterSetVui = u32;
pub const StdVideoH264AspectRatioIdc = u32;
pub const StdVideoH264HrdParameters = u32;
pub const StdVideoH264SpsVuiFlags = u32;
pub const StdVideoH264WeightedBipredIdc = u32;
pub const StdVideoH264PpsFlags = u32;
pub const StdVideoH264SliceType = u32;
pub const StdVideoH264CabacInitIdc = u32;
pub const StdVideoH264DisableDeblockingFilterIdc = u32;
pub const StdVideoH264PictureType = u32;
pub const StdVideoH264ModificationOfPicNumsIdc = u32;
pub const StdVideoH264MemMgmtControlOp = u32;
pub const StdVideoDecodeH264PictureInfo = u32;
pub const StdVideoDecodeH264ReferenceInfo = u32;
pub const StdVideoDecodeH264PictureInfoFlags = u32;
pub const StdVideoDecodeH264ReferenceInfoFlags = u32;
pub const StdVideoH264SequenceParameterSet = u32;
pub const StdVideoH264PictureParameterSet = u32;
pub const StdVideoH265ProfileIdc = u32;
pub const StdVideoH265VideoParameterSet = u32;
pub const StdVideoH265SequenceParameterSet = u32;
pub const StdVideoH265PictureParameterSet = u32;
pub const StdVideoH265DecPicBufMgr = u32;
pub const StdVideoH265HrdParameters = u32;
pub const StdVideoH265VpsFlags = u32;
pub const StdVideoH265LevelIdc = u32;
pub const StdVideoH265SpsFlags = u32;
pub const StdVideoH265ScalingLists = u32;
pub const StdVideoH265SequenceParameterSetVui = u32;
pub const StdVideoH265PredictorPaletteEntries = u32;
pub const StdVideoH265PpsFlags = u32;
pub const StdVideoH265SubLayerHrdParameters = u32;
pub const StdVideoH265HrdFlags = u32;
pub const StdVideoH265SpsVuiFlags = u32;
pub const StdVideoH265SliceType = u32;
pub const StdVideoH265PictureType = u32;
pub const StdVideoDecodeH265PictureInfo = u32;
pub const StdVideoDecodeH265ReferenceInfo = u32;
pub const StdVideoDecodeH265PictureInfoFlags = u32;
pub const StdVideoDecodeH265ReferenceInfoFlags = u32;
pub const StdVideoAV1Profile = u32;
pub const StdVideoAV1Level = u32;
pub const StdVideoAV1SequenceHeader = u32;
pub const StdVideoDecodeAV1PictureInfo = u32;
pub const StdVideoDecodeAV1ReferenceInfo = u32;
pub const StdVideoEncodeH264SliceHeader = u32;
pub const StdVideoEncodeH264PictureInfo = u32;
pub const StdVideoEncodeH264ReferenceInfo = u32;
pub const StdVideoEncodeH264SliceHeaderFlags = u32;
pub const StdVideoEncodeH264ReferenceListsInfo = u32;
pub const StdVideoEncodeH264PictureInfoFlags = u32;
pub const StdVideoEncodeH264ReferenceInfoFlags = u32;
pub const StdVideoEncodeH264RefMgmtFlags = u32;
pub const StdVideoEncodeH264RefListModEntry = u32;
pub const StdVideoEncodeH264RefPicMarkingEntry = u32;
pub const StdVideoEncodeH265PictureInfoFlags = u32;
pub const StdVideoEncodeH265PictureInfo = u32;
pub const StdVideoEncodeH265SliceSegmentHeader = u32;
pub const StdVideoEncodeH265ReferenceInfo = u32;
pub const StdVideoEncodeH265ReferenceListsInfo = u32;
pub const StdVideoEncodeH265SliceSegmentHeaderFlags = u32;
pub const StdVideoEncodeH265ReferenceInfoFlags = u32;
pub const StdVideoEncodeH265ReferenceModificationFlags = u32;
pub const StdVideoEncodeAV1OperatingPointInfo = u32;
} else struct {};
comptime {
@setEvalBranchQuota(1000000);