From c454400ff344a72bfe2e48634992508f76f6523d Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Mon, 29 Jun 2020 19:45:23 +0200 Subject: [PATCH] Extension/feature filtering + main generate function --- generator/generator.zig | 146 ++++++++++++++++++++++++++++++++++- generator/main.zig | 20 +---- generator/registry.zig | 1 + generator/registry/parse.zig | 7 +- generator/render.zig | 6 +- 5 files changed, 153 insertions(+), 27 deletions(-) diff --git a/generator/generator.zig b/generator/generator.zig index b72c423..4285fb2 100644 --- a/generator/generator.zig +++ b/generator/generator.zig @@ -158,7 +158,7 @@ pub const Generator = struct { reg_arena: std.heap.ArenaAllocator, 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); return Generator{ .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(); } + 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`. - pub fn resolveDeclarations(self: *Generator) !void { + fn resolveDeclarations(self: *Generator) !void { var resolver = DeclarationResolver.init(self.gpa, &self.reg_arena.allocator, &self.registry); defer resolver.deinit(); 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); } }; + +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); +} diff --git a/generator/main.zig b/generator/main.zig index bb4956b..2229aaa 100644 --- a/generator/main.zig +++ b/generator/main.zig @@ -120,24 +120,12 @@ pub fn main() !void { const file = try std.fs.cwd().openFileZ(std.os.argv[1], .{}); 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(); - 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)}); } diff --git a/generator/registry.zig b/generator/registry.zig index eddf29a..0b11900 100644 --- a/generator/registry.zig +++ b/generator/registry.zig @@ -162,6 +162,7 @@ pub const Extension = struct { depends: []const []const u8, // Other extensions promoted_to: Promotion, platform: ?[]const u8, + required_feature_level: ?FeatureLevel, requires: []Require, }; diff --git a/generator/registry/parse.zig b/generator/registry/parse.zig index 907e71e..37f0362 100644 --- a/generator/registry/parse.zig +++ b/generator/registry/parse.zig @@ -732,12 +732,6 @@ fn parseExtension(allocator: *Allocator, extension: *xml.Element) !registry.Exte var it = extension.findChildrenByTag("require"); while (it.next()) |require| { 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; } @@ -749,6 +743,7 @@ fn parseExtension(allocator: *Allocator, extension: *xml.Element) !registry.Exte .depends = depends, .promoted_to = promoted_to, .platform = platform, + .required_feature_level = requires_core, .requires = allocator.shrink(requires, i) }; } diff --git a/generator/render.zig b/generator/render.zig index 37537b7..9a193e6 100644 --- a/generator/render.zig +++ b/generator/render.zig @@ -281,10 +281,14 @@ fn Renderer(comptime WriterType: type) type { peeked = null; switch (tok.id) { - .lparen, .rparen, .tilde, .minus, .id => { + .lparen, .rparen, .tilde, .minus => { try self.writer.writeAll(tok.text); continue; }, + .id => { + try self.writeIdentifier(util.trimVkNamespace(api_constant.value.alias)); + continue; + }, .int => {}, else => return error.InvalidApiConstant, }