diff --git a/b64_dump.py b/b64_dump.py deleted file mode 100755 index 720bac9..0000000 --- a/b64_dump.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/python - -import sys -import base64 -import struct - -def format_base64_to_u32(base64_string): - # Decode the Base64 string - decoded_bytes = base64.b64decode(base64_string) - - # Interpret the bytes as a sequence of u32 values - u32_values = struct.unpack('I' * (len(decoded_bytes) // 4), decoded_bytes) - - # Format the u32 values as C-style hex values - formatted_u32_values = ', '.join(f'0x{value:08x}' for value in u32_values) - - # Split the formatted string into lines of 8 values each - lines = [', '.join(formatted_u32_values.split(', ')[i:i+8]) for i in range(0, len(u32_values), 8)] - - # Print the formatted values - print(',\n'.join(lines)) - -if __name__ == "__main__": - if len(sys.argv) != 2: - print(f"Usage: python {sys.argv[0]} ") - sys.exit(1) - - base64_string = sys.argv[1] - format_base64_to_u32(base64_string) diff --git a/build.zig b/build.zig index e7da8c8..8393b7e 100644 --- a/build.zig +++ b/build.zig @@ -2,6 +2,17 @@ const builtin = @import("builtin"); const std = @import("std"); +const SubPath = struct { + buffer: [max]u8 = [_]u8{0} ** max, + unused: u8 = max, + + const max = 255; + + pub fn utf8(self: *const SubPath) [:0]const u8 { + return self.buffer[0 .. (max - self.unused):0]; + } +}; + pub fn build(b: *std.Build) !void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); @@ -48,16 +59,52 @@ pub fn build(b: *std.Build) !void { }); ona_module.addIncludePath(b.path("ext/")); - ona_module.linkLibrary(build_spirvcross(b, target, optimize)); - b.step("test", "Run unit tests").dependOn(create: { + ona_module.linkLibrary(spirv_cross: { + const dir = "ext/spirv-cross/"; + + const sources = [_][]const u8{ + "spirv_cross.cpp", + "spirv_parser.cpp", + "spirv_cross_parsed_ir.cpp", + "spirv_cfg.cpp", + "spirv_glsl.cpp", + "spirv_msl.cpp", + "spirv_hlsl.cpp", + "spirv_reflect.cpp", + "spirv_cross_util.cpp", + "spirv_cross_c.cpp", + }; + + const lib = b.addStaticLibrary(.{ + .name = "spirvcross", + .target = target, + .optimize = optimize, + }); + + switch (lib.rootModuleTarget().abi) { + .msvc => lib.linkLibC(), + else => lib.linkLibCpp(), + } + + inline for (sources) |src| { + lib.addCSourceFile(.{ + .file = b.path(dir ++ src), + .flags = &.{"-fstrict-aliasing", "-DSPIRV_CROSS_C_API_GLSL", "-DSPIRV_CROSS_C_API_HLSL", "-DSPIRV_CROSS_C_API_MSL"}, + }); + } + + break: spirv_cross lib; + }); + + b.step("test", "Run unit tests").dependOn(tests: { const tests = b.addTest(.{ .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, }); - break: create &tests.step; + break: tests &tests.step; }); b.installArtifact(add: { @@ -73,47 +120,115 @@ pub fn build(b: *std.Build) !void { exe.linkLibC(); exe.linkSystemLibrary("SDL2"); + const shaders_sub_path = "src/ona/gfx/shaders/"; + + var shaders_dir = try std.fs.cwd().openDir(shaders_sub_path, .{ + .iterate = true, + }); + + defer shaders_dir.close(); + + var shaders_walker = try shaders_dir.walk(b.allocator); + + defer shaders_walker.deinit(); + + const Shader = struct { + source_sub_path: SubPath = .{}, + binary_sub_path: SubPath = .{}, + }; + + var pending_shaders = std.ArrayList(Shader).init(b.allocator); + + defer pending_shaders.deinit(); + + scan_shaders: while (try shaders_walker.next()) |entry| { + if (entry.kind != .file) { + continue: scan_shaders; + } + + const is_shader_file = std.mem.endsWith(u8, entry.path, ".frag") or std.mem.endsWith(u8, entry.path, ".vert"); + + if (!is_shader_file) { + continue: scan_shaders; + } + + const shader_name = std.fs.path.stem(entry.path); + + for (pending_shaders.items) |pending_shader| { + if (std.mem.endsWith(u8, pending_shader.source_sub_path.utf8(), shader_name)) { + continue: scan_shaders; + } + } + + var shader = Shader{}; + const source_sub_path = try std.fmt.bufPrint(&shader.source_sub_path.buffer, "{s}{s}", .{shaders_sub_path, shader_name}); + const binary_sub_path = try std.fmt.bufPrint(&shader.binary_sub_path.buffer, "{s}.spv", .{source_sub_path}); + + shaders_dir.access(std.fs.path.basename(binary_sub_path), .{.mode = .read_only}) catch { + shader.source_sub_path.unused -= @intCast(source_sub_path.len); + shader.binary_sub_path.unused -= @intCast(binary_sub_path.len); + + try pending_shaders.append(shader); + + continue: scan_shaders; + }; + + if ((try shaders_dir.statFile(entry.basename)).mtime > (try shaders_dir.statFile(std.fs.path.basename(binary_sub_path))).mtime) { + shader.source_sub_path.unused -= @intCast(source_sub_path.len); + shader.binary_sub_path.unused -= @intCast(binary_sub_path.len); + + try pending_shaders.append(shader); + + continue: scan_shaders; + } + } + + for (pending_shaders.items) |pending_shader| { + var vertex_binary_sub_path = SubPath{}; + var fragment_binary_sub_path = SubPath{}; + const source_sub_path_utf8 = pending_shader.source_sub_path.utf8(); + + vertex_binary_sub_path.unused -= @intCast((try std.fmt.bufPrint(&vertex_binary_sub_path.buffer, "{s}.vert.spv", .{source_sub_path_utf8})).len); + fragment_binary_sub_path.unused -= @intCast((try std.fmt.bufPrint(&fragment_binary_sub_path.buffer, "{s}.frag.spv", .{source_sub_path_utf8})).len); + + const vertex_binary_sub_path_utf8 = vertex_binary_sub_path.utf8(); + const fragment_binary_sub_path_utf8 = fragment_binary_sub_path.utf8(); + + const link_command = b.addSystemCommand(&.{ + "spirv-link", + vertex_binary_sub_path_utf8, + fragment_binary_sub_path_utf8, + "-o", + pending_shader.binary_sub_path.utf8(), + }); + + exe.step.dependOn(&link_command.step); + + link_command.step.dependOn(compile_vertex: { + const compile_command = b.addSystemCommand(&.{ + "glslangValidator", + "-V", + vertex_binary_sub_path_utf8[0 .. vertex_binary_sub_path_utf8.len - 4], + "-o", + vertex_binary_sub_path_utf8, + }); + + break: compile_vertex &compile_command.step; + }); + + link_command.step.dependOn(compile_fragment: { + const compile_command = b.addSystemCommand(&.{ + "glslangValidator", + "-V", + fragment_binary_sub_path_utf8[0 .. fragment_binary_sub_path_utf8.len - 4], + "-o", + fragment_binary_sub_path_utf8, + }); + + break: compile_fragment &compile_command.step; + }); + } + break: add exe; }); } - -fn build_spirvcross( - b: *std.Build, - target: std.Build.ResolvedTarget, - mode: std.builtin.OptimizeMode, -) *std.Build.Step.Compile { - const dir = "ext/spirv-cross/"; - - const sources = [_][]const u8{ - "spirv_cross.cpp", - "spirv_parser.cpp", - "spirv_cross_parsed_ir.cpp", - "spirv_cfg.cpp", - "spirv_glsl.cpp", - "spirv_msl.cpp", - "spirv_hlsl.cpp", - "spirv_reflect.cpp", - "spirv_cross_util.cpp", - "spirv_cross_c.cpp", - }; - - const lib = b.addStaticLibrary(.{ - .name = "spirvcross", - .target = target, - .optimize = mode, - }); - - switch (lib.rootModuleTarget().abi) { - .msvc => lib.linkLibC(), - else => lib.linkLibCpp(), - } - - inline for (sources) |src| { - lib.addCSourceFile(.{ - .file = b.path(dir ++ src), - .flags = &.{"-fstrict-aliasing", "-DSPIRV_CROSS_C_API_GLSL", "-DSPIRV_CROSS_C_API_HLSL", "-DSPIRV_CROSS_C_API_MSL"}, - }); - } - - return lib; -} diff --git a/readme.md b/readme.md index 7e8848a..e8d558e 100644 --- a/readme.md +++ b/readme.md @@ -33,6 +33,7 @@ Ona is also the Catalan word for "wave". Ona currently depends the following third-party tools to build it: * Platform support for SDL2 at version 2.0.20 or above. + * SPIR-V shader compilation system utilities, namely `glslangValidator` and `spirv-link`. * Zig compiler toolchain. As the project evolves, dependencies on libraries external to the project codebase will be minimized or removed outright to meet the goals of the project as closely as possible.