created spvreflect2
This commit is contained in:
parent
e1a270bfcb
commit
393b550c74
|
|
@ -0,0 +1,25 @@
|
|||
const std = @import("std");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "spvReflect2",
|
||||
.root_source_file = b.path("spvReflect2.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
b.installArtifact(exe);
|
||||
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
run_cmd.step.dependOn(b.getInstallStep());
|
||||
|
||||
if (b.args) |args| {
|
||||
run_cmd.addArgs(args);
|
||||
}
|
||||
|
||||
const run_step = b.step("run", "Run the JSON parser");
|
||||
run_step.dependOn(&run_cmd.step);
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
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 const Scene = extern struct {
|
||||
imagePosition: vec2,
|
||||
imageSize: vec2,
|
||||
anchorPoint: vec2,
|
||||
scale: vec2,
|
||||
alpha: float,
|
||||
borderWidth: float,
|
||||
flags: uint,
|
||||
pad0: [4]c_char = undefined,
|
||||
baseColor: vec4,
|
||||
rounding: vec4,
|
||||
edgeColor: vec4,
|
||||
|
||||
pub const Buffer: BufferInfo = .{ .storage = 0 };
|
||||
|
||||
pub const FieldDetails: []shaderTypes.FieldDetail = &.{
|
||||
.{ .name = "imagePosition", .offset = 0, .size = 8 },
|
||||
.{ .name = "imageSize", .offset = 8, .size = 8 },
|
||||
.{ .name = "anchorPoint", .offset = 16, .size = 8 },
|
||||
.{ .name = "scale", .offset = 24, .size = 8 },
|
||||
.{ .name = "alpha", .offset = 32, .size = 4 },
|
||||
.{ .name = "borderWidth", .offset = 36, .size = 4 },
|
||||
.{ .name = "flags", .offset = 40, .size = 4 },
|
||||
.{ .name = "baseColor", .offset = 48, .size = 16 },
|
||||
.{ .name = "rounding", .offset = 64, .size = 16 },
|
||||
.{ .name = "edgeColor", .offset = 80, .size = 16 },
|
||||
};
|
||||
};
|
||||
|
||||
comptime {
|
||||
shaderTypes.ValidateGeneratedStruct(Scene);
|
||||
}
|
||||
pub const Uniforms = extern struct {
|
||||
Extents: vec2,
|
||||
|
||||
pub const Buffer: BufferInfo = .{ .uniform = 0 };
|
||||
|
||||
pub const FieldDetails: []shaderTypes.FieldDetail = &.{
|
||||
.{ .name = "Extents", .offset = 0, .size = 8 },
|
||||
};
|
||||
};
|
||||
|
||||
comptime {
|
||||
shaderTypes.ValidateGeneratedStruct(Uniforms);
|
||||
}
|
||||
pub const LoadArgs = shaderTypes.ShaderLoadArgs{
|
||||
.num_samplers = 0,
|
||||
.num_storage_textures = 0,
|
||||
.num_storage_buffers = 1,
|
||||
.num_uniform_buffers = 1,
|
||||
};
|
||||
|
|
@ -14,9 +14,18 @@ pub const BufferInfo = shaderTypes.BufferInfo;
|
|||
pub const Uniforms = struct {
|
||||
time: float,
|
||||
|
||||
pub const FieldDetails: []shaderTypes.FieldDetail = &.{
|
||||
.{ .name = "time", .size = 4, .offset = 0 },
|
||||
};
|
||||
|
||||
pub const Buffer: BufferInfo = .{ .uniform = 0 };
|
||||
};
|
||||
|
||||
// Compile-time validation check
|
||||
comptime {
|
||||
shaderTypes.ValidateGeneratedStruct(Uniforms);
|
||||
}
|
||||
|
||||
pub const LoadArgs = shaderTypes.ShaderLoadArgs{
|
||||
.num_samplers = 1, // The number of samplers defined in the shader.
|
||||
.num_storage_textures = 0, // The number of storage textures defined in the shader.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,719 @@
|
|||
const std = @import("std");
|
||||
|
||||
fn printLog(comptime fmt: []const u8, args: anytype) void {
|
||||
//_ = fmt;
|
||||
// _ = args;
|
||||
std.debug.print(fmt ++ "\n", args);
|
||||
}
|
||||
|
||||
fn printErr(comptime fmt: []const u8, args: anytype) void {
|
||||
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 {
|
||||
printErr("missing ubos entry", .{});
|
||||
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 {
|
||||
printErr("missing ssbos entry", .{});
|
||||
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: []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: []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];
|
||||
|
||||
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});
|
||||
}
|
||||
Loading…
Reference in New Issue