forked from mirror/vulkan-zig
Extension/feature filtering + main generate function
This commit is contained in:
@@ -158,7 +158,7 @@ pub const Generator = struct {
|
|||||||
reg_arena: std.heap.ArenaAllocator,
|
reg_arena: std.heap.ArenaAllocator,
|
||||||
registry: reg.Registry,
|
registry: reg.Registry,
|
||||||
|
|
||||||
pub fn init(allocator: *Allocator, spec: *xml.Element) !Generator {
|
fn init(allocator: *Allocator, spec: *xml.Element) !Generator {
|
||||||
const result = try parseXml(allocator, spec);
|
const result = try parseXml(allocator, spec);
|
||||||
return Generator{
|
return Generator{
|
||||||
.gpa = allocator,
|
.gpa = allocator,
|
||||||
@@ -167,18 +167,156 @@ pub const Generator = struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: Generator) void {
|
fn deinit(self: Generator) void {
|
||||||
self.reg_arena.deinit();
|
self.reg_arena.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn filterFeatureLevel(self: *Generator, feature_level: FeatureLevel) void {
|
||||||
|
var write_index: usize = 0;
|
||||||
|
for (self.registry.features) |feature| {
|
||||||
|
if (cmpFeatureLevels(feature.level, feature_level).compare(.lte)) {
|
||||||
|
self.registry.features[write_index] = feature;
|
||||||
|
write_index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.registry.features.len = write_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn filterExtensions(self: *Generator, feature_level: FeatureLevel, filter: ExtensionFilter) !void {
|
||||||
|
switch (filter) {
|
||||||
|
.all => {
|
||||||
|
var write_index: usize = 0;
|
||||||
|
for (self.registry.extensions) |ext| {
|
||||||
|
const keep = if (ext.required_feature_level) |required_level|
|
||||||
|
cmpFeatureLevels(required_level, feature_level).compare(.lte)
|
||||||
|
else
|
||||||
|
true;
|
||||||
|
if (keep) {
|
||||||
|
self.registry.extensions[write_index] = ext;
|
||||||
|
write_index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.registry.extensions.len = write_index;
|
||||||
|
},
|
||||||
|
.none => self.registry.extensions.len = 0,
|
||||||
|
.include => |include| {
|
||||||
|
try self.filterExtensionsInclude(include);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (self.registry.extensions) |*ext| {
|
||||||
|
if (ext.required_feature_level) |required_level| {
|
||||||
|
if (cmpFeatureLevels(required_level, feature_level).compare(.gt)) {
|
||||||
|
return error.FeatureLevelTooLow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var write_index: usize = 0;
|
||||||
|
for (ext.requires) |req| {
|
||||||
|
const keep = if (req.required_feature_level) |required_level|
|
||||||
|
cmpFeatureLevels(required_level, feature_level).compare(.lte)
|
||||||
|
else
|
||||||
|
true;
|
||||||
|
|
||||||
|
if (keep) {
|
||||||
|
ext.requires[write_index] = req;
|
||||||
|
write_index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ext.requires.len = write_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn filterExtensionsInclude(self: *Generator, include: []const []const u8) !void {
|
||||||
|
if (include.len == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Entry = struct {
|
||||||
|
ext: *const reg.Extension,
|
||||||
|
seen: bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
var extensions = std.StringHashMap(Entry).init(self.gpa);
|
||||||
|
defer extensions.deinit();
|
||||||
|
|
||||||
|
try extensions.ensureCapacity(self.registry.extensions.len);
|
||||||
|
for (self.registry.extensions) |*ext| {
|
||||||
|
_ = try extensions.put(ext.name, .{.ext = ext, .seen = false});
|
||||||
|
}
|
||||||
|
|
||||||
|
var queue = std.fifo.LinearFifo(*const reg.Extension, .Dynamic).init(self.gpa);
|
||||||
|
defer queue.deinit();
|
||||||
|
|
||||||
|
for (include) |ext_name| {
|
||||||
|
const kv = extensions.get(ext_name) orelse return error.MissingExtension;
|
||||||
|
if (!kv.value.seen) {
|
||||||
|
kv.value.seen = true;
|
||||||
|
try queue.writeItem(kv.value.ext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (queue.readItem()) |ext| {
|
||||||
|
for (ext.depends) |dep| {
|
||||||
|
const kv = extensions.get(dep) orelse return error.MissingExtension;
|
||||||
|
if (!kv.value.seen) {
|
||||||
|
kv.value.seen = true;
|
||||||
|
try queue.writeItem(kv.value.ext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var write_index: usize = 0;
|
||||||
|
for (self.registry.extensions) |ext| {
|
||||||
|
const kv = extensions.getValue(ext.name).?;
|
||||||
|
if (kv.seen) {
|
||||||
|
self.registry.extensions[write_index] = ext;
|
||||||
|
write_index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.registry.extensions.len = write_index;
|
||||||
|
}
|
||||||
|
|
||||||
// Solve `registry.declarations` according to `registry.extensions` and `registry.features`.
|
// Solve `registry.declarations` according to `registry.extensions` and `registry.features`.
|
||||||
pub fn resolveDeclarations(self: *Generator) !void {
|
fn resolveDeclarations(self: *Generator) !void {
|
||||||
var resolver = DeclarationResolver.init(self.gpa, &self.reg_arena.allocator, &self.registry);
|
var resolver = DeclarationResolver.init(self.gpa, &self.reg_arena.allocator, &self.registry);
|
||||||
defer resolver.deinit();
|
defer resolver.deinit();
|
||||||
try resolver.resolve();
|
try resolver.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(self: *Generator, out_stream: var) !void {
|
fn render(self: *Generator, out_stream: var) !void {
|
||||||
try renderRegistry(out_stream, &self.reg_arena.allocator, &self.registry);
|
try renderRegistry(out_stream, &self.reg_arena.allocator, &self.registry);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const ExtensionFilter = union(enum) {
|
||||||
|
/// All extensions are included, according to Options.feature_level
|
||||||
|
all,
|
||||||
|
/// No extensions are included
|
||||||
|
none,
|
||||||
|
/// Exactly these extensions are included, along with their dependencies
|
||||||
|
include: []const []const u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Options = struct {
|
||||||
|
feature_level: FeatureLevel,
|
||||||
|
extensions: ExtensionFilter,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn generate(allocator: *Allocator, xml_reader: var, writer: var, opts: Options) !void {
|
||||||
|
const source = try xml_reader.readAllAlloc(allocator, std.math.maxInt(usize));
|
||||||
|
defer allocator.free(source);
|
||||||
|
|
||||||
|
const spec = try xml.parse(allocator, source);
|
||||||
|
defer spec.deinit();
|
||||||
|
|
||||||
|
var gen = try Generator.init(allocator, spec.root);
|
||||||
|
defer gen.deinit();
|
||||||
|
|
||||||
|
gen.filterFeatureLevel(opts.feature_level);
|
||||||
|
try gen.filterExtensions(opts.feature_level, opts.extensions);
|
||||||
|
|
||||||
|
try gen.resolveDeclarations();
|
||||||
|
try gen.render(writer);
|
||||||
|
}
|
||||||
|
|||||||
@@ -120,24 +120,12 @@ pub fn main() !void {
|
|||||||
const file = try std.fs.cwd().openFileZ(std.os.argv[1], .{});
|
const file = try std.fs.cwd().openFileZ(std.os.argv[1], .{});
|
||||||
defer file.close();
|
defer file.close();
|
||||||
|
|
||||||
const size = try file.seekableStream().getEndPos();
|
|
||||||
const source = try allocator.alloc(u8, size);
|
|
||||||
defer allocator.free(source);
|
|
||||||
|
|
||||||
_ = try file.inStream().read(source);
|
|
||||||
|
|
||||||
const spec = try xml.parse(allocator, source);
|
|
||||||
defer spec.deinit();
|
|
||||||
|
|
||||||
var gen = try vkgen.Generator.init(allocator, spec.root);
|
|
||||||
defer gen.deinit();
|
|
||||||
|
|
||||||
try gen.resolveDeclarations();
|
|
||||||
|
|
||||||
const stdout = std.io.getStdOut().writer();
|
const stdout = std.io.getStdOut().writer();
|
||||||
try gen.render(stdout);
|
try vkgen.generate(&prof_alloc.allocator, file.reader(), stdout, .{
|
||||||
|
.feature_level = .{.major = 1, .minor = 0},
|
||||||
|
.extensions = .all,
|
||||||
|
});
|
||||||
|
|
||||||
std.debug.warn("Total declarations: {}\n", .{gen.registry.decls.len});
|
|
||||||
std.debug.warn("Total memory usage: {} KiB\n", .{@divTrunc(prof_alloc.max_usage, 1024)});
|
std.debug.warn("Total memory usage: {} KiB\n", .{@divTrunc(prof_alloc.max_usage, 1024)});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -162,6 +162,7 @@ pub const Extension = struct {
|
|||||||
depends: []const []const u8, // Other extensions
|
depends: []const []const u8, // Other extensions
|
||||||
promoted_to: Promotion,
|
promoted_to: Promotion,
|
||||||
platform: ?[]const u8,
|
platform: ?[]const u8,
|
||||||
|
required_feature_level: ?FeatureLevel,
|
||||||
requires: []Require,
|
requires: []Require,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -732,12 +732,6 @@ fn parseExtension(allocator: *Allocator, extension: *xml.Element) !registry.Exte
|
|||||||
var it = extension.findChildrenByTag("require");
|
var it = extension.findChildrenByTag("require");
|
||||||
while (it.next()) |require| {
|
while (it.next()) |require| {
|
||||||
requires[i] = try parseRequire(allocator, require, number);
|
requires[i] = try parseRequire(allocator, require, number);
|
||||||
|
|
||||||
// If the required feature level has been set explicitly, keep it.
|
|
||||||
if (requires[i].required_feature_level == null) {
|
|
||||||
requires[i].required_feature_level = requires_core;
|
|
||||||
}
|
|
||||||
|
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -749,6 +743,7 @@ fn parseExtension(allocator: *Allocator, extension: *xml.Element) !registry.Exte
|
|||||||
.depends = depends,
|
.depends = depends,
|
||||||
.promoted_to = promoted_to,
|
.promoted_to = promoted_to,
|
||||||
.platform = platform,
|
.platform = platform,
|
||||||
|
.required_feature_level = requires_core,
|
||||||
.requires = allocator.shrink(requires, i)
|
.requires = allocator.shrink(requires, i)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -281,10 +281,14 @@ fn Renderer(comptime WriterType: type) type {
|
|||||||
peeked = null;
|
peeked = null;
|
||||||
|
|
||||||
switch (tok.id) {
|
switch (tok.id) {
|
||||||
.lparen, .rparen, .tilde, .minus, .id => {
|
.lparen, .rparen, .tilde, .minus => {
|
||||||
try self.writer.writeAll(tok.text);
|
try self.writer.writeAll(tok.text);
|
||||||
continue;
|
continue;
|
||||||
},
|
},
|
||||||
|
.id => {
|
||||||
|
try self.writeIdentifier(util.trimVkNamespace(api_constant.value.alias));
|
||||||
|
continue;
|
||||||
|
},
|
||||||
.int => {},
|
.int => {},
|
||||||
else => return error.InvalidApiConstant,
|
else => return error.InvalidApiConstant,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user