Minor refactors and cleanups

This commit is contained in:
2025-12-02 15:58:51 +01:00
parent faddb1f35e
commit be4ae4f1a7
15 changed files with 223 additions and 121 deletions

View File

@@ -151,8 +151,6 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain
var blocks = try Blocks.init(allocator);
errdefer blocks.deinit(allocator);
blocks.loadAll(engine, &materials, &textures, allocator);
const sampler = try engine.createSampler(.{
.mag_filter = .linear,
.min_filter = .linear,
@@ -510,6 +508,13 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain
});
engine.setObjectName(global_descriptor_set, "DS Global", .{});
const block_grass = try blocks.getOrLoadFilename(engine, &materials, &textures, "Grass.json", allocator);
const block_dirt = try blocks.getOrLoadFilename(engine, &materials, &textures, "Dirt.json", allocator);
const block_stone = try blocks.getOrLoadFilename(engine, &materials, &textures, "Stone.json", allocator);
const block_bedrock = try blocks.getOrLoadFilename(engine, &materials, &textures, "Bedrock.json", allocator);
// VOLATILE Load all assets before this point
const descriptor_images = try allocator.alloc(vk.DescriptorImageInfo, textures.textures.items.len);
for (textures.textures.items, descriptor_images) |texture, *info| {
info.* = .{
@@ -603,12 +608,7 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain
const world_seed = engine.random.int(u64);
std.log.info("Using world seed 0x{x:0<16}", .{world_seed});
var it = Interator2(i16).init(-10, -10, 9, 9);
const block_grass = blocks.getFilename("Grass.json").?;
const block_dirt = blocks.getFilename("Dirt.json").?;
const block_stone = blocks.getFilename("Stone.json").?;
const block_bedrock = blocks.getFilename("Bedrock.json").?;
var it = Interator2(i16).init(-8, -8, 7, 7);
while (it.next()) |chunk_coords2| {
const chunk_coords3 = chunk_coords2 ++ [_]i16{0};
@@ -729,7 +729,7 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain
}
pub fn deinit(self: *Game) void {
std.log.debug("Deinitializing {*}", .{self});
std.log.scoped(.deinit).debug("Deinitializing {*}", .{self});
self.vertex_buffer.deinit(self.engine);
self.index_buffer.deinit(self.engine);

View File

@@ -1,7 +1,7 @@
const Blocks = @This();
const std = @import("std");
const atoms = @import("../engine/atoms.zig");
const Atom = @import("../engine/Atom.zig").Atom;
const Engine = @import("../engine/Engine.zig");
const Materials = @import("Materials.zig");
const Textures = @import("Textures.zig");
@@ -60,7 +60,7 @@ blocks: Array,
pub const capacity = std.math.maxInt(std.meta.Tag(Id));
pub const Key = struct { atom: atoms.Atom };
pub const Key = struct { atom: Atom };
pub const Id = enum(u12) {
air = 0,
_,
@@ -91,20 +91,20 @@ pub fn init(allocator: std.mem.Allocator) !Blocks {
}
pub fn deinit(self: *Blocks, allocator: std.mem.Allocator) void {
std.log.debug("Deinitializing {*} with Allocator{{{*},{*}}}", .{ self, allocator.ptr, allocator.vtable });
std.log.scoped(.deinit).debug("Deinitializing {*} with Allocator{{{*},{*}}}", .{ self, allocator.ptr, allocator.vtable });
self.blocks.deinit(allocator);
self.map.deinit(allocator);
self.* = undefined;
}
pub fn getAtom(self: *const Blocks, atom: atoms.Atom) ?Id {
pub fn getAtom(self: *const Blocks, atom: Atom) ?Id {
const key: Key = .{ .atom = atom };
return self.map.get(key);
}
pub fn getFilename(self: *const Blocks, filename: []const u8) ?Id {
const atom = atoms.getAtom(filename) orelse return null;
const atom = Atom.fromStringIfExists(filename) orelse return null;
const key: Key = .{ .atom = atom };
return self.map.get(key);
}
@@ -119,7 +119,7 @@ pub fn getOrLoadAtom(
engine: *Engine,
materials: *Materials,
textures: *Textures,
atom: atoms.Atom,
atom: Atom,
temp_allocator: std.mem.Allocator,
) !Id {
const key: Key = .{ .atom = atom };
@@ -129,7 +129,7 @@ pub fn getOrLoadAtom(
return entry.value_ptr.*;
} else {
errdefer _ = self.map.remove(key);
const block = try loadBlock(engine, materials, textures, atoms.getString(atom), temp_allocator);
const block = try loadBlock(engine, materials, textures, Atom.toString(), temp_allocator);
const id = self.nextId();
entry.value_ptr.* = id;
self.blocks.appendAssumeCapacity(block);
@@ -145,7 +145,7 @@ pub fn getOrLoadFilename(
filename: []const u8,
temp_allocator: std.mem.Allocator,
) !Id {
const atom = try atoms.getOrPutAtom(filename);
const atom = try Atom.fromString(filename);
const key: Key = .{ .atom = atom };
const entry = self.map.getOrPutAssumeCapacity(key);

View File

@@ -98,6 +98,8 @@ pub fn init(engine: *Engine, init_info: InitInfo) !Chunk {
}
pub fn deinit(self: *Chunk, engine: *Engine, descriptor_pool: vk.DescriptorPool) void {
std.log.scoped(.deinit).debug("Deinitializing chunk {d} with {*} and {s}#{X}", .{ self.chunk_id, engine, @typeName(vk.DescriptorPool), @intFromEnum(descriptor_pool) });
self.object_buffer.deinit(engine);
engine.freeDescriptorSet(descriptor_pool, self.descriptor_set);

View File

@@ -3,7 +3,7 @@ const std = @import("std");
const vk = @import("vulkan");
const atoms = @import("../engine/atoms.zig");
const Atom = @import("../engine/Atom.zig").Atom;
const Engine = @import("../engine/Engine.zig");
const GenericBuffer = @import("../engine/GenericBuffer.zig").GenericBuffer;
const Textures = @import("Textures.zig");
@@ -17,7 +17,7 @@ next_id: Id,
// capacity * @sizeOf(Material) = 832 kiB
pub const capacity = std.math.maxInt(std.meta.Tag(Id));
pub const Key = struct { atom: atoms.Atom };
pub const Key = struct { atom: Atom };
pub const Id = enum(u16) {
empty,
_,
@@ -67,25 +67,25 @@ pub fn init(engine: *Engine, allocator: std.mem.Allocator) !Materials {
}
pub fn deinit(self: *Materials, engine: *Engine, allocator: std.mem.Allocator) void {
std.log.debug("Deinitializing {*} with {*} and Allocator{{{*},{*}}}", .{ self, engine, allocator.ptr, allocator.vtable });
std.log.scoped(.deinit).debug("Deinitializing {*} with {*} and Allocator{{{*},{*}}}", .{ self, engine, allocator.ptr, allocator.vtable });
self.material_buffer.deinit(engine);
self.map.deinit(allocator);
self.* = undefined;
}
pub fn getAtom(self: *const Materials, atom: atoms.Atom) ?Id {
pub fn getAtom(self: *const Materials, atom: Atom) ?Id {
const key: Key = .{ .atom = atom };
return self.map.get(key);
}
pub fn getFilename(self: *const Materials, filename: []const u8) ?Id {
const atom = atoms.getAtom(filename) orelse return null;
const atom = Atom.fromStringIfExists(filename) orelse return null;
const key: Key = .{ .atom = atom };
return self.map.get(key);
}
pub fn getOrLoadAtom(self: *Materials, engine: *Engine, textures: *Textures, atom: atoms.Atom, temp_allocator: std.mem.Allocator) !Id {
pub fn getOrLoadAtom(self: *Materials, engine: *Engine, textures: *Textures, atom: Atom, temp_allocator: std.mem.Allocator) !Id {
const key: Key = .{ .atom = atom };
const entry = self.map.getOrPutAssumeCapacity(key);
@@ -93,14 +93,14 @@ pub fn getOrLoadAtom(self: *Materials, engine: *Engine, textures: *Textures, ato
return entry.value_ptr.*;
} else {
errdefer _ = self.map.remove(key);
const id = try self.loadMaterial(engine, textures, atoms.getString(atom), temp_allocator);
const id = try self.loadMaterial(engine, textures, atom.toString(), temp_allocator);
entry.value_ptr.* = id;
return id;
}
}
pub fn getOrLoadFilename(self: *Materials, engine: *Engine, textures: *Textures, filename: []const u8, temp_allocator: std.mem.Allocator) !Id {
const atom = try atoms.getOrPutAtom(filename);
const atom = try Atom.fromString(filename);
const key: Key = .{ .atom = atom };
const entry = self.map.getOrPutAssumeCapacity(key);

View File

@@ -3,7 +3,7 @@ const std = @import("std");
const stbi = @import("zstbi");
const atoms = @import("../engine/atoms.zig");
const Atom = @import("../engine/Atom.zig").Atom;
const Engine = @import("../engine/Engine.zig");
const Texture = @import("../engine/Texture.zig");
@@ -13,7 +13,7 @@ textures: Array,
pub const capacity = std.math.maxInt(std.meta.Tag(Id));
pub const Key = struct {
atom: atoms.Atom,
atom: Atom,
usage: Texture.Usage,
};
@@ -93,7 +93,7 @@ pub fn init(engine: *Engine, allocator: std.mem.Allocator) !Textures {
}
pub fn deinit(self: *Textures, engine: *Engine, allocator: std.mem.Allocator) void {
std.log.debug("Deinitializing {*} with {*} and Allocator{{{*},{*}}}", .{ self, engine, allocator.ptr, allocator.vtable });
std.log.scoped(.deinit).debug("Deinitializing {*} with {*} and Allocator{{{*},{*}}}", .{ self, engine, allocator.ptr, allocator.vtable });
for (self.textures.items) |*texture| {
texture.deinit(engine);
@@ -103,13 +103,13 @@ pub fn deinit(self: *Textures, engine: *Engine, allocator: std.mem.Allocator) vo
self.* = undefined;
}
pub fn getAtom(self: *const Textures, atom: atoms.Atom, usage: Texture.Usage) ?Id {
pub fn getAtom(self: *const Textures, atom: Atom, usage: Texture.Usage) ?Id {
const key: Key = .{ .atom = atom, .usage = usage };
return self.map.get(key);
}
pub fn getFilename(self: *const Textures, filename: []const u8, usage: Texture.Usage) ?Id {
const atom = atoms.getAtom(filename) orelse return null;
const atom = Atom.fromStringIfExists(filename) orelse return null;
const key: Key = .{ .atom = atom, .usage = usage };
return self.map.get(key);
}
@@ -119,7 +119,7 @@ pub fn getTexture(self: *const Textures, id: Id) ?*Texture {
return if (index < self.textures.items.len) &self.textures.items[index] else null;
}
pub fn getOrLoadAtom(self: *Textures, engine: *Engine, atom: atoms.Atom, usage: Texture.Usage, temp_allocator: std.mem.Allocator) !Id {
pub fn getOrLoadAtom(self: *Textures, engine: *Engine, atom: Atom, usage: Texture.Usage, temp_allocator: std.mem.Allocator) !Id {
const key: Key = .{ .atom = atom, .usage = usage };
const entry = self.map.getOrPutAssumeCapacity(key);
@@ -127,7 +127,7 @@ pub fn getOrLoadAtom(self: *Textures, engine: *Engine, atom: atoms.Atom, usage:
return entry.value_ptr.*;
} else {
errdefer _ = self.map.remove(key);
const texture = try loadTexture(engine, atoms.getString(atom), usage, temp_allocator);
const texture = try loadTexture(engine, atom.toString(), usage, temp_allocator);
const id = self.nextId();
entry.value_ptr.* = id;
self.textures.appendAssumeCapacity(texture);
@@ -136,7 +136,7 @@ pub fn getOrLoadAtom(self: *Textures, engine: *Engine, atom: atoms.Atom, usage:
}
pub fn getOrLoadFilename(self: *Textures, engine: *Engine, filename: []const u8, usage: Texture.Usage, temp_allocator: std.mem.Allocator) !Id {
const atom = try atoms.getOrPutAtom(filename);
const atom = try Atom.fromString(filename);
const key: Key = .{ .atom = atom, .usage = usage };
const entry = self.map.getOrPutAssumeCapacity(key);

167
src/engine/Atom.zig Normal file
View File

@@ -0,0 +1,167 @@
//! Module for string interning. A string can be converted to a stable integer
//! constant, called an *atom*. The value of an for a given string is guaranteed
//! to be stable throughout a program's runtime, but not across different runs.
//! There can be no more than 2¹⁶ atoms.
//!
//! Use this module for converting string IDs into numbers, so that they can be
//! compared more easily. The users of this module should import it with
//! `@import("Atom.zig").Atom` and use the methods available in the `Atom` type.
const std = @import("std");
pub const Atom = enum(u16) {
// VOLATILE Synchronize explicit values with `init` implementation.
/// Atom representing an empty string, i.e. `""`.
empty,
_,
/// Cast an integer into an atom. This can produce an invalid atom.
pub fn fromInt(value: u16) Atom {
return @enumFromInt(value);
}
/// Cast an index into an atom. This can produce an invalid atom. The caller
/// asserts that the index is not greater than the max atom value.
pub fn fromIndex(index: usize) Atom {
return @enumFromInt(@as(u16, @intCast(index)));
}
/// Cast an index into an atom. This can produce an invalid atom. Returns an
/// error if the index is greater than the max atom value.
pub fn fromIndexSafe(index: usize) error{Overflow}!Atom {
return @enumFromInt(std.math.cast(u16, index) orelse return error.Overflow);
}
/// Turn a string into an atom. Returns either an existing atom or makes a
/// new one, if necessary. This will always produce a valid atom. Will not
/// return any error if the atom already exists.
pub fn fromString(string: []const u8) error{ OutOfMemory, OutOfAtoms }!Atom {
mutex.lock();
defer mutex.unlock();
std.debug.assert(initialized);
const entry = try map.getOrPut(allocator, string);
if (entry.found_existing) {
return entry.value_ptr.*;
} else {
errdefer _ = map.remove(string);
const atom = Atom.fromIndexSafe(array.items.len) catch |err| switch (err) {
error.Overflow => return error.OutOfAtoms,
};
try array.ensureUnusedCapacity(allocator, 1);
const owned_string = try toOwnedString(string);
entry.key_ptr.* = owned_string;
entry.value_ptr.* = atom;
array.appendAssumeCapacity(owned_string);
return atom;
}
}
/// Turn a string into an atom, if the string has been already registered as
/// an atom. Returns `null` otherwise. This will always produce a valid
/// atom.
pub fn fromStringIfExists(string: []const u8) ?Atom {
mutex.lock();
defer mutex.unlock();
std.debug.assert(initialized);
return map.get(string);
}
/// Cast an atom into an integer.
pub fn toInt(self: Atom) u32 {
return @intFromEnum(self);
}
/// Cast an atom into a string. The caller asserts that the atom is valid.
pub fn toString(self: Atom) [:0]const u8 {
try mutex.lock();
defer mutex.unlock();
std.debug.assert(initialized);
return array.items[self.toInt()];
}
};
/// Flag for debug purposes, to catch misuses of the API.
var initialized: bool = false;
/// Allocator used for `map` and `array`. Also used as a child allocator for
/// `string_arena`.
var allocator: std.mem.Allocator = undefined;
/// Allocator for all string values. All values of `map` and keys of `map` are
/// allocated with this arena. The strings are allocated with a null terminator
/// for interoperability with libraries.
var string_arena: std.heap.ArenaAllocator = undefined;
/// Maps a string value to an atom value.
var map: std.StringHashMapUnmanaged(Atom) = undefined;
/// Maps an atom value to a string.
var array: std.ArrayList([:0]const u8) = undefined;
/// Protects all reads and writes to `map` and `array`.
var mutex: std.Thread.Mutex = .{};
pub fn init(_allocator: std.mem.Allocator) !void {
mutex.lock();
defer mutex.unlock();
std.debug.assert(!initialized);
allocator = _allocator;
string_arena = .init(_allocator);
map = .{};
array = .empty;
initialized = true;
// VOLATILE Synchronize with explicit values on top of `Atom` type.
try map.put(allocator, "", .empty);
try array.append(allocator, "");
}
pub fn deinit() void {
mutex.lock();
defer mutex.unlock();
std.log.scoped(.deinit).debug("Deinitializing atoms", .{});
std.debug.assert(initialized);
string_arena.deinit();
map.deinit(allocator);
array.deinit(allocator);
allocator = undefined;
string_arena = undefined;
map = undefined;
array = undefined;
initialized = false;
}
/// Dump all atoms in a readable format. Use for debugging. Does not flush the
/// writer.
pub fn dump(writer: std.Io.Writer) !void {
mutex.lock();
defer mutex.unlock();
std.debug.assert(initialized);
for (array.items, 0..) |string, i| {
const atom: u32 = @intCast(i);
writer.print("0x{X:0<8} {s}\n", .{ atom, string });
}
}
fn toOwnedString(string: []const u8) ![:0]const u8 {
const owned_string = try string_arena.allocator().dupeZ(u8, string);
return owned_string;
}

View File

@@ -398,7 +398,7 @@ pub fn init(allocator: std.mem.Allocator, maybe_window: ?*glfw.Window) !Engine {
}
pub fn deinit(self: *Engine) void {
std.log.debug("Deinitializing {*}", .{self});
std.log.scoped(.deinit).debug("Deinitializing {*}", .{self});
const allocator = self.vk_allocator.allocator;

View File

@@ -89,7 +89,7 @@ pub fn GenericBuffer(comptime Header: type, comptime Element: type) type {
}
pub fn deinit(self: *Self, engine: *Engine) void {
std.log.debug("Deinitializing {*} with {*}", .{ self, engine });
std.log.scoped(.deinit).debug("Deinitializing {*} with {*}", .{ self, engine });
engine.freeMemory(self.device_memory);
engine.destroyBuffer(self.buffer);

View File

@@ -51,7 +51,7 @@ pub fn init(engine: *Engine, init_info: InitInfo) !StagingBuffer {
}
pub fn deinit(self: *StagingBuffer, engine: *Engine) void {
std.log.debug("Deinitializing {*} with {*}", .{ self, engine });
std.log.scoped(.deinit).debug("Deinitializing {*} with {*}", .{ self, engine });
engine.freeMemory(self.device_memory);
engine.destroyBuffer(self.buffer);

View File

@@ -116,7 +116,7 @@ pub fn init(engine: *Engine) !Swapchain {
}
pub fn deinit(self: *Swapchain, engine: *Engine) void {
std.log.debug("Deinitializing {*} with {*}", .{ self, engine });
std.log.scoped(.deinit).debug("Deinitializing {*} with {*}", .{ self, engine });
const allocator = engine.vk_allocator.allocator;
@@ -475,7 +475,7 @@ const SwapchainImage = struct {
}
fn deinit(self: *SwapchainImage, engine: *Engine) void {
std.log.debug("Deinitializing {*} with {*}", .{ &self, engine });
std.log.scoped(.deinit).debug("Deinitializing {*} with {*}", .{ self, engine });
engine.waitForFence(self.fence) catch {};
if (self.command_buffer) |*command_buffer| {

View File

@@ -157,7 +157,7 @@ pub fn init(engine: *Engine, init_info: InitInfo) !Texture {
}
pub fn deinit(self: *Texture, engine: *Engine) void {
std.log.debug("Deinitializing {*} with {*}", .{ self, engine });
std.log.scoped(.deinit).debug("Deinitializing {*} with {*}", .{ self, engine });
engine.destroyImageView(self.image_view);
engine.freeMemory(self.device_memory);

View File

@@ -35,7 +35,7 @@ pub fn init(allocator: std.mem.Allocator) !*VkAllocator {
}
pub fn deinit(self: *VkAllocator) void {
std.log.debug("Deinitializing {*}", .{self});
std.log.scoped(.deinit).debug("Deinitializing {*}", .{self});
if (self.allocated_bytes > 0) {
log.warn("{d} byte(s) still allocated while deinitializing", .{self.allocated_bytes});
}

View File

@@ -1,77 +0,0 @@
pub const Atoms = @This();
const std = @import("std");
var allocator: std.mem.Allocator = undefined;
var string_arena: std.heap.ArenaAllocator = undefined;
var map: Map = undefined;
var array: Array = undefined;
var mutex: std.Thread.Mutex = undefined;
pub const Atom = enum(u32) { _ };
pub const Map = std.StringHashMapUnmanaged(Atom);
pub const Array = std.ArrayList([:0]const u8);
pub fn init(_allocator: std.mem.Allocator) void {
allocator = _allocator;
string_arena = .init(_allocator);
map = .{};
array = .empty;
mutex = .{};
}
pub fn deinit() void {
std.log.debug("Deinitializing atoms", .{});
string_arena.deinit();
map.deinit(allocator);
array.deinit(allocator);
allocator = undefined;
string_arena = undefined;
map = undefined;
array = undefined;
mutex = undefined;
}
pub fn getString(atom: Atom) [:0]const u8 {
try mutex.lock();
defer mutex.unlock();
return array.items[atom.value];
}
pub fn getAtom(string: []const u8) ?Atom {
mutex.lock();
defer mutex.unlock();
return map.get(string);
}
pub fn getOrPutAtom(string: []const u8) !Atom {
mutex.lock();
defer mutex.unlock();
const entry = try map.getOrPut(allocator, string);
if (entry.found_existing) {
return entry.value_ptr.*;
} else {
errdefer _ = map.remove(string);
try array.ensureUnusedCapacity(allocator, 1);
const owned_string = try toOwnedString(string);
const atom: Atom = @enumFromInt(array.items.len);
entry.key_ptr.* = owned_string;
entry.value_ptr.* = atom;
array.appendAssumeCapacity(owned_string);
return atom;
}
}
fn toOwnedString(string: []const u8) ![:0]const u8 {
const owned_string = try string_arena.allocator().dupeZ(u8, string);
return owned_string;
}

View File

@@ -6,13 +6,23 @@ const vk = @import("vulkan");
const c = @import("const.zig");
const atoms = @import("engine/atoms.zig");
const atoms = @import("engine/Atom.zig");
const Engine = @import("engine/Engine.zig");
const Swapchain = @import("engine/Swapchain.zig");
const Game = @import("Game.zig");
pub const std_options: std.Options = .{
.log_level = .info,
.log_scope_levels = &.{
.{
.scope = .vulkan,
.level = .debug,
},
.{
.scope = .deinit,
// Change to `.debug` to see a log when calling `deinit`.
.level = .info,
},
},
};
pub fn main() !void {
@@ -21,7 +31,7 @@ pub fn main() !void {
const allocator = gpa.allocator();
atoms.init(allocator);
try atoms.init(allocator);
defer atoms.deinit();
stbi.init(allocator);

View File

@@ -31,7 +31,7 @@ pub inline fn epu64x2(value: u64) u64x8 {
pub inline fn lerp(a: f32, b: f32, t: f32) f32 {
const s = 1.0 - t;
return a * t + b * s;
return a * s + b * t;
}
pub const noise2 = @import("math/noise.zig").noise2;