727 lines
25 KiB
Zig
727 lines
25 KiB
Zig
const std = @import("std");
|
|
|
|
fn printLog(comptime fmt: []const u8, args: anytype) void {
|
|
_ = fmt;
|
|
_ = args;
|
|
// std.debug.print(fmt ++ "\n", args);
|
|
}
|
|
|
|
// so bad but its fun
|
|
var workingFileName: []const u8 = "Unknown File";
|
|
var first: bool = true;
|
|
|
|
fn printErr(comptime fmt: []const u8, args: anytype) void {
|
|
if (first) {
|
|
std.debug.print("Error in {s}:\n", .{workingFileName});
|
|
first = false;
|
|
}
|
|
std.debug.print(fmt ++ "\n", args);
|
|
}
|
|
|
|
const FieldEntry = struct {
|
|
name: []const u8,
|
|
typeName: []const u8,
|
|
offset: u32 = 0,
|
|
typeIndex: ?usize = null, // this is only set if it's a reference to a type in the typemap
|
|
};
|
|
|
|
const TypeEntry = struct {
|
|
name: []u8,
|
|
size: u32 = 0,
|
|
subType: ?usize = null, // if set, this is a list or a uniform, and the underlying type is the real type that we want to generate code for.
|
|
stride: u32 = 0,
|
|
alignment: u32 = 0,
|
|
fields: std.ArrayListUnmanaged(FieldEntry) = .{},
|
|
fieldsArena: std.heap.ArenaAllocator,
|
|
|
|
pub fn init(allocator: std.mem.Allocator, name: []const u8) !@This() {
|
|
var arena = std.heap.ArenaAllocator.init(allocator);
|
|
|
|
const new: @This() = .{
|
|
.name = try arena.allocator().dupe(u8, name),
|
|
.fieldsArena = arena,
|
|
};
|
|
|
|
return new;
|
|
}
|
|
|
|
pub fn fieldsAllocator(self: *@This()) std.mem.Allocator {
|
|
return self.fieldsArena.allocator();
|
|
}
|
|
|
|
pub fn setName(self: *@This(), name: []const u8) !void {
|
|
self.fieldsAllocator().free(self.name);
|
|
self.name = try self.fieldsAllocator().dupe(u8, name);
|
|
}
|
|
|
|
pub fn pushField(self: *@This(), field: FieldEntry) !void {
|
|
try self.fields.append(self.fieldsAllocator(), field);
|
|
}
|
|
|
|
pub fn deinit(self: *@This()) void {
|
|
self.fields.deinit(self.fieldsAllocator());
|
|
self.fieldsArena.deinit();
|
|
}
|
|
};
|
|
|
|
var stringsArena: std.heap.ArenaAllocator = undefined;
|
|
|
|
const BuiltinType = struct {
|
|
name: []const u8,
|
|
size: u32 = 0,
|
|
alignment: u32 = 0,
|
|
};
|
|
|
|
const builtinTypes: []const BuiltinType = &.{
|
|
.{ .name = "int", .size = 4, .alignment = 4 },
|
|
.{ .name = "uint", .size = 4, .alignment = 4 },
|
|
.{ .name = "float", .size = 4, .alignment = 4 },
|
|
.{ .name = "vec2", .size = 8, .alignment = 8 },
|
|
.{ .name = "vec3", .size = 12, .alignment = 16 }, // vec3 has complex alignment rules
|
|
.{ .name = "vec4", .size = 16, .alignment = 16 },
|
|
.{ .name = "u8vec4", .size = 4, .alignment = 1 },
|
|
.{ .name = "mat4", .size = 64, .alignment = 16 }, // mat4 alignment depends on layout
|
|
};
|
|
|
|
const TypeMap = struct {
|
|
types: std.StringHashMapUnmanaged(usize) = .{},
|
|
typeList: std.ArrayListUnmanaged(TypeEntry) = .{},
|
|
allocator: std.mem.Allocator,
|
|
|
|
pub fn init(allocator: std.mem.Allocator) @This() {
|
|
return .{
|
|
.allocator = allocator,
|
|
};
|
|
}
|
|
|
|
pub fn pushType(self: *@This(), typeName: []const u8, entry: TypeEntry) !void {
|
|
try self.types.put(self.allocator, try stringsArena.allocator().dupe(u8, typeName), self.typeList.items.len);
|
|
try self.typeList.append(self.allocator, entry);
|
|
}
|
|
|
|
fn isBuiltinType(tag: []const u8) bool {
|
|
const builtinTypeTags: []const []const u8 = &.{
|
|
"vec2",
|
|
"int",
|
|
"uint",
|
|
"u8vec4",
|
|
"vec3",
|
|
"vec4",
|
|
"mat4",
|
|
"float",
|
|
};
|
|
|
|
for (builtinTypeTags) |cmp| {
|
|
if (std.mem.eql(u8, cmp, tag)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
pub fn parseTypes(self: *@This(), parsed: std.json.Parsed(std.json.Value)) !void {
|
|
const root = parsed.value.object;
|
|
const typesObj = root.get("types") orelse return;
|
|
|
|
var typesIterator = typesObj.object.iterator();
|
|
while (typesIterator.next()) |typeEntry| {
|
|
// const typeId = typeEntry.key_ptr.*;
|
|
const typeData = typeEntry.value_ptr.*.object;
|
|
|
|
const typeName = typeData.get("name").?.string;
|
|
var entry = try TypeEntry.init(self.allocator, typeName);
|
|
|
|
// Parse members if they exist
|
|
if (typeData.get("members")) |membersValue| {
|
|
const members = membersValue.array;
|
|
for (members.items) |member| {
|
|
const memberObj = member.object;
|
|
const fieldName = memberObj.get("name").?.string;
|
|
const fieldTypeTag = memberObj.get("type").?.string;
|
|
const fieldOffset = @as(u32, @intCast(memberObj.get("offset").?.integer));
|
|
|
|
var field: FieldEntry = .{
|
|
.name = fieldName,
|
|
.typeName = fieldTypeTag,
|
|
.offset = fieldOffset,
|
|
};
|
|
|
|
if (!isBuiltinType(fieldTypeTag)) {
|
|
if (typesObj.object.get(fieldTypeTag)) |*typeRefObj| {
|
|
if (typeRefObj.object.get("name")) |nameObj| {
|
|
const fieldType = nameObj.string;
|
|
|
|
field = FieldEntry{
|
|
.name = try entry.fieldsAllocator().dupe(u8, fieldName),
|
|
.typeName = try entry.fieldsAllocator().dupe(u8, fieldType),
|
|
};
|
|
} else {
|
|
printErr("No field 'name' in type '{s}'", .{fieldTypeTag});
|
|
}
|
|
} else {
|
|
printErr("unable to find type object '{s}' in json", .{fieldTypeTag});
|
|
}
|
|
}
|
|
|
|
try entry.pushField(field);
|
|
}
|
|
}
|
|
|
|
try self.pushType(typeName, entry);
|
|
}
|
|
|
|
// elaborate type indices
|
|
for (self.typeList.items) |*t| {
|
|
for (t.fields.items) |*f| {
|
|
if (self.types.get(f.typeName)) |typeIndex| {
|
|
f.typeIndex = typeIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
// resolve structured buffer subtypes
|
|
for (self.typeList.items) |*t| {
|
|
const structuredBufferStr = "type.StructuredBuffer.";
|
|
if (std.mem.startsWith(u8, t.name, structuredBufferStr)) {
|
|
if (t.fields.items.len == 0) {
|
|
printErr("structuredBuffer type {s} has no subfields? that's odd..", .{t.name});
|
|
continue;
|
|
}
|
|
const subName = t.fields.items[0].typeName;
|
|
|
|
t.subType = self.types.get(subName);
|
|
if (t.subType == null) {
|
|
printErr("structuredBuffer type {s} refers to child type {s} which is not found in the json", .{ t.name, subName });
|
|
}
|
|
}
|
|
}
|
|
|
|
// update field sizes
|
|
for (self.typeList.items) |*t| {
|
|
var lastOffset: u32 = 0;
|
|
for (t.fields.items) |field| {
|
|
std.debug.assert(lastOffset <= field.offset);
|
|
t.size = field.offset + self.getFieldSize(field);
|
|
lastOffset = field.offset;
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn getFieldSize(self: *const @This(), field: FieldEntry) u32 {
|
|
// First check if it's a builtin type
|
|
for (builtinTypes) |builtinType| {
|
|
if (std.mem.eql(u8, builtinType.name, field.typeName)) {
|
|
return builtinType.size;
|
|
}
|
|
}
|
|
|
|
// If it's a reference to another type in the typemap
|
|
if (field.typeIndex) |typeIndex| {
|
|
if (typeIndex < self.typeList.items.len) {
|
|
return self.typeList.items[typeIndex].size;
|
|
}
|
|
}
|
|
|
|
// Unknown type, return 0
|
|
printErr("Unknown field type '{s}', returning size 0", .{field.typeName});
|
|
return 0;
|
|
}
|
|
|
|
pub fn printAll(self: *@This()) void {
|
|
printLog("TypeMap contains {} types:", .{self.typeList.items.len});
|
|
|
|
var typesIterator = self.types.iterator();
|
|
while (typesIterator.next()) |entry| {
|
|
const typeId = entry.key_ptr.*;
|
|
const typeIndex = entry.value_ptr.*;
|
|
const typeEntry = &self.typeList.items[typeIndex];
|
|
|
|
printLog(" Type ID: '{s}' -> Name: '{s}' index: {d}", .{ typeId, typeEntry.name, typeIndex });
|
|
printLog(" Size: {}, Stride: {}, Alignment: {}", .{ typeEntry.size, typeEntry.stride, typeEntry.alignment });
|
|
|
|
if (typeEntry.fields.items.len > 0) {
|
|
printLog(" Fields ({}):", .{typeEntry.fields.items.len});
|
|
for (typeEntry.fields.items) |field| {
|
|
printLog(" - {s}: {s}", .{ field.name, field.typeName });
|
|
}
|
|
} else {
|
|
printLog(" No fields", .{});
|
|
}
|
|
printLog("", .{});
|
|
}
|
|
}
|
|
|
|
pub fn deinit(self: *@This()) void {
|
|
for (self.typeList.items) |*t| {
|
|
t.deinit();
|
|
}
|
|
|
|
self.typeList.deinit(self.allocator);
|
|
self.types.deinit(self.allocator);
|
|
}
|
|
};
|
|
|
|
const UboEntry = struct {
|
|
name: []u8,
|
|
typeIndex: usize,
|
|
set: u32,
|
|
binding: u32,
|
|
block_size: u32,
|
|
};
|
|
|
|
const UboList = struct {
|
|
list: std.ArrayListUnmanaged(UboEntry) = .{},
|
|
allocator: std.mem.Allocator,
|
|
|
|
pub fn init(allocator: std.mem.Allocator) @This() {
|
|
return .{
|
|
.allocator = allocator,
|
|
};
|
|
}
|
|
|
|
pub fn parseFromJson(self: *@This(), parsed: std.json.Parsed(std.json.Value), map: *const TypeMap) !void {
|
|
const root = parsed.value.object;
|
|
const list = root.get("ubos") orelse {
|
|
return;
|
|
};
|
|
|
|
if (list != .array) {
|
|
printErr("invalid type for list 'ubos' in root, skipping", .{});
|
|
return;
|
|
}
|
|
|
|
for (list.array.items) |i| {
|
|
if (i != .object) {
|
|
printErr("invalid type in ubos json list, expected an object", .{});
|
|
continue;
|
|
}
|
|
|
|
const typeField = root.get("types").?.object.get(i.object.get("type").?.string).?.object.get("name").?.string;
|
|
const nameField = i.object.get("name").?.string;
|
|
const blockSizeField = @as(u32, @intCast(i.object.get("block_size").?.integer));
|
|
const setField = @as(u32, @intCast(i.object.get("set").?.integer));
|
|
const bindingField = @as(u32, @intCast(i.object.get("binding").?.integer));
|
|
|
|
var entry = UboEntry{
|
|
.name = try stringsArena.allocator().dupe(u8, nameField),
|
|
.typeIndex = undefined,
|
|
.block_size = blockSizeField,
|
|
.set = setField,
|
|
.binding = bindingField,
|
|
};
|
|
|
|
if (map.types.get(typeField)) |typeIndex| {
|
|
entry.typeIndex = typeIndex;
|
|
} else {
|
|
printErr("Type for UBO '{s}' of type '{s}' not found in provided typemap", .{ nameField, typeField });
|
|
return error.BadUniformBuffer;
|
|
}
|
|
|
|
try self.list.append(self.allocator, entry);
|
|
}
|
|
}
|
|
|
|
pub fn printAll(self: *@This(), typeMap: *const TypeMap) void {
|
|
printLog("UboList contains {} UBOs:", .{self.list.items.len});
|
|
|
|
for (self.list.items, 0..) |ubo, i| {
|
|
printLog(" UBO {}: '{s}'", .{ i, ubo.name });
|
|
printLog(" Type Index: {}", .{ubo.typeIndex});
|
|
if (ubo.typeIndex < typeMap.typeList.items.len) {
|
|
const typeEntry = &typeMap.typeList.items[ubo.typeIndex];
|
|
printLog(" Type Name: '{s}'", .{typeEntry.name});
|
|
}
|
|
printLog(" Set: {}, Binding: {}", .{ ubo.set, ubo.binding });
|
|
printLog(" Block Size: {}", .{ubo.block_size});
|
|
printLog("", .{});
|
|
}
|
|
}
|
|
|
|
pub fn deinit(self: *@This()) void {
|
|
self.list.deinit(self.allocator);
|
|
}
|
|
};
|
|
|
|
const SsboEntry = struct {
|
|
name: []u8,
|
|
typeIndex: usize,
|
|
readOnly: bool,
|
|
set: u32,
|
|
binding: u32,
|
|
block_size: u32,
|
|
};
|
|
|
|
const SsboList = struct {
|
|
list: std.ArrayListUnmanaged(SsboEntry) = .{},
|
|
allocator: std.mem.Allocator,
|
|
|
|
pub fn init(allocator: std.mem.Allocator) @This() {
|
|
return .{
|
|
.allocator = allocator,
|
|
};
|
|
}
|
|
|
|
pub fn parseFromJson(self: *@This(), parsed: std.json.Parsed(std.json.Value), map: *const TypeMap) !void {
|
|
const root = parsed.value.object;
|
|
const list = root.get("ssbos") orelse {
|
|
return;
|
|
};
|
|
|
|
if (list != .array) {
|
|
printErr("invalid type for list 'ssbos' in root, skipping", .{});
|
|
return;
|
|
}
|
|
|
|
for (list.array.items) |i| {
|
|
if (i != .object) {
|
|
printErr("invalid type in ssbos json list, expected an object", .{});
|
|
continue;
|
|
}
|
|
|
|
const typeField = root.get("types").?.object.get(i.object.get("type").?.string).?.object.get("name").?.string;
|
|
const nameField = i.object.get("name").?.string;
|
|
const readOnlyField = i.object.get("readonly").?.bool;
|
|
const blockSizeField = @as(u32, @intCast(i.object.get("block_size").?.integer));
|
|
const setField = @as(u32, @intCast(i.object.get("set").?.integer));
|
|
const bindingField = @as(u32, @intCast(i.object.get("binding").?.integer));
|
|
|
|
var entry = SsboEntry{
|
|
.name = try stringsArena.allocator().dupe(u8, nameField),
|
|
.typeIndex = undefined,
|
|
.readOnly = readOnlyField,
|
|
.block_size = blockSizeField,
|
|
.set = setField,
|
|
.binding = bindingField,
|
|
};
|
|
|
|
if (map.types.get(typeField)) |typeIndex| {
|
|
entry.typeIndex = typeIndex;
|
|
if (map.typeList.items[typeIndex].subType) |subType| {
|
|
entry.typeIndex = subType;
|
|
}
|
|
} else {
|
|
printErr("Type for SSBO '{s}' of type '{s}' not found in provided typemap", .{ nameField, typeField });
|
|
return error.BadStructuredBuffer;
|
|
}
|
|
|
|
try self.list.append(self.allocator, entry);
|
|
}
|
|
}
|
|
|
|
pub fn printAll(self: *@This(), typeMap: *const TypeMap) void {
|
|
printLog("SsboList contains {} SSBOs:", .{self.list.items.len});
|
|
|
|
for (self.list.items, 0..) |ssbo, i| {
|
|
printLog(" SSBO {}: '{s}'", .{ i, ssbo.name });
|
|
printLog(" Type Index: {}", .{ssbo.typeIndex});
|
|
if (ssbo.typeIndex < typeMap.typeList.items.len) {
|
|
const typeEntry = &typeMap.typeList.items[ssbo.typeIndex];
|
|
printLog(" Type Name: '{s}'", .{typeEntry.name});
|
|
}
|
|
printLog(" Read Only: {}", .{ssbo.readOnly});
|
|
printLog(" Set: {}, Binding: {}", .{ ssbo.set, ssbo.binding });
|
|
printLog(" Block Size: {}", .{ssbo.block_size});
|
|
printLog("", .{});
|
|
}
|
|
}
|
|
|
|
pub fn deinit(self: *@This()) void {
|
|
self.list.deinit(self.allocator);
|
|
}
|
|
};
|
|
|
|
const SamplersEntry = struct {
|
|
name: []const u8,
|
|
set: u32,
|
|
binding: u32,
|
|
};
|
|
|
|
const SamplersList = struct {
|
|
list: std.ArrayListUnmanaged(SamplersEntry) = .{},
|
|
allocator: std.mem.Allocator,
|
|
|
|
pub fn init(allocator: std.mem.Allocator) @This() {
|
|
return .{
|
|
.allocator = allocator,
|
|
};
|
|
}
|
|
|
|
pub fn parseSamplers(self: *@This(), parsed: std.json.Parsed(std.json.Value)) !void {
|
|
const root = parsed.value.object;
|
|
const samplersArray = root.get("separate_samplers") orelse return;
|
|
|
|
for (samplersArray.array.items) |samplerItem| {
|
|
const samplerObj = samplerItem.object;
|
|
|
|
const name = try stringsArena.allocator().dupe(u8, samplerObj.get("name").?.string);
|
|
const set = @as(u32, @intCast(samplerObj.get("set").?.integer));
|
|
const binding = @as(u32, @intCast(samplerObj.get("binding").?.integer));
|
|
|
|
const entry = SamplersEntry{
|
|
.name = name,
|
|
.set = set,
|
|
.binding = binding,
|
|
};
|
|
|
|
try self.list.append(self.allocator, entry);
|
|
}
|
|
}
|
|
|
|
pub fn printAll(self: *@This()) void {
|
|
printLog("SamplersList contains {} samplers:", .{self.list.items.len});
|
|
|
|
for (self.list.items, 0..) |sampler, i| {
|
|
printLog(" Sampler {}: '{s}'", .{ i, sampler.name });
|
|
printLog(" Set: {}, Binding: {}", .{ sampler.set, sampler.binding });
|
|
printLog("", .{});
|
|
}
|
|
}
|
|
|
|
pub fn deinit(self: *@This()) void {
|
|
self.list.deinit(self.allocator);
|
|
}
|
|
};
|
|
|
|
const OutputFile = struct {
|
|
output: std.ArrayList(u8),
|
|
typeMap: *const TypeMap,
|
|
ssbos: *const SsboList,
|
|
ubos: *const UboList,
|
|
samplers: *const SamplersList,
|
|
|
|
pub fn init(
|
|
allocator: std.mem.Allocator,
|
|
typeMap: *const TypeMap,
|
|
ssbos: *const SsboList,
|
|
ubos: *const UboList,
|
|
samplers: *const SamplersList,
|
|
) !@This() {
|
|
return .{
|
|
.output = std.ArrayList(u8).init(allocator),
|
|
.typeMap = typeMap,
|
|
.ssbos = ssbos,
|
|
.ubos = ubos,
|
|
.samplers = samplers,
|
|
};
|
|
}
|
|
|
|
pub fn finalize(self: *@This()) !void {
|
|
try self.output.append(0);
|
|
}
|
|
|
|
pub fn writeOut(self: *@This(), path: []const u8) !void {
|
|
const file = try std.fs.cwd().createFile(path, .{});
|
|
defer file.close();
|
|
|
|
var ast = try std.zig.Ast.parse(
|
|
self.output.allocator,
|
|
@as([:0]const u8, @ptrCast(self.output.items[0 .. self.output.items.len - 1])),
|
|
.zig,
|
|
);
|
|
// std.debug.print("output=\n{s}", .{content.items[0 .. content.items.len - 1]});
|
|
defer ast.deinit(self.output.allocator);
|
|
const out = try ast.render(self.output.allocator);
|
|
|
|
try file.writeAll(out);
|
|
}
|
|
|
|
pub fn generatePreamble(self: *@This()) !void {
|
|
var writer = self.output.writer();
|
|
try writer.print(
|
|
\\pub const shaderTypes = @import("shaderTypes");
|
|
\\
|
|
\\pub const int = shaderTypes.int;
|
|
\\pub const uint = shaderTypes.uint;
|
|
\\
|
|
\\pub const vec2 = shaderTypes.vec2;
|
|
\\pub const u8vec4 = shaderTypes.u8vec4;
|
|
\\pub const vec3 = shaderTypes.vec3;
|
|
\\pub const vec4 = shaderTypes.vec4;
|
|
\\pub const mat4 = shaderTypes.mat4;
|
|
\\pub const float = shaderTypes.float;
|
|
\\
|
|
\\pub const BufferInfo = shaderTypes.BufferInfo;
|
|
\\
|
|
, .{});
|
|
}
|
|
|
|
pub fn generateSsbos(self: *@This()) !void {
|
|
var writer = self.output.writer();
|
|
for (self.ssbos.list.items) |ssbo| {
|
|
const ssboType = self.typeMap.typeList.items[ssbo.typeIndex];
|
|
try writer.print("pub const {s} = extern struct {{\n", .{ssboType.name});
|
|
|
|
var lastFieldEnd: u32 = 0;
|
|
var paddingCount: u32 = 0;
|
|
for (ssboType.fields.items) |field| {
|
|
if (lastFieldEnd != field.offset) {
|
|
printErr("WARNING: Field {s} in ssbo {s} generated padding, expected offset {d} offset + struct size = {d}", .{ field.name, ssbo.name, field.offset, lastFieldEnd });
|
|
try writer.print(" pad{d}: [{d}]c_char = undefined,\n", .{ paddingCount, field.offset - lastFieldEnd });
|
|
paddingCount += 1;
|
|
}
|
|
try writer.print(" {s}: {s},\n", .{ field.name, field.typeName });
|
|
lastFieldEnd = field.offset + self.typeMap.getFieldSize(field);
|
|
}
|
|
|
|
// generate buffer info
|
|
|
|
try writer.print("\n pub const Buffer: BufferInfo = .{{ .storage = {d} }};\n", .{ssbo.binding});
|
|
|
|
try writer.print("\n pub const FieldDetails: []const shaderTypes.FieldDetail = &.{{ \n", .{});
|
|
for (ssboType.fields.items) |field| {
|
|
try writer.print(" " ** 8, .{});
|
|
try writer.print(".{{ .name = \"{s}\", .offset = {d}, .size = {d} }},\n", .{ field.name, field.offset, self.typeMap.getFieldSize(field) });
|
|
}
|
|
try writer.print(" }};\n", .{});
|
|
|
|
try writer.print("}};\n", .{});
|
|
}
|
|
|
|
try writer.print("\ncomptime {{\n", .{});
|
|
for (self.ssbos.list.items) |ssbo| {
|
|
const ssboType = self.typeMap.typeList.items[ssbo.typeIndex];
|
|
try writer.print(" shaderTypes.ValidateGeneratedStruct({s});\n", .{ssboType.name});
|
|
}
|
|
try writer.print("}}\n", .{});
|
|
}
|
|
|
|
pub fn generateUbos(self: *@This()) !void {
|
|
var writer = self.output.writer();
|
|
for (self.ubos.list.items) |ubo| {
|
|
const uboType = self.typeMap.typeList.items[ubo.typeIndex];
|
|
try writer.print("pub const Uniforms = extern struct {{\n", .{});
|
|
|
|
var lastFieldEnd: u32 = 0;
|
|
var paddingCount: u32 = 0;
|
|
for (uboType.fields.items) |field| {
|
|
if (lastFieldEnd != field.offset) {
|
|
printErr("WARNING: Field {s} in ubo {s} generated padding, expected offset {d} offset + struct size = {d}", .{ field.name, ubo.name, field.offset, lastFieldEnd });
|
|
try writer.print(" pad{d}: [{d}]c_char = undefined,\n", .{ paddingCount, field.offset - lastFieldEnd });
|
|
paddingCount += 1;
|
|
}
|
|
try writer.print(" {s}: {s},\n", .{ field.name, field.typeName });
|
|
lastFieldEnd = field.offset + self.typeMap.getFieldSize(field);
|
|
}
|
|
|
|
// generate buffer info using .uniform instead of .storage
|
|
try writer.print("\n pub const Buffer: BufferInfo = .{{ .uniform = {d} }};\n", .{ubo.binding});
|
|
|
|
try writer.print("\n pub const FieldDetails: []const shaderTypes.FieldDetail = &.{{ \n", .{});
|
|
for (uboType.fields.items) |field| {
|
|
try writer.print(" " ** 8, .{});
|
|
try writer.print(".{{ .name = \"{s}\", .offset = {d}, .size = {d} }},\n", .{ field.name, field.offset, self.typeMap.getFieldSize(field) });
|
|
}
|
|
try writer.print(" }};\n", .{});
|
|
|
|
try writer.print("}};\n", .{});
|
|
}
|
|
|
|
try writer.print("\ncomptime {{\n", .{});
|
|
for (self.ubos.list.items) |ubo| {
|
|
const uboType = self.typeMap.typeList.items[ubo.typeIndex];
|
|
_ = uboType;
|
|
try writer.print(" shaderTypes.ValidateGeneratedStruct({s});\n", .{"Uniforms"});
|
|
}
|
|
try writer.print("}}\n", .{});
|
|
}
|
|
|
|
pub fn generateLoadArguments(self: *@This()) !void {
|
|
var writer = self.output.writer();
|
|
|
|
try writer.print("pub const LoadArgs = shaderTypes.ShaderLoadArgs {{\n", .{});
|
|
|
|
try writer.print(
|
|
\\.num_samplers = {d},
|
|
\\.num_storage_textures = {d},
|
|
\\.num_storage_buffers = {d},
|
|
\\.num_uniform_buffers = {d},
|
|
, .{
|
|
self.samplers.list.items.len,
|
|
0, // storage textures to be added later
|
|
self.ssbos.list.items.len,
|
|
self.ubos.list.items.len,
|
|
});
|
|
try writer.print("}};\n", .{});
|
|
}
|
|
|
|
pub fn deinit(self: *@This()) void {
|
|
self.output.deinit();
|
|
}
|
|
};
|
|
|
|
pub fn main() !void {
|
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
defer _ = gpa.deinit();
|
|
|
|
var arena = std.heap.ArenaAllocator.init(gpa.allocator());
|
|
defer arena.deinit();
|
|
|
|
const allocator = arena.allocator();
|
|
stringsArena = std.heap.ArenaAllocator.init(gpa.allocator());
|
|
defer stringsArena.deinit();
|
|
|
|
const args = try std.process.argsAlloc(allocator);
|
|
defer std.process.argsFree(allocator, args);
|
|
|
|
if (args.len < 3) {
|
|
printLog("Usage: {s} <jsonFilePath> <outputPath>", .{args[0]});
|
|
return;
|
|
}
|
|
|
|
const jsonFilePath = args[1];
|
|
workingFileName = jsonFilePath;
|
|
|
|
const file = std.fs.cwd().openFile(jsonFilePath, .{}) catch |err| {
|
|
printErr("Error opening file '{s}': {}", .{ jsonFilePath, err });
|
|
return;
|
|
};
|
|
defer file.close();
|
|
|
|
const fileSize = try file.getEndPos();
|
|
const jsonContent = try allocator.alloc(u8, fileSize);
|
|
_ = try file.readAll(jsonContent);
|
|
|
|
var parsed = std.json.parseFromSlice(std.json.Value, allocator, jsonContent, .{}) catch |err| {
|
|
printErr("Error parsing JSON: {}", .{err});
|
|
return;
|
|
};
|
|
defer parsed.deinit();
|
|
|
|
var types: TypeMap = TypeMap.init(allocator);
|
|
try types.parseTypes(parsed);
|
|
defer types.deinit();
|
|
|
|
types.printAll();
|
|
|
|
// create ssbolist and parse out the ssbolist from the typeslist
|
|
var ssbos = SsboList.init(allocator);
|
|
try ssbos.parseFromJson(parsed, &types);
|
|
defer ssbos.deinit();
|
|
ssbos.printAll(&types);
|
|
|
|
// create ubolist and parse out the ubolist from the typeslist
|
|
var ubos = UboList.init(allocator);
|
|
try ubos.parseFromJson(parsed, &types);
|
|
defer ubos.deinit();
|
|
|
|
ubos.printAll(&types);
|
|
|
|
// parse out the samplers
|
|
var samplers = SamplersList.init(allocator);
|
|
try samplers.parseSamplers(parsed);
|
|
defer samplers.deinit();
|
|
|
|
var outputfile = try OutputFile.init(allocator, &types, &ssbos, &ubos, &samplers);
|
|
try outputfile.generatePreamble();
|
|
try outputfile.generateSsbos();
|
|
try outputfile.generateUbos();
|
|
try outputfile.generateLoadArguments();
|
|
// printLog("{s}", .{outputfile.output.items});
|
|
try outputfile.finalize();
|
|
try outputfile.writeOut(args[2]);
|
|
defer outputfile.deinit();
|
|
|
|
printLog("Successfully parsed JSON from: {s}", .{jsonFilePath});
|
|
}
|