forked from mirror/vulkan-zig
11
.github/workflows/build.yml
vendored
11
.github/workflows/build.yml
vendored
@@ -31,11 +31,16 @@ jobs:
|
|||||||
sudo apt install shaderc libglfw3 libglfw3-dev
|
sudo apt install shaderc libglfw3 libglfw3-dev
|
||||||
|
|
||||||
- name: Fetch latest vk.xml
|
- 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
|
- name: Test and install with latest zig & latest vk.xml
|
||||||
run: zig build test install -Dregistry=$(pwd)/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
|
- name: Build example with latest zig & vk.xml from dependency
|
||||||
run: zig build --build-file $(pwd)/examples/build.zig
|
run: zig build --build-file $(pwd)/examples/build.zig
|
||||||
|
|
||||||
@@ -46,5 +51,7 @@ jobs:
|
|||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: vk.zig
|
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
|
if-no-files-found: error
|
||||||
|
|||||||
13
README.md
13
README.md
@@ -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).
|
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
|
## 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.
|
* 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.
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ pub fn build(b: *std.Build) void {
|
|||||||
const target = b.standardTargetOptions(.{});
|
const target = b.standardTargetOptions(.{});
|
||||||
const optimize = b.standardOptimizeOption(.{});
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
const maybe_registry = b.option(std.Build.LazyPath, "registry", "Set the path to the Vulkan registry (vk.xml)");
|
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");
|
const test_step = b.step("test", "Run all the tests");
|
||||||
|
|
||||||
// Using the package manager, this artifact can be obtained by the user
|
// 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| {
|
if (maybe_registry) |registry| {
|
||||||
const vk_generate_cmd = b.addRunArtifact(generator_exe);
|
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);
|
vk_generate_cmd.addFileArg(registry);
|
||||||
|
|
||||||
const vk_zig = vk_generate_cmd.addOutputFileArg("vk.zig");
|
const vk_zig = vk_generate_cmd.addOutputFileArg("vk.zig");
|
||||||
|
|||||||
@@ -58,10 +58,10 @@ 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.makeApiVersion(0, 0, 0, 0),
|
.application_version = @bitCast(vk.makeApiVersion(0, 0, 0, 0)),
|
||||||
.p_engine_name = app_name,
|
.p_engine_name = app_name,
|
||||||
.engine_version = vk.makeApiVersion(0, 0, 0, 0),
|
.engine_version = @bitCast(vk.makeApiVersion(0, 0, 0, 0)),
|
||||||
.api_version = vk.API_VERSION_1_2,
|
.api_version = @bitCast(vk.API_VERSION_1_2),
|
||||||
};
|
};
|
||||||
|
|
||||||
const instance = try self.vkb.createInstance(&.{
|
const instance = try self.vkb.createInstance(&.{
|
||||||
|
|||||||
23
src/main.zig
23
src/main.zig
@@ -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);
|
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||||
defer arena.deinit();
|
defer arena.deinit();
|
||||||
const allocator = arena.allocator();
|
const allocator = arena.allocator();
|
||||||
@@ -34,6 +34,7 @@ pub fn main() void {
|
|||||||
|
|
||||||
var maybe_xml_path: ?[]const u8 = null;
|
var maybe_xml_path: ?[]const u8 = null;
|
||||||
var maybe_out_path: ?[]const u8 = null;
|
var maybe_out_path: ?[]const u8 = null;
|
||||||
|
var maybe_video_xml_path: ?[]const u8 = null;
|
||||||
var debug: bool = false;
|
var debug: bool = false;
|
||||||
var api = generator.Api.vulkan;
|
var api = generator.Api.vulkan;
|
||||||
|
|
||||||
@@ -53,6 +54,8 @@ pub fn main() void {
|
|||||||
\\-h --help show this message and exit.
|
\\-h --help show this message and exit.
|
||||||
\\-a --api <api> Generate API for 'vulkan' or 'vulkansc'. Defaults to 'vulkan'.
|
\\-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},
|
.{prog_name},
|
||||||
@@ -68,12 +71,16 @@ pub fn main() void {
|
|||||||
api = std.meta.stringToEnum(generator.Api, api_str) orelse {
|
api = std.meta.stringToEnum(generator.Api, api_str) orelse {
|
||||||
invalidUsage(prog_name, "invalid api '{s}'", .{api_str});
|
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) {
|
} else if (maybe_xml_path == null) {
|
||||||
maybe_xml_path = arg;
|
maybe_xml_path = arg;
|
||||||
} else if (maybe_out_path == null) {
|
} else if (maybe_out_path == null) {
|
||||||
maybe_out_path = arg;
|
maybe_out_path = arg;
|
||||||
} else if (std.mem.eql(u8, arg, "--debug")) {
|
|
||||||
debug = true;
|
|
||||||
} else {
|
} else {
|
||||||
invalidUsage(prog_name, "superficial argument '{s}'", .{arg});
|
invalidUsage(prog_name, "superficial argument '{s}'", .{arg});
|
||||||
}
|
}
|
||||||
@@ -93,8 +100,16 @@ pub fn main() void {
|
|||||||
std.process.exit(1);
|
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);
|
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 => {
|
error.InvalidXml => {
|
||||||
std.log.err("invalid vulkan registry - invalid xml", .{});
|
std.log.err("invalid vulkan registry - invalid xml", .{});
|
||||||
std.log.err("please check that the correct vk.xml file is passed", .{});
|
std.log.err("please check that the correct vk.xml file is passed", .{});
|
||||||
|
|||||||
@@ -90,10 +90,18 @@ pub const CTokenizer = struct {
|
|||||||
const start = self.offset;
|
const start = self.offset;
|
||||||
_ = self.consumeNoEof();
|
_ = self.consumeNoEof();
|
||||||
|
|
||||||
|
const hex = self.peek() == 'x';
|
||||||
|
if (hex) {
|
||||||
|
_ = self.consumeNoEof();
|
||||||
|
}
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const c = self.peek() orelse break;
|
switch (self.peek() orelse break) {
|
||||||
switch (c) {
|
|
||||||
'0'...'9' => _ = self.consumeNoEof(),
|
'0'...'9' => _ = self.consumeNoEof(),
|
||||||
|
'A'...'F', 'a'...'f' => {
|
||||||
|
if (!hex) break;
|
||||||
|
_ = self.consumeNoEof();
|
||||||
|
},
|
||||||
else => break,
|
else => break,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -164,7 +172,12 @@ pub const XmlCTokenizer = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn elemToToken(elem: *xml.Element) !?Token {
|
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;
|
return error.InvalidXml;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,8 +188,6 @@ pub const XmlCTokenizer = struct {
|
|||||||
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")) {
|
|
||||||
return null;
|
|
||||||
} else {
|
} else {
|
||||||
return error.InvalidTag;
|
return error.InvalidTag;
|
||||||
}
|
}
|
||||||
@@ -530,7 +541,10 @@ fn parseArrayDeclarator(xctok: *XmlCTokenizer) !?ArraySize {
|
|||||||
error.InvalidCharacter => unreachable,
|
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,
|
else => return error.InvalidSyntax,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -538,7 +552,7 @@ fn parseArrayDeclarator(xctok: *XmlCTokenizer) !?ArraySize {
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parseVersion(xctok: *XmlCTokenizer) ![4][]const u8 {
|
pub fn parseVersion(xctok: *XmlCTokenizer) !registry.ApiConstant.Value {
|
||||||
_ = 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")) {
|
||||||
@@ -547,12 +561,22 @@ pub fn parseVersion(xctok: *XmlCTokenizer) ![4][]const u8 {
|
|||||||
|
|
||||||
_ = 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_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;
|
return error.NotVersion;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parseVersionValues(xctok: *XmlCTokenizer, comptime count: usize) ![count][]const u8 {
|
||||||
_ = try xctok.expect(.lparen);
|
_ = try xctok.expect(.lparen);
|
||||||
var version: [4][]const u8 = undefined;
|
var version: [count][]const u8 = undefined;
|
||||||
for (&version, 0..) |*part, i| {
|
for (&version, 0..) |*part, i| {
|
||||||
if (i != 0) {
|
if (i != 0) {
|
||||||
_ = try xctok.expect(.comma);
|
_ = try xctok.expect(.comma);
|
||||||
|
|||||||
@@ -10,11 +10,13 @@ const FeatureLevel = reg.FeatureLevel;
|
|||||||
|
|
||||||
const EnumFieldMerger = struct {
|
const EnumFieldMerger = struct {
|
||||||
const EnumExtensionMap = std.StringArrayHashMapUnmanaged(std.ArrayListUnmanaged(reg.Enum.Field));
|
const EnumExtensionMap = std.StringArrayHashMapUnmanaged(std.ArrayListUnmanaged(reg.Enum.Field));
|
||||||
|
const ApiConstantMap = std.StringArrayHashMapUnmanaged(reg.ApiConstant);
|
||||||
const FieldSet = std.StringArrayHashMapUnmanaged(void);
|
const FieldSet = std.StringArrayHashMapUnmanaged(void);
|
||||||
|
|
||||||
arena: Allocator,
|
arena: Allocator,
|
||||||
registry: *reg.Registry,
|
registry: *reg.Registry,
|
||||||
enum_extensions: EnumExtensionMap,
|
enum_extensions: EnumExtensionMap,
|
||||||
|
api_constants: ApiConstantMap,
|
||||||
field_set: FieldSet,
|
field_set: FieldSet,
|
||||||
|
|
||||||
fn init(arena: Allocator, registry: *reg.Registry) EnumFieldMerger {
|
fn init(arena: Allocator, registry: *reg.Registry) EnumFieldMerger {
|
||||||
@@ -22,6 +24,7 @@ const EnumFieldMerger = struct {
|
|||||||
.arena = arena,
|
.arena = arena,
|
||||||
.registry = registry,
|
.registry = registry,
|
||||||
.enum_extensions = .{},
|
.enum_extensions = .{},
|
||||||
|
.api_constants = .{},
|
||||||
.field_set = .{},
|
.field_set = .{},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -38,7 +41,17 @@ const EnumFieldMerger = struct {
|
|||||||
fn addRequires(self: *EnumFieldMerger, reqs: []const reg.Require) !void {
|
fn addRequires(self: *EnumFieldMerger, reqs: []const reg.Require) !void {
|
||||||
for (reqs) |req| {
|
for (reqs) |req| {
|
||||||
for (req.extends) |enum_ext| {
|
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 {
|
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| {
|
for (self.registry.features) |feature| {
|
||||||
try self.addRequires(feature.requires);
|
try self.addRequires(feature.requires);
|
||||||
}
|
}
|
||||||
@@ -91,6 +108,8 @@ const EnumFieldMerger = struct {
|
|||||||
try self.mergeEnumFields(decl.name, &decl.decl_type.enumeration);
|
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,
|
arena: std.heap.ArenaAllocator,
|
||||||
registry: reg.Registry,
|
registry: reg.Registry,
|
||||||
id_renderer: IdRenderer,
|
id_renderer: IdRenderer,
|
||||||
|
have_video: bool,
|
||||||
|
|
||||||
fn init(allocator: Allocator, spec: *xml.Element, api: reg.Api) !Generator {
|
fn init(allocator: Allocator, spec: *xml.Element, maybe_video_spec: ?*xml.Element, api: reg.Api) !Generator {
|
||||||
const result = try parseXml(allocator, spec, api);
|
const result = try parseXml(allocator, spec, maybe_video_spec, api);
|
||||||
|
|
||||||
const tags = try allocator.alloc([]const u8, result.registry.tags.len);
|
const tags = try allocator.alloc([]const u8, result.registry.tags.len);
|
||||||
for (tags, result.registry.tags) |*tag, registry_tag| tag.* = registry_tag.name;
|
for (tags, result.registry.tags) |*tag, registry_tag| tag.* = registry_tag.name;
|
||||||
@@ -109,6 +129,7 @@ pub const Generator = struct {
|
|||||||
.arena = result.arena,
|
.arena = result.arena,
|
||||||
.registry = result.registry,
|
.registry = result.registry,
|
||||||
.id_renderer = IdRenderer.init(allocator, tags),
|
.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 {
|
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
|
/// 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
|
/// 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`.
|
/// 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) {
|
const spec = xml.parse(allocator, spec_xml) catch |err| switch (err) {
|
||||||
error.InvalidDocument,
|
error.InvalidDocument,
|
||||||
error.UnexpectedEof,
|
error.UnexpectedEof,
|
||||||
@@ -195,7 +216,26 @@ pub fn generate(allocator: Allocator, api: Api, spec_xml: []const u8, writer: an
|
|||||||
};
|
};
|
||||||
defer spec.deinit();
|
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.InvalidXml,
|
||||||
error.InvalidCharacter,
|
error.InvalidCharacter,
|
||||||
error.Overflow,
|
error.Overflow,
|
||||||
|
|||||||
@@ -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);
|
var arena = ArenaAllocator.init(backing_allocator);
|
||||||
errdefer arena.deinit();
|
errdefer arena.deinit();
|
||||||
|
|
||||||
const allocator = arena.allocator();
|
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{
|
const reg = registry.Registry{
|
||||||
.decls = try parseDeclarations(allocator, root, api),
|
.decls = decls.items,
|
||||||
.api_constants = try parseApiConstants(allocator, root, api),
|
.api_constants = api_constants.items,
|
||||||
.tags = try parseTags(allocator, root),
|
.tags = tags.items,
|
||||||
.features = try parseFeatures(allocator, root, api),
|
.features = features.items,
|
||||||
.extensions = try parseExtensions(allocator, root, api),
|
.extensions = extensions.items,
|
||||||
};
|
};
|
||||||
|
|
||||||
return ParseResult{
|
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 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;
|
try parseTypes(allocator, types_elem, api, decls);
|
||||||
const decls = try allocator.alloc(registry.Declaration, decl_upper_bound);
|
try parseEnums(allocator, root, api, decls);
|
||||||
|
|
||||||
var count: usize = 0;
|
if (root.findChildByTag("commands")) |commands_elem| {
|
||||||
count += try parseTypes(allocator, decls, types_elem, api);
|
try decls.ensureUnusedCapacity(allocator, commands_elem.children.len);
|
||||||
count += try parseEnums(allocator, decls[count..], root, api);
|
try parseCommands(allocator, commands_elem, api, decls);
|
||||||
count += try parseCommands(allocator, decls[count..], commands_elem, api);
|
}
|
||||||
return decls[0..count];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parseTypes(allocator: Allocator, out: []registry.Declaration, types_elem: *xml.Element, api: registry.Api) !usize {
|
fn parseTypes(
|
||||||
var i: usize = 0;
|
allocator: Allocator,
|
||||||
|
types_elem: *xml.Element,
|
||||||
|
api: registry.Api,
|
||||||
|
decls: *std.ArrayListUnmanaged(registry.Declaration),
|
||||||
|
) !void {
|
||||||
var it = types_elem.findChildrenByTag("type");
|
var it = types_elem.findChildrenByTag("type");
|
||||||
while (it.next()) |ty| {
|
while (it.next()) |ty| {
|
||||||
out[i] = blk: {
|
try decls.append(allocator, blk: {
|
||||||
if (!requiredByApi(ty, api))
|
if (!requiredByApi(ty, api))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -80,12 +113,8 @@ fn parseTypes(allocator: Allocator, out: []registry.Declaration, types_elem: *xm
|
|||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
};
|
});
|
||||||
|
|
||||||
i += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parseForeigntype(ty: *xml.Element) !registry.Declaration {
|
fn parseForeigntype(ty: *xml.Element) !registry.Declaration {
|
||||||
@@ -388,8 +417,7 @@ fn parseEnumAlias(elem: *xml.Element) !?registry.Declaration {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parseEnums(allocator: Allocator, out: []registry.Declaration, root: *xml.Element, api: registry.Api) !usize {
|
fn parseEnums(allocator: Allocator, root: *xml.Element, api: registry.Api, decls: *std.ArrayListUnmanaged(registry.Declaration)) !void {
|
||||||
var i: usize = 0;
|
|
||||||
var it = root.findChildrenByTag("enums");
|
var it = root.findChildrenByTag("enums");
|
||||||
while (it.next()) |enums| {
|
while (it.next()) |enums| {
|
||||||
const name = enums.getAttribute("name") orelse return error.InvalidRegistry;
|
const name = enums.getAttribute("name") orelse return error.InvalidRegistry;
|
||||||
@@ -397,14 +425,11 @@ fn parseEnums(allocator: Allocator, out: []registry.Declaration, root: *xml.Elem
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
out[i] = .{
|
try decls.append(allocator, .{
|
||||||
.name = name,
|
.name = name,
|
||||||
.decl_type = .{ .enumeration = try parseEnumFields(allocator, enums, api) },
|
.decl_type = .{ .enumeration = try parseEnumFields(allocator, enums, api) },
|
||||||
};
|
});
|
||||||
i += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parseEnumFields(allocator: Allocator, elem: *xml.Element, api: registry.Api) !registry.Enum {
|
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 {
|
fn parseCommands(
|
||||||
var i: usize = 0;
|
allocator: Allocator,
|
||||||
|
commands_elem: *xml.Element,
|
||||||
|
api: registry.Api,
|
||||||
|
decls: *std.ArrayListUnmanaged(registry.Declaration),
|
||||||
|
) !void {
|
||||||
var it = commands_elem.findChildrenByTag("command");
|
var it = commands_elem.findChildrenByTag("command");
|
||||||
while (it.next()) |elem| {
|
while (it.next()) |elem| {
|
||||||
if (!requiredByApi(elem, api))
|
if (!requiredByApi(elem, api))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
out[i] = try parseCommand(allocator, elem, api);
|
try decls.append(allocator, try parseCommand(allocator, elem, api));
|
||||||
i += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn splitCommaAlloc(allocator: Allocator, text: []const u8) ![][]const u8 {
|
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 {
|
fn parseApiConstants(
|
||||||
var enums = blk: {
|
allocator: Allocator,
|
||||||
|
root: *xml.Element,
|
||||||
|
api: registry.Api,
|
||||||
|
api_constants: *std.ArrayListUnmanaged(registry.ApiConstant),
|
||||||
|
) !void {
|
||||||
|
const maybe_enums = blk: {
|
||||||
var it = root.findChildrenByTag("enums");
|
var it = root.findChildrenByTag("enums");
|
||||||
while (it.next()) |child| {
|
while (it.next()) |child| {
|
||||||
const name = child.getAttribute("name") orelse continue;
|
const name = child.getAttribute("name") orelse continue;
|
||||||
@@ -597,26 +628,10 @@ 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;
|
if (maybe_enums) |enums| {
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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");
|
var it = enums.findChildrenByTag("enum");
|
||||||
while (it.next()) |constant| {
|
while (it.next()) |constant| {
|
||||||
if (!requiredByApi(constant, api))
|
if (!requiredByApi(constant, api))
|
||||||
@@ -629,20 +644,23 @@ fn parseApiConstants(allocator: Allocator, root: *xml.Element, api: registry.Api
|
|||||||
else
|
else
|
||||||
return error.InvalidRegistry;
|
return error.InvalidRegistry;
|
||||||
|
|
||||||
constants[i] = .{
|
try api_constants.append(allocator, .{
|
||||||
.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 += try parseDefines(types, constants[i..], api);
|
const types = root.findChildByTag("types") orelse return error.InvalidRegistry;
|
||||||
return constants[0..i];
|
try parseDefines(allocator, types, api, api_constants);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parseDefines(types: *xml.Element, out: []registry.ApiConstant, api: registry.Api) !usize {
|
fn parseDefines(
|
||||||
var i: usize = 0;
|
allocator: Allocator,
|
||||||
|
types: *xml.Element,
|
||||||
|
api: registry.Api,
|
||||||
|
api_constants: *std.ArrayListUnmanaged(registry.ApiConstant),
|
||||||
|
) !void {
|
||||||
var it = types.findChildrenByTag("type");
|
var it = types.findChildrenByTag("type");
|
||||||
while (it.next()) |ty| {
|
while (it.next()) |ty| {
|
||||||
if (!requiredByApi(ty, api))
|
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;
|
const name = ty.getCharData("name") orelse continue;
|
||||||
if (mem.eql(u8, name, "VK_HEADER_VERSION") or mem.eql(u8, name, "VKSC_API_VARIANT")) {
|
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,
|
.name = name,
|
||||||
.value = .{ .expr = mem.trim(u8, ty.children[2].char_data, " ") },
|
.value = .{ .expr = mem.trim(u8, ty.children[2].char_data, " ") },
|
||||||
};
|
});
|
||||||
} else {
|
} else {
|
||||||
var xctok = cparse.XmlCTokenizer.init(ty);
|
var xctok = cparse.XmlCTokenizer.init(ty);
|
||||||
out[i] = .{
|
try api_constants.append(allocator, .{
|
||||||
.name = name,
|
.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 {
|
fn parseTags(
|
||||||
var tags_elem = root.findChildByTag("tags") orelse return error.InvalidRegistry;
|
allocator: Allocator,
|
||||||
const tags = try allocator.alloc(registry.Tag, tags_elem.children.len);
|
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");
|
var it = tags_elem.findChildrenByTag("tag");
|
||||||
while (it.next()) |tag| {
|
while (it.next()) |tag| {
|
||||||
tags[i] = .{
|
tags.appendAssumeCapacity(.{
|
||||||
.name = tag.getAttribute("name") orelse return error.InvalidRegistry,
|
.name = tag.getAttribute("name") orelse return error.InvalidRegistry,
|
||||||
.author = tag.getAttribute("author") 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 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| {
|
while (it.next()) |feature| {
|
||||||
if (!requiredByApi(feature, api))
|
if (!requiredByApi(feature, api))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
features[i] = try parseFeature(allocator, feature, api);
|
try features.append(allocator, try parseFeature(allocator, feature, api));
|
||||||
i += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return features[0..i];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parseFeature(allocator: Allocator, feature: *xml.Element, api: registry.Api) !registry.Feature {
|
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 {
|
fn parseEnumExtension(elem: *xml.Element, parent_extnumber: ?u31) !?registry.Require.EnumExtension {
|
||||||
// check for either _SPEC_VERSION or _EXTENSION_NAME
|
// 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| {
|
if (elem.getAttribute("offset")) |offset_str| {
|
||||||
const offset = try std.fmt.parseInt(u31, offset_str, 10);
|
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|
|
const extnumber = if (elem.getAttribute("extnumber")) |num|
|
||||||
try std.fmt.parseInt(u31, num, 10)
|
try std.fmt.parseInt(u31, num, 10)
|
||||||
else
|
else
|
||||||
@@ -763,17 +781,19 @@ 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,
|
||||||
|
.value = .{
|
||||||
.field = .{
|
.field = .{
|
||||||
.name = name,
|
.name = name,
|
||||||
.value = .{ .int = value },
|
.value = .{ .int = value },
|
||||||
},
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return registry.Require.EnumExtension{
|
return registry.Require.EnumExtension{
|
||||||
.extends = extends,
|
.extends = extends,
|
||||||
.extnumber = parent_extnumber,
|
.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;
|
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");
|
var it = extensions_elem.findChildrenByTag("extension");
|
||||||
while (it.next()) |extension| {
|
while (it.next()) |extension| {
|
||||||
if (!requiredByApi(extension, api))
|
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);
|
extensions.appendAssumeCapacity(try parseExtension(allocator, extension, api));
|
||||||
i += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return extensions[0..i];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn findExtVersion(extension: *xml.Element) !u32 {
|
fn findExtVersion(extension: *xml.Element) !registry.Extension.Version {
|
||||||
var req_it = extension.findChildrenByTag("require");
|
var req_it = extension.findChildrenByTag("require");
|
||||||
while (req_it.next()) |req| {
|
while (req_it.next()) |req| {
|
||||||
var enum_it = req.findChildrenByTag("enum");
|
var enum_it = req.findChildrenByTag("enum");
|
||||||
@@ -875,17 +896,23 @@ fn findExtVersion(extension: *xml.Element) !u32 {
|
|||||||
const name = e.getAttribute("name") orelse continue;
|
const name = e.getAttribute("name") orelse continue;
|
||||||
const value = e.getAttribute("value") orelse continue;
|
const value = e.getAttribute("value") orelse continue;
|
||||||
if (mem.endsWith(u8, name, "_SPEC_VERSION")) {
|
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 {
|
fn parseExtension(allocator: Allocator, extension: *xml.Element, api: registry.Api) !registry.Extension {
|
||||||
const name = extension.getAttribute("name") orelse return error.InvalidRegistry;
|
const name = extension.getAttribute("name") orelse return error.InvalidRegistry;
|
||||||
const platform = extension.getAttribute("platform");
|
const platform = extension.getAttribute("platform");
|
||||||
|
|
||||||
|
const is_video = std.mem.startsWith(u8, name, "vulkan_video_");
|
||||||
|
|
||||||
const version = try findExtVersion(extension);
|
const version = try findExtVersion(extension);
|
||||||
|
|
||||||
// For some reason there are two ways for an extension to state its required
|
// 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: {
|
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;
|
const number_str = extension.getAttribute("number") orelse return error.InvalidRegistry;
|
||||||
break :blk try std.fmt.parseInt(u31, number_str, 10);
|
break :blk try std.fmt.parseInt(u31, number_str, 10);
|
||||||
};
|
};
|
||||||
|
|
||||||
const ext_type: ?registry.Extension.ExtensionType = blk: {
|
const ext_type: ?registry.Extension.ExtensionType = blk: {
|
||||||
|
if (is_video) break :blk .video;
|
||||||
const ext_type_str = extension.getAttribute("type") orelse break :blk null;
|
const ext_type_str = extension.getAttribute("type") orelse break :blk null;
|
||||||
if (mem.eql(u8, ext_type_str, "instance")) {
|
if (mem.eql(u8, ext_type_str, "instance")) {
|
||||||
break :blk .instance;
|
break :blk .instance;
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ pub const ApiConstant = struct {
|
|||||||
pub const Value = union(enum) {
|
pub const Value = union(enum) {
|
||||||
expr: []const u8,
|
expr: []const u8,
|
||||||
version: [4][]const u8,
|
version: [4][]const u8,
|
||||||
|
video_std_version: [3][]const u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
@@ -179,6 +180,7 @@ pub const Extension = struct {
|
|||||||
pub const ExtensionType = enum {
|
pub const ExtensionType = enum {
|
||||||
instance,
|
instance,
|
||||||
device,
|
device,
|
||||||
|
video,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Promotion = union(enum) {
|
pub const Promotion = union(enum) {
|
||||||
@@ -187,9 +189,15 @@ pub const Extension = struct {
|
|||||||
extension: []const u8,
|
extension: []const u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const Version = union(enum) {
|
||||||
|
int: u32,
|
||||||
|
alias: []const u8,
|
||||||
|
unknown,
|
||||||
|
};
|
||||||
|
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
number: u31,
|
number: u31,
|
||||||
version: u32,
|
version: Version,
|
||||||
extension_type: ?ExtensionType,
|
extension_type: ?ExtensionType,
|
||||||
depends: []const []const u8, // Other extensions
|
depends: []const []const u8, // Other extensions
|
||||||
promoted_to: Promotion,
|
promoted_to: Promotion,
|
||||||
@@ -200,9 +208,13 @@ pub const Extension = struct {
|
|||||||
|
|
||||||
pub const Require = struct {
|
pub const Require = struct {
|
||||||
pub const EnumExtension = struct {
|
pub const EnumExtension = struct {
|
||||||
|
pub const Value = union(enum) {
|
||||||
|
field: Enum.Field,
|
||||||
|
new_api_constant_expr: []const u8,
|
||||||
|
};
|
||||||
extends: []const u8,
|
extends: []const u8,
|
||||||
extnumber: ?u31,
|
extnumber: ?u31,
|
||||||
field: Enum.Field,
|
value: Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
extends: []EnumExtension,
|
extends: []EnumExtension,
|
||||||
|
|||||||
@@ -81,24 +81,18 @@ const preamble =
|
|||||||
\\ }
|
\\ }
|
||||||
\\ };
|
\\ };
|
||||||
\\}
|
\\}
|
||||||
\\pub fn makeApiVersion(variant: u3, major: u7, minor: u10, patch: u12) u32 {
|
\\pub const Version = packed struct(u32) {
|
||||||
\\ return (@as(u32, variant) << 29) | (@as(u32, major) << 22) | (@as(u32, minor) << 12) | patch;
|
\\ patch: u12,
|
||||||
\\}
|
\\ minor: u10,
|
||||||
\\pub fn apiVersionVariant(version: u32) u3 {
|
\\ major: u7,
|
||||||
\\ return @truncate(version >> 29);
|
\\ variant: u3,
|
||||||
\\}
|
\\};
|
||||||
\\pub fn apiVersionMajor(version: u32) u7 {
|
\\pub fn makeApiVersion(variant: u3, major: u7, minor: u10, patch: u12) Version {
|
||||||
\\ return @truncate(version >> 22);
|
\\ return .{ .variant = variant, .major = major, .minor = minor, .patch = patch };
|
||||||
\\}
|
|
||||||
\\pub fn apiVersionMinor(version: u32) u10 {
|
|
||||||
\\ return @truncate(version >> 12);
|
|
||||||
\\}
|
|
||||||
\\pub fn apiVersionPatch(version: u32) u12 {
|
|
||||||
\\ return @truncate(version);
|
|
||||||
\\}
|
\\}
|
||||||
\\pub const ApiInfo = struct {
|
\\pub const ApiInfo = struct {
|
||||||
\\ name: [:0]const u8 = "custom",
|
\\ name: [:0]const u8 = "custom",
|
||||||
\\ version: u32 = makeApiVersion(0, 0, 0, 0),
|
\\ version: Version = makeApiVersion(0, 0, 0, 0),
|
||||||
\\ base_commands: BaseCommandFlags = .{},
|
\\ base_commands: BaseCommandFlags = .{},
|
||||||
\\ instance_commands: InstanceCommandFlags = .{},
|
\\ instance_commands: InstanceCommandFlags = .{},
|
||||||
\\ device_commands: DeviceCommandFlags = .{},
|
\\ device_commands: DeviceCommandFlags = .{},
|
||||||
@@ -375,25 +369,43 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
allocator: Allocator,
|
allocator: Allocator,
|
||||||
registry: *const reg.Registry,
|
registry: *const reg.Registry,
|
||||||
id_renderer: *IdRenderer,
|
id_renderer: *IdRenderer,
|
||||||
declarations_by_name: std.StringHashMap(*const reg.DeclarationType),
|
decls_by_name: std.StringArrayHashMap(reg.DeclarationType),
|
||||||
structure_types: std.StringHashMap(void),
|
structure_types: std.StringHashMap(void),
|
||||||
|
have_video: bool,
|
||||||
|
|
||||||
fn init(writer: WriterType, allocator: Allocator, registry: *const reg.Registry, id_renderer: *IdRenderer) !Self {
|
fn init(
|
||||||
var declarations_by_name = std.StringHashMap(*const reg.DeclarationType).init(allocator);
|
writer: WriterType,
|
||||||
errdefer declarations_by_name.deinit();
|
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| {
|
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) {
|
if (result.found_existing) {
|
||||||
|
// 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});
|
std.log.err("duplicate registry entry '{s}'", .{decl.name});
|
||||||
return error.InvalidRegistry;
|
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_decl = decls_by_name.get("VkStructureType") orelse return error.InvalidRegistry;
|
||||||
const vk_structure_type = switch (vk_structure_type_decl.*) {
|
const vk_structure_type = switch (vk_structure_type_decl) {
|
||||||
.enumeration => |e| e,
|
.enumeration => |e| e,
|
||||||
else => return error.InvalidRegistry,
|
else => return error.InvalidRegistry,
|
||||||
};
|
};
|
||||||
@@ -409,13 +421,14 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.registry = registry,
|
.registry = registry,
|
||||||
.id_renderer = id_renderer,
|
.id_renderer = id_renderer,
|
||||||
.declarations_by_name = declarations_by_name,
|
.decls_by_name = decls_by_name,
|
||||||
.structure_types = structure_types,
|
.structure_types = structure_types,
|
||||||
|
.have_video = have_video,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deinit(self: *Self) void {
|
fn deinit(self: *Self) void {
|
||||||
self.declarations_by_name.deinit();
|
self.decls_by_name.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn writeIdentifier(self: Self, id: []const u8) !void {
|
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 {
|
fn resolveDeclaration(self: Self, name: []const u8) ?reg.DeclarationType {
|
||||||
const decl = self.declarations_by_name.get(name) orelse return null;
|
const decl = self.decls_by_name.get(name) orelse return null;
|
||||||
return self.resolveAlias(decl.*) catch return null;
|
return self.resolveAlias(decl) catch return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolveAlias(self: Self, start_decl: reg.DeclarationType) !reg.DeclarationType {
|
fn resolveAlias(self: Self, start_decl: reg.DeclarationType) !reg.DeclarationType {
|
||||||
@@ -513,8 +526,7 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
else => return decl,
|
else => return decl,
|
||||||
};
|
};
|
||||||
|
|
||||||
const decl_ptr = self.declarations_by_name.get(name) orelse return error.InvalidRegistry;
|
decl = self.decls_by_name.get(name) orelse return error.InvalidRegistry;
|
||||||
decl = decl_ptr.*;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -614,12 +626,17 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
fn render(self: *Self) !void {
|
fn render(self: *Self) !void {
|
||||||
try self.writer.writeAll(preamble);
|
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| {
|
for (self.registry.api_constants) |api_constant| {
|
||||||
try self.renderApiConstant(api_constant);
|
try self.renderApiConstant(api_constant);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (self.registry.decls) |decl| {
|
for (self.decls_by_name.keys(), self.decls_by_name.values()) |name, decl_type| {
|
||||||
try self.renderDecl(decl);
|
try self.renderDecl(.{
|
||||||
|
.name = name,
|
||||||
|
.decl_type = decl_type,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
try self.renderCommandPtrs();
|
try self.renderCommandPtrs();
|
||||||
@@ -636,8 +653,12 @@ 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| {
|
inline .version, .video_std_version => |version, kind| {
|
||||||
try self.writer.writeAll("makeApiVersion(");
|
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| {
|
for (version, 0..) |part, i| {
|
||||||
if (i != 0) {
|
if (i != 0) {
|
||||||
try self.writer.writeAll(", ");
|
try self.writer.writeAll(", ");
|
||||||
@@ -688,6 +709,7 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
} else if (mem.eql(u8, suffix.text, "U")) {
|
} else if (mem.eql(u8, suffix.text, "U")) {
|
||||||
try self.writer.print("@as(u32, {s})", .{tok.text});
|
try self.writer.print("@as(u32, {s})", .{tok.text});
|
||||||
} else {
|
} else {
|
||||||
|
std.debug.print("aaa {s}\n", .{suffix.text});
|
||||||
return error.InvalidApiConstant;
|
return error.InvalidApiConstant;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -894,17 +916,6 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
.{maybe_author orelse ""},
|
.{maybe_author orelse ""},
|
||||||
);
|
);
|
||||||
return true;
|
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")) {
|
} else if (std.mem.eql(u8, basename, "VkClusterAccelerationStructureBuildTriangleClusterInfo")) {
|
||||||
try self.writer.print(
|
try self.writer.print(
|
||||||
\\extern struct {{
|
\\extern struct {{
|
||||||
@@ -980,11 +991,50 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
return false;
|
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 {
|
fn renderContainer(self: *Self, name: []const u8, container: reg.Container) !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(" = ");
|
try self.writer.writeAll(" = ");
|
||||||
|
|
||||||
|
if (try self.renderSimpleBitContainer(container)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (try self.renderSpecialContainer(name)) {
|
if (try self.renderSpecialContainer(name)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1231,7 +1281,9 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn renderForeign(self: *Self, name: []const u8, foreign: reg.Foreign) !void {
|
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
|
return; // Skip built-in types, they are handled differently
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1262,18 +1314,18 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn renderCommandPtrs(self: *Self) !void {
|
fn renderCommandPtrs(self: *Self) !void {
|
||||||
for (self.registry.decls) |decl| {
|
for (self.decls_by_name.keys(), self.decls_by_name.values()) |name, decl_type| {
|
||||||
switch (decl.decl_type) {
|
switch (decl_type) {
|
||||||
.command => {
|
.command => {
|
||||||
try self.writer.writeAll("pub const ");
|
try self.writer.writeAll("pub const ");
|
||||||
try self.renderCommandPtrName(decl.name);
|
try self.renderCommandPtrName(name);
|
||||||
try self.writer.writeAll(" = ");
|
try self.writer.writeAll(" = ");
|
||||||
try self.renderCommandPtr(decl.decl_type.command, false);
|
try self.renderCommandPtr(decl_type.command, false);
|
||||||
try self.writer.writeAll(";\n");
|
try self.writer.writeAll(";\n");
|
||||||
},
|
},
|
||||||
.alias => |alias| if (alias.target == .other_command) {
|
.alias => |alias| if (alias.target == .other_command) {
|
||||||
try self.writer.writeAll("pub const ");
|
try self.writer.writeAll("pub const ");
|
||||||
try self.renderCommandPtrName(decl.name);
|
try self.renderCommandPtrName(name);
|
||||||
try self.writer.writeAll(" = ");
|
try self.writer.writeAll(" = ");
|
||||||
try self.renderCommandPtrName(alias.name);
|
try self.renderCommandPtrName(alias.name);
|
||||||
try self.writer.writeAll(";\n");
|
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.
|
// 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();
|
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();
|
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();
|
defer device_commands.deinit();
|
||||||
for (self.registry.features) |feature| {
|
for (self.registry.features) |feature| {
|
||||||
|
base_commands.clearRetainingCapacity();
|
||||||
|
instance_commands.clearRetainingCapacity();
|
||||||
|
device_commands.clearRetainingCapacity();
|
||||||
|
|
||||||
try self.writer.writeAll("pub const ");
|
try self.writer.writeAll("pub const ");
|
||||||
try self.writeIdentifierWithCase(.snake, trimVkNamespace(feature.name));
|
try self.writeIdentifierWithCase(.snake, trimVkNamespace(feature.name));
|
||||||
try self.writer.writeAll("= ApiInfo {\n");
|
try self.writer.writeAll("= ApiInfo {\n");
|
||||||
@@ -1317,31 +1373,28 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
};
|
};
|
||||||
const class = classifyCommandDispatch(command_name, command);
|
const class = classifyCommandDispatch(command_name, command);
|
||||||
switch (class) {
|
switch (class) {
|
||||||
.base => {
|
.base => try base_commands.put(command_name, {}),
|
||||||
try base_commands.insert(command_name);
|
.instance => try instance_commands.put(command_name, {}),
|
||||||
},
|
.device => try device_commands.put(command_name, {}),
|
||||||
.instance => {
|
|
||||||
try instance_commands.insert(command_name);
|
|
||||||
},
|
|
||||||
.device => {
|
|
||||||
try device_commands.insert(command_name);
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// and write them out
|
// and write them out
|
||||||
// clear command lists for next iteration
|
// clear command lists for next iteration
|
||||||
|
if (base_commands.count() != 0) {
|
||||||
try self.writer.writeAll(".base_commands = ");
|
try self.writer.writeAll(".base_commands = ");
|
||||||
try self.renderCommandFlags(&base_commands);
|
try self.renderCommandFlags(base_commands.keys());
|
||||||
base_commands.hash_map.clearRetainingCapacity();
|
}
|
||||||
|
|
||||||
|
if (instance_commands.count() != 0) {
|
||||||
try self.writer.writeAll(".instance_commands = ");
|
try self.writer.writeAll(".instance_commands = ");
|
||||||
try self.renderCommandFlags(&instance_commands);
|
try self.renderCommandFlags(instance_commands.keys());
|
||||||
instance_commands.hash_map.clearRetainingCapacity();
|
}
|
||||||
|
|
||||||
|
if (device_commands.count() != 0) {
|
||||||
try self.writer.writeAll(".device_commands = ");
|
try self.writer.writeAll(".device_commands = ");
|
||||||
try self.renderCommandFlags(&device_commands);
|
try self.renderCommandFlags(device_commands.keys());
|
||||||
device_commands.hash_map.clearRetainingCapacity();
|
}
|
||||||
|
|
||||||
try self.writer.writeAll("};\n");
|
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.
|
// 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();
|
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();
|
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();
|
defer device_commands.deinit();
|
||||||
for (self.registry.extensions) |ext| {
|
for (self.registry.extensions) |ext| {
|
||||||
|
base_commands.clearRetainingCapacity();
|
||||||
|
instance_commands.clearRetainingCapacity();
|
||||||
|
device_commands.clearRetainingCapacity();
|
||||||
|
|
||||||
try self.writer.writeAll("pub const ");
|
try self.writer.writeAll("pub const ");
|
||||||
|
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.writeIdentifierWithCase(.snake, trimVkNamespace(ext.name));
|
||||||
|
}
|
||||||
try self.writer.writeAll("= ApiInfo {\n");
|
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
|
// collect extension functions
|
||||||
for (ext.requires) |require| {
|
for (ext.requires) |require| {
|
||||||
for (require.commands) |command_name| {
|
for (require.commands) |command_name| {
|
||||||
@@ -1379,42 +1450,38 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
};
|
};
|
||||||
const class = classifyCommandDispatch(command_name, command);
|
const class = classifyCommandDispatch(command_name, command);
|
||||||
switch (class) {
|
switch (class) {
|
||||||
.base => {
|
.base => try base_commands.put(command_name, {}),
|
||||||
try base_commands.insert(command_name);
|
.instance => try instance_commands.put(command_name, {}),
|
||||||
},
|
.device => try device_commands.put(command_name, {}),
|
||||||
.instance => {
|
|
||||||
try instance_commands.insert(command_name);
|
|
||||||
},
|
|
||||||
.device => {
|
|
||||||
try device_commands.insert(command_name);
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// and write them out
|
// and write them out
|
||||||
|
if (base_commands.count() != 0) {
|
||||||
try self.writer.writeAll(".base_commands = ");
|
try self.writer.writeAll(".base_commands = ");
|
||||||
try self.renderCommandFlags(&base_commands);
|
try self.renderCommandFlags(base_commands.keys());
|
||||||
base_commands.hash_map.clearRetainingCapacity();
|
}
|
||||||
|
|
||||||
|
if (instance_commands.count() != 0) {
|
||||||
try self.writer.writeAll(".instance_commands = ");
|
try self.writer.writeAll(".instance_commands = ");
|
||||||
try self.renderCommandFlags(&instance_commands);
|
try self.renderCommandFlags(instance_commands.keys());
|
||||||
instance_commands.hash_map.clearRetainingCapacity();
|
}
|
||||||
|
|
||||||
|
if (device_commands.count() != 0) {
|
||||||
try self.writer.writeAll(".device_commands = ");
|
try self.writer.writeAll(".device_commands = ");
|
||||||
try self.renderCommandFlags(&device_commands);
|
try self.renderCommandFlags(device_commands.keys());
|
||||||
device_commands.hash_map.clearRetainingCapacity();
|
}
|
||||||
|
|
||||||
try self.writer.writeAll("};\n");
|
try self.writer.writeAll("};\n");
|
||||||
}
|
}
|
||||||
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");
|
try self.writer.writeAll(".{\n");
|
||||||
var iterator = commands.iterator();
|
for (commands) |command_name| {
|
||||||
while (iterator.next()) |command_name| {
|
|
||||||
try self.writer.writeAll(".");
|
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(" = true, \n");
|
||||||
}
|
}
|
||||||
try self.writer.writeAll("},\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 {
|
pub fn render(
|
||||||
var renderer = try Renderer(@TypeOf(writer)).init(writer, allocator, registry, id_renderer);
|
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();
|
defer renderer.deinit();
|
||||||
try renderer.render();
|
try renderer.render();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,76 +22,79 @@ pub const MTLSharedEvent_id = u32;
|
|||||||
pub const IOSurfaceRef = u32;
|
pub const IOSurfaceRef = u32;
|
||||||
|
|
||||||
// For some reason these types are exported in a different header, and not described in vk.xml.
|
// For some reason these types are exported in a different header, and not described in vk.xml.
|
||||||
pub const StdVideoH264ProfileIdc = u32;
|
// If we are not also generating these, the user will have to manually specify them.
|
||||||
pub const StdVideoH264LevelIdc = u32;
|
pub usingnamespace if (!vk.have_vulkan_video) struct {
|
||||||
pub const StdVideoH264ChromaFormatIdc = u32;
|
pub const StdVideoH264ProfileIdc = u32;
|
||||||
pub const StdVideoH264PocType = u32;
|
pub const StdVideoH264LevelIdc = u32;
|
||||||
pub const StdVideoH264SpsFlags = u32;
|
pub const StdVideoH264ChromaFormatIdc = u32;
|
||||||
pub const StdVideoH264ScalingLists = u32;
|
pub const StdVideoH264PocType = u32;
|
||||||
pub const StdVideoH264SequenceParameterSetVui = u32;
|
pub const StdVideoH264SpsFlags = u32;
|
||||||
pub const StdVideoH264AspectRatioIdc = u32;
|
pub const StdVideoH264ScalingLists = u32;
|
||||||
pub const StdVideoH264HrdParameters = u32;
|
pub const StdVideoH264SequenceParameterSetVui = u32;
|
||||||
pub const StdVideoH264SpsVuiFlags = u32;
|
pub const StdVideoH264AspectRatioIdc = u32;
|
||||||
pub const StdVideoH264WeightedBipredIdc = u32;
|
pub const StdVideoH264HrdParameters = u32;
|
||||||
pub const StdVideoH264PpsFlags = u32;
|
pub const StdVideoH264SpsVuiFlags = u32;
|
||||||
pub const StdVideoH264SliceType = u32;
|
pub const StdVideoH264WeightedBipredIdc = u32;
|
||||||
pub const StdVideoH264CabacInitIdc = u32;
|
pub const StdVideoH264PpsFlags = u32;
|
||||||
pub const StdVideoH264DisableDeblockingFilterIdc = u32;
|
pub const StdVideoH264SliceType = u32;
|
||||||
pub const StdVideoH264PictureType = u32;
|
pub const StdVideoH264CabacInitIdc = u32;
|
||||||
pub const StdVideoH264ModificationOfPicNumsIdc = u32;
|
pub const StdVideoH264DisableDeblockingFilterIdc = u32;
|
||||||
pub const StdVideoH264MemMgmtControlOp = u32;
|
pub const StdVideoH264PictureType = u32;
|
||||||
pub const StdVideoDecodeH264PictureInfo = u32;
|
pub const StdVideoH264ModificationOfPicNumsIdc = u32;
|
||||||
pub const StdVideoDecodeH264ReferenceInfo = u32;
|
pub const StdVideoH264MemMgmtControlOp = u32;
|
||||||
pub const StdVideoDecodeH264PictureInfoFlags = u32;
|
pub const StdVideoDecodeH264PictureInfo = u32;
|
||||||
pub const StdVideoDecodeH264ReferenceInfoFlags = u32;
|
pub const StdVideoDecodeH264ReferenceInfo = u32;
|
||||||
pub const StdVideoH264SequenceParameterSet = u32;
|
pub const StdVideoDecodeH264PictureInfoFlags = u32;
|
||||||
pub const StdVideoH264PictureParameterSet = u32;
|
pub const StdVideoDecodeH264ReferenceInfoFlags = u32;
|
||||||
pub const StdVideoH265ProfileIdc = u32;
|
pub const StdVideoH264SequenceParameterSet = u32;
|
||||||
pub const StdVideoH265VideoParameterSet = u32;
|
pub const StdVideoH264PictureParameterSet = u32;
|
||||||
pub const StdVideoH265SequenceParameterSet = u32;
|
pub const StdVideoH265ProfileIdc = u32;
|
||||||
pub const StdVideoH265PictureParameterSet = u32;
|
pub const StdVideoH265VideoParameterSet = u32;
|
||||||
pub const StdVideoH265DecPicBufMgr = u32;
|
pub const StdVideoH265SequenceParameterSet = u32;
|
||||||
pub const StdVideoH265HrdParameters = u32;
|
pub const StdVideoH265PictureParameterSet = u32;
|
||||||
pub const StdVideoH265VpsFlags = u32;
|
pub const StdVideoH265DecPicBufMgr = u32;
|
||||||
pub const StdVideoH265LevelIdc = u32;
|
pub const StdVideoH265HrdParameters = u32;
|
||||||
pub const StdVideoH265SpsFlags = u32;
|
pub const StdVideoH265VpsFlags = u32;
|
||||||
pub const StdVideoH265ScalingLists = u32;
|
pub const StdVideoH265LevelIdc = u32;
|
||||||
pub const StdVideoH265SequenceParameterSetVui = u32;
|
pub const StdVideoH265SpsFlags = u32;
|
||||||
pub const StdVideoH265PredictorPaletteEntries = u32;
|
pub const StdVideoH265ScalingLists = u32;
|
||||||
pub const StdVideoH265PpsFlags = u32;
|
pub const StdVideoH265SequenceParameterSetVui = u32;
|
||||||
pub const StdVideoH265SubLayerHrdParameters = u32;
|
pub const StdVideoH265PredictorPaletteEntries = u32;
|
||||||
pub const StdVideoH265HrdFlags = u32;
|
pub const StdVideoH265PpsFlags = u32;
|
||||||
pub const StdVideoH265SpsVuiFlags = u32;
|
pub const StdVideoH265SubLayerHrdParameters = u32;
|
||||||
pub const StdVideoH265SliceType = u32;
|
pub const StdVideoH265HrdFlags = u32;
|
||||||
pub const StdVideoH265PictureType = u32;
|
pub const StdVideoH265SpsVuiFlags = u32;
|
||||||
pub const StdVideoDecodeH265PictureInfo = u32;
|
pub const StdVideoH265SliceType = u32;
|
||||||
pub const StdVideoDecodeH265ReferenceInfo = u32;
|
pub const StdVideoH265PictureType = u32;
|
||||||
pub const StdVideoDecodeH265PictureInfoFlags = u32;
|
pub const StdVideoDecodeH265PictureInfo = u32;
|
||||||
pub const StdVideoDecodeH265ReferenceInfoFlags = u32;
|
pub const StdVideoDecodeH265ReferenceInfo = u32;
|
||||||
pub const StdVideoAV1Profile = u32;
|
pub const StdVideoDecodeH265PictureInfoFlags = u32;
|
||||||
pub const StdVideoAV1Level = u32;
|
pub const StdVideoDecodeH265ReferenceInfoFlags = u32;
|
||||||
pub const StdVideoAV1SequenceHeader = u32;
|
pub const StdVideoAV1Profile = u32;
|
||||||
pub const StdVideoDecodeAV1PictureInfo = u32;
|
pub const StdVideoAV1Level = u32;
|
||||||
pub const StdVideoDecodeAV1ReferenceInfo = u32;
|
pub const StdVideoAV1SequenceHeader = u32;
|
||||||
pub const StdVideoEncodeH264SliceHeader = u32;
|
pub const StdVideoDecodeAV1PictureInfo = u32;
|
||||||
pub const StdVideoEncodeH264PictureInfo = u32;
|
pub const StdVideoDecodeAV1ReferenceInfo = u32;
|
||||||
pub const StdVideoEncodeH264ReferenceInfo = u32;
|
pub const StdVideoEncodeH264SliceHeader = u32;
|
||||||
pub const StdVideoEncodeH264SliceHeaderFlags = u32;
|
pub const StdVideoEncodeH264PictureInfo = u32;
|
||||||
pub const StdVideoEncodeH264ReferenceListsInfo = u32;
|
pub const StdVideoEncodeH264ReferenceInfo = u32;
|
||||||
pub const StdVideoEncodeH264PictureInfoFlags = u32;
|
pub const StdVideoEncodeH264SliceHeaderFlags = u32;
|
||||||
pub const StdVideoEncodeH264ReferenceInfoFlags = u32;
|
pub const StdVideoEncodeH264ReferenceListsInfo = u32;
|
||||||
pub const StdVideoEncodeH264RefMgmtFlags = u32;
|
pub const StdVideoEncodeH264PictureInfoFlags = u32;
|
||||||
pub const StdVideoEncodeH264RefListModEntry = u32;
|
pub const StdVideoEncodeH264ReferenceInfoFlags = u32;
|
||||||
pub const StdVideoEncodeH264RefPicMarkingEntry = u32;
|
pub const StdVideoEncodeH264RefMgmtFlags = u32;
|
||||||
pub const StdVideoEncodeH265PictureInfoFlags = u32;
|
pub const StdVideoEncodeH264RefListModEntry = u32;
|
||||||
pub const StdVideoEncodeH265PictureInfo = u32;
|
pub const StdVideoEncodeH264RefPicMarkingEntry = u32;
|
||||||
pub const StdVideoEncodeH265SliceSegmentHeader = u32;
|
pub const StdVideoEncodeH265PictureInfoFlags = u32;
|
||||||
pub const StdVideoEncodeH265ReferenceInfo = u32;
|
pub const StdVideoEncodeH265PictureInfo = u32;
|
||||||
pub const StdVideoEncodeH265ReferenceListsInfo = u32;
|
pub const StdVideoEncodeH265SliceSegmentHeader = u32;
|
||||||
pub const StdVideoEncodeH265SliceSegmentHeaderFlags = u32;
|
pub const StdVideoEncodeH265ReferenceInfo = u32;
|
||||||
pub const StdVideoEncodeH265ReferenceInfoFlags = u32;
|
pub const StdVideoEncodeH265ReferenceListsInfo = u32;
|
||||||
pub const StdVideoEncodeH265ReferenceModificationFlags = u32;
|
pub const StdVideoEncodeH265SliceSegmentHeaderFlags = u32;
|
||||||
pub const StdVideoEncodeAV1OperatingPointInfo = u32;
|
pub const StdVideoEncodeH265ReferenceInfoFlags = u32;
|
||||||
|
pub const StdVideoEncodeH265ReferenceModificationFlags = u32;
|
||||||
|
pub const StdVideoEncodeAV1OperatingPointInfo = u32;
|
||||||
|
} else struct {};
|
||||||
|
|
||||||
comptime {
|
comptime {
|
||||||
@setEvalBranchQuota(1000000);
|
@setEvalBranchQuota(1000000);
|
||||||
|
|||||||
Reference in New Issue
Block a user