From e5cf30860977587188d810cc6524a5101312426c Mon Sep 17 00:00:00 2001 From: abux Date: Sun, 12 Jul 2026 19:46:40 +0100 Subject: [PATCH] HelloWorld! --- .gitignore | 2 + README.md | 35 ++++++++++ build.zig | 24 +++++++ build.zig.zon | 17 +++++ src/ansi.zig | 59 +++++++++++++++++ src/gtl.zig | 128 +++++++++++++++++++++++++++++++++++ src/handle.zig | 17 +++++ src/log.zig | 123 ++++++++++++++++++++++++++++++++++ src/slot_map.zig | 169 +++++++++++++++++++++++++++++++++++++++++++++++ src/type_id.zig | 8 +++ 10 files changed, 582 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 build.zig create mode 100644 build.zig.zon create mode 100644 src/ansi.zig create mode 100644 src/gtl.zig create mode 100644 src/handle.zig create mode 100644 src/log.zig create mode 100644 src/slot_map.zig create mode 100644 src/type_id.zig diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5858b8d --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.zig-cache/ +zig-pkg/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..1e27a7b --- /dev/null +++ b/README.md @@ -0,0 +1,35 @@ +**Includes** +- ANSI +- SlotMap +- Custom Log +- TypeID + +**Logs** +```zig +log.warn("...", .{}); +log.err("...", .{}); +log.info("...", .{}); +log.debug("...", .{}); +log.trace("...", .{}); +log.fatal("...", .{}); +``` + +**SlotMap** +```zig +// --- SHADER MAP --- +var shaders: SlotMap(ShaderID, GPUShader) = .init(alloc); +defer shaders.deinit(); + +// --- NEW SHADER --- +const shader_handle = try shaders.put(.pbr, .{ .raw = undefined }); +defer shaders.remove(shader_handle); +``` + +**TypeID** +```zig +const Player = struct {}; +std.debug.print("TypeID(Player): {}\n", .{TypeID(Player)}); +``` +```bash +Output: TypeID(Player): 2462284566368800629 +``` diff --git a/build.zig b/build.zig new file mode 100644 index 0000000..ec1e006 --- /dev/null +++ b/build.zig @@ -0,0 +1,24 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + // --- TARGET --- + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + // --- MOD --- + const mod = b.addModule("gtl", .{ + .root_source_file = b.path("src/gtl.zig"), + + .target = target, + .optimize = optimize, + }); + + const mod_tests = b.addTest(.{ + .root_module = mod, + }); + + const run_mod_tests = b.addRunArtifact(mod_tests); + + const test_step = b.step("test", "Run tests"); + test_step.dependOn(&run_mod_tests.step); +} diff --git a/build.zig.zon b/build.zig.zon new file mode 100644 index 0000000..5749246 --- /dev/null +++ b/build.zig.zon @@ -0,0 +1,17 @@ +.{ + .name = .gtl, + + .version = "0.0.0", + + .fingerprint = 0xbd8e95362428747b, + + .minimum_zig_version = "0.16.0", + + .dependencies = .{}, + + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + }, +} diff --git a/src/ansi.zig b/src/ansi.zig new file mode 100644 index 0000000..81e9231 --- /dev/null +++ b/src/ansi.zig @@ -0,0 +1,59 @@ +// --- RESET --- +pub const reset = "\x1b[0m"; + +// --- STYLES --- +pub const bold = "\x1b[1m"; +pub const dim = "\x1b[2m"; +pub const italic = "\x1b[3m"; +pub const underline = "\x1b[4m"; +pub const slow_blink = "\x1b[5m"; +pub const rapid_blink = "\x1b[6m"; +pub const invert = "\x1b[7m"; +pub const hidden = "\x1b[8m"; +pub const strikethrough = "\x1b[9m"; + +// --- STYLE RESETS --- +pub const bold_off = "\x1b[22m"; +pub const dim_off = "\x1b[22m"; +pub const italic_off = "\x1b[23m"; +pub const underline_off = "\x1b[24m"; +pub const blink_off = "\x1b[25m"; +pub const invert_off = "\x1b[27m"; +pub const hidden_off = "\x1b[28m"; +pub const strikethrough_off = "\x1b[29m"; + +// --- FOREGROUND --- +pub const black = "\x1b[30m"; +pub const red = "\x1b[31m"; +pub const green = "\x1b[32m"; +pub const yellow = "\x1b[33m"; +pub const blue = "\x1b[34m"; +pub const purple = "\x1b[35m"; +pub const cyan = "\x1b[36m"; +pub const white = "\x1b[37m"; +pub const bright_black = "\x1b[90m"; +pub const bright_red = "\x1b[91m"; +pub const bright_green = "\x1b[92m"; +pub const bright_yellow = "\x1b[93m"; +pub const bright_blue = "\x1b[94m"; +pub const bright_purple = "\x1b[95m"; +pub const bright_cyan = "\x1b[96m"; +pub const bright_white = "\x1b[97m"; + +// --- BACKGROUND --- +pub const bg_black = "\x1b[40m"; +pub const bg_red = "\x1b[41m"; +pub const bg_green = "\x1b[42m"; +pub const bg_yellow = "\x1b[43m"; +pub const bg_blue = "\x1b[44m"; +pub const bg_purple = "\x1b[45m"; +pub const bg_cyan = "\x1b[46m"; +pub const bg_white = "\x1b[47m"; +pub const bg_bright_black = "\x1b[100m"; +pub const bg_bright_red = "\x1b[101m"; +pub const bg_bright_green = "\x1b[102m"; +pub const bg_bright_yellow = "\x1b[103m"; +pub const bg_bright_blue = "\x1b[104m"; +pub const bg_bright_purple = "\x1b[105m"; +pub const bg_bright_cyan = "\x1b[106m"; +pub const bg_bright_white = "\x1b[107m"; diff --git a/src/gtl.zig b/src/gtl.zig new file mode 100644 index 0000000..f81a081 --- /dev/null +++ b/src/gtl.zig @@ -0,0 +1,128 @@ +// +// PRIVATE +// + +const std = @import("std"); + +// +// PUBLIC +// + +pub const log = @import("log.zig"); +pub const ansi = @import("ansi.zig"); +pub const Handle = @import("handle.zig").Handle; +pub const SlotMap = @import("slot_map.zig").SlotMap; +pub const TypeID = @import("type_id.zig").of; + +// +// STRUCTS +// + +// --- SHADER --- +const ShaderID = union(enum) { pbr, unlit, custom: Handle([]const u8) }; +const GPUShader = struct { raw: *anyopaque }; + +// --- PIPELINE --- +const PipelineKey = struct { shader: ShaderID }; +const GPUPipeline = struct { raw: *anyopaque }; + +// --- MATERIAL --- +const Material = struct { + shader: ShaderID = .pbr, + albedo: @Vector(4, f32) = .{ 0, 0, 0, 1 }, +}; + +test "SlotMap" { + // --- ALLOCATOR --- + const alloc = std.testing.allocator; + + // + // LISTS + // + + // --- MATERIALS --- + var materials: std.ArrayList(Material) = .empty; + defer materials.deinit(alloc); + + // + // SLOT MAPS + // + + // --- SHADER MAP --- + var shaders: SlotMap(ShaderID, GPUShader) = .init(alloc); + defer shaders.deinit(); + + // --- PIPELINE MAP --- + var pipelines: SlotMap(PipelineKey, GPUPipeline) = .init(alloc); + defer pipelines.deinit(); + + // --- ADD MATERIALS --- + try materials.append(alloc, .{ .shader = .pbr }); // BLACK + try materials.append(alloc, .{ .shader = .pbr, .albedo = .{ 1, 0, 0, 1 } }); // RED + try materials.append(alloc, .{ .shader = .unlit, .albedo = .{ 0, 1, 0, 1 } }); // GREEN + try materials.append(alloc, .{ .shader = .unlit, .albedo = .{ 0, 0, 1, 1 } }); // GREEN + + { + // --- DEBUG --- + std.debug.print("┏━ MATERIALS\n", .{}); + defer std.debug.print("┗━\n", .{}); + + for (materials.items) |material| { + // --- REGISTER SHADER --- + const shader = shaders.getByKey(material.shader) orelse blk: { + const handle = try shaders.put(material.shader, .{ .raw = undefined }); + break :blk shaders.get(handle).?; + }; + _ = shader; + + // --- DEBUG --- + std.debug.print("┃ ┏━\n", .{}); + std.debug.print("┃ ┃ Shader: {s}\n", .{@tagName(material.shader)}); + std.debug.print("┃ ┃ Albedo: rgba({}, {}, {}, {})\n", .{ material.albedo[0], material.albedo[1], material.albedo[2], material.albedo[3] }); + std.debug.print("┃ ┗━\n", .{}); + } + } + + { + // --- DEBUG --- + std.debug.print("┏━ SHADERS\n", .{}); + defer std.debug.print("┗━\n", .{}); + + for (shaders.values.items) |entry| { + const source = switch (entry.key) { + .pbr => "assets/shaders/pbr.wgsl", + .unlit => "assets/shaders/unlit.wgsl", + .custom => "idk", + }; + + std.debug.print("┃ ┏━\n", .{}); + std.debug.print("┃ ┃ ID: {s}\n", .{@tagName(entry.key)}); + std.debug.print("┃ ┃ Source: {s}\n", .{source}); + std.debug.print("┃ ┗━\n", .{}); + } + } + + { + std.debug.print("┏━ ANSI\n", .{}); + defer std.debug.print("┗━\n", .{}); + std.debug.print( + \\{s}┃ RED + \\{s}┃ GREEN + \\{s}┃ BLUE + \\{s} + , .{ + ansi.red, + ansi.green, + ansi.blue, + ansi.reset, + }); + } + + { + log.warn("...", .{}); + log.err("...", .{}); + log.info("...", .{}); + log.debug("...", .{}); + log.trace("...", .{}); + } +} diff --git a/src/handle.zig b/src/handle.zig new file mode 100644 index 0000000..9acdae5 --- /dev/null +++ b/src/handle.zig @@ -0,0 +1,17 @@ +pub fn Handle(comptime T: type) type { + _ = T; + return struct { + const Self = @This(); + + idx: u32 = 0, + gen: u32 = 0, + + pub fn eql( + self: Self, + other: Self, + ) bool { + return self.idx == other.idx and + self.gen == other.gen; + } + }; +} diff --git a/src/log.zig b/src/log.zig new file mode 100644 index 0000000..1ba3d68 --- /dev/null +++ b/src/log.zig @@ -0,0 +1,123 @@ +const std = @import("std"); +const ansi = @import("ansi.zig"); + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub const Level = enum(u8) { + debug = 0, + trace = 1, + info = 2, + warn = 3, + err = 4, + fatal = 5, + + fn label(self: Level) []const u8 { + return switch (self) { + .debug => "DEBUG", + .trace => "TRACE", + .info => "INFO", + .warn => "WARN", + .err => "ERROR", + .fatal => "FATAL", + }; + } + + fn color(self: Level) []const u8 { + return switch (self) { + .debug => ansi.bright_black, + .trace => ansi.cyan, + .info => ansi.green, + .warn => ansi.yellow, + .err => ansi.red, + .fatal => ansi.bright_red ++ ansi.bold, + }; + } +}; + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub const Config = struct { + level: Level = .debug, + source_loc: bool = true, +}; + +// --- CONFIG --- +var config: Config = .{}; + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn set(c: Config) void { + config = c; +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn debug(comptime fmt: []const u8, args: anytype) void { + log(.debug, @src(), fmt, args); +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn trace(comptime fmt: []const u8, args: anytype) void { + log(.trace, @src(), fmt, args); +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn info(comptime fmt: []const u8, args: anytype) void { + log(.info, @src(), fmt, args); +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn warn(comptime fmt: []const u8, args: anytype) void { + log(.warn, @src(), fmt, args); +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn err(comptime fmt: []const u8, args: anytype) void { + log(.err, @src(), fmt, args); +} + +/// ---------------------------------------------------- +/// Uses **@breakpoint()** +/// ---------------------------------------------------- +pub fn fatal(comptime fmt: []const u8, args: anytype) void { + log(.fatal, @src(), fmt, args); + if (@import("builtin").mode == .Debug) @breakpoint(); +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +fn log( + level: Level, + src: std.builtin.SourceLocation, + comptime fmt: []const u8, + args: anytype, +) void { + // --- CHECK LOG LVL --- + if (@intFromEnum(level) < @intFromEnum(config.level)) return; + + // --- BUFFER --- + var buf: [64]u8 = undefined; + const stderr = std.debug.lockStderr(&buf); + defer std.debug.unlockStderr(); + + // --- WRITE --- + const w = &stderr.file_writer.interface; + w.writeAll(level.color()) catch return; + w.print("[" ++ ansi.bold ++ "{s}" ++ ansi.reset, .{level.label()}) catch return; + w.writeAll(level.color()) catch return; + w.writeAll("]" ++ ansi.reset ++ " ") catch return; + + // --- CHECK LOCATION --- + if (config.source_loc) { + w.writeAll(ansi.bright_black) catch return; + w.print("({s}:{d}) ", .{ std.fs.path.basename(src.file), src.line }) catch return; + w.writeAll(ansi.reset) catch return; + } + + // --- OUTPUT --- + w.print(fmt ++ "\n", args) catch return; +} diff --git a/src/slot_map.zig b/src/slot_map.zig new file mode 100644 index 0000000..ac24adf --- /dev/null +++ b/src/slot_map.zig @@ -0,0 +1,169 @@ +const std = @import("std"); +const Handle = @import("handle.zig").Handle; + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn SlotMap( + comptime K: type, + comptime V: type, +) type { + return struct { + const Self = @This(); + + /// ---------------------------------------------------- + /// ---------------------------------------------------- + pub const Entry = struct { + key: K, + value: V, + }; + + // + // FIELDS + // + + // --- VALUES --- + values: std.ArrayList(Entry), + lookup: std.AutoHashMap(K, Handle(V)), + + // --- LIFE TIMES --- + generations: std.ArrayList(u32), + free_list: std.ArrayList(u32), + + alloc: std.mem.Allocator, + + /// ---------------------------------------------------- + /// ---------------------------------------------------- + pub fn init(alloc: std.mem.Allocator) Self { + return .{ + .values = .empty, + .lookup = .init(alloc), + + .generations = .empty, + .free_list = .empty, + + .alloc = alloc, + }; + } + + /// ---------------------------------------------------- + /// ---------------------------------------------------- + pub fn deinit(self: *Self) void { + self.values.deinit(self.alloc); + self.lookup.deinit(); + self.generations.deinit(self.alloc); + self.free_list.deinit(self.alloc); + } + + /// ---------------------------------------------------- + /// ---------------------------------------------------- + pub fn put( + self: *Self, + key: K, + value: V, + ) error{ Duplicate, OutOfMemory }!Handle(V) { + // --- PREVENT DUPS --- + if (self.lookup.contains(key)) return error.Duplicate; + + // --- NEW GENERATION --- + if (self.free_list.pop()) |idx| { + self.values.items[@intCast(idx)] = .{ + .key = key, + .value = value, + }; + self.generations.items[@intCast(idx)] += 1; + + return .{ + .idx = idx, + .gen = self.generations.items[idx], + }; + } + + // --- METADATA --- + const idx = self.values.items.len; + const handle: Handle(V) = .{ + .idx = @intCast(idx), + .gen = 1, + }; + + // --- NEW VALUE --- + try self.values.append(self.alloc, .{ + .value = value, + .key = key, + }); + errdefer _ = self.values.swapRemove(idx); + + // --- NEW LOOKUP --- + try self.lookup.put(key, handle); + errdefer _ = self.lookup.remove(key); + + // --- NEW GENERATION --- + try self.generations.append(self.alloc, 1); + + // --- RESULT --- + return handle; + } + + /// ---------------------------------------------------- + /// ---------------------------------------------------- + pub fn remove( + self: *Self, + handle: Handle(V), + ) void { + const idx: u32 = handle.idx; + + // --- CHECK IF STALE --- + if (idx >= self.generations.items.len) return; + if (self.generations.items[idx] != handle.gen) return; + + // --- REMOVE KEY --- + const key = self.values.items[idx].key; + _ = self.lookup.remove(key); + + // --- GENERATION --- + self.generations.items[idx] +|= 1; + self.free_list.append(self.alloc, idx) catch unreachable; + } + + /// ---------------------------------------------------- + /// ---------------------------------------------------- + pub fn removeByKey(self: *Self, key: K) void { + const idx = self.lookup.get(key) orelse return; + _ = self.lookup.remove(key); + self.generations.items[idx] +|= 1; + self.free_list.append(self.alloc, idx) catch unreachable; + } + + /// ---------------------------------------------------- + /// ---------------------------------------------------- + pub fn get(self: *Self, handle: Handle(V)) ?*V { + if (handle.idx >= self.values.items.len) return null; + if (self.generations.items[handle.idx] != handle.gen) return null; + return &self.values.items[handle.idx].value; + } + + /// ---------------------------------------------------- + /// ---------------------------------------------------- + pub fn getByKey(self: *Self, key: K) ?*V { + const handle = self.lookup.get(key) orelse return null; + return self.get(handle); + } + + /// ---------------------------------------------------- + /// ---------------------------------------------------- + pub fn getHandle(self: *Self, key: K) ?Handle(V) { + return self.lookup.get(key); + } + + /// ---------------------------------------------------- + /// ---------------------------------------------------- + pub fn contains(self: *Self, key: K) bool { + return self.lookup.contains(key); + } + + /// ---------------------------------------------------- + /// ---------------------------------------------------- + pub fn count(self: *Self) u32 { + return self.lookup.count(); + } + }; +} diff --git a/src/type_id.zig b/src/type_id.zig new file mode 100644 index 0000000..c756d00 --- /dev/null +++ b/src/type_id.zig @@ -0,0 +1,8 @@ +const std = @import("std"); + +pub fn of(comptime T: type) u64 { + return std.hash.Wyhash.hash( + 694721, + @typeName(T), + ); +}