Make use of some globals to stop typing engine everywhere.

This commit is contained in:
2026-05-15 02:20:08 +02:00
parent 36434f8107
commit ad80fb4fd9
21 changed files with 902 additions and 798 deletions

View File

@@ -2,6 +2,7 @@ const Game = @This();
const std = @import("std");
const c = @import("const.zig");
const ctx = @import("AppContext.zig");
const glfw = @import("zglfw");
const math = @import("math.zig");
const media = @import("media");
@@ -25,10 +26,6 @@ const Swapchain = @import("engine/Swapchain.zig");
const Texture = @import("engine/Texture.zig");
const Textures = @import("assets/Textures.zig");
allocator: std.mem.Allocator,
io: std.Io,
engine: *Engine,
swapchain: *Swapchain,
global_descriptor_set_layout: vk.DescriptorSetLayout,
per_batch_descriptor_set_layout: vk.DescriptorSetLayout,
descriptor_pool: vk.DescriptorPool,
@@ -64,24 +61,30 @@ const chunk_descriptor_pool = 1024;
const camera_near_plane = 0.1;
pub fn init(allocator: std.mem.Allocator, io: std.Io, engine: *Engine, swapchain: *Swapchain) !Game {
var stbi = media.stbi.init(allocator, io);
pub fn init() !Game {
const allocator_general = ctx.allocator_general;
const allocator_frame = ctx.allocator_frame;
const io = ctx.io;
const engine = ctx.engine;
const swapchain = ctx.swapchain;
var stbi = media.stbi.init(allocator_general, io);
errdefer stbi.deinit();
var materials = try Materials.init(engine, allocator);
errdefer materials.deinit(engine, allocator);
var materials = try Materials.init();
errdefer materials.deinit();
var textures = try Textures.init(engine, allocator);
errdefer textures.deinit(engine, allocator);
var textures = try Textures.init();
errdefer textures.deinit();
var blocks = try Blocks.init(allocator);
errdefer blocks.deinit(allocator);
var blocks = try Blocks.init();
errdefer blocks.deinit();
// JANK HACK When this line is removed, capturing a frame with RenderDoc
// will crash the game with segfault reading address 0x140 (presumably
// within librenderdoc.so).
blocks.loadAll(engine, &materials, &textures, &stbi, allocator, io);
blocks.loadAll(&materials, &textures, &stbi);
const sampler = try engine.createSampler(.{
.mag_filter = .linear,
@@ -177,14 +180,14 @@ pub fn init(allocator: std.mem.Allocator, io: std.Io, engine: *Engine, swapchain
defer engine.destroyShaderModule(fragment_shader);
engine.setObjectName(fragment_shader, "SM main_frag", .{});
var vertex_buffer = try shaders.VertexBuffer.init(engine, .{
var vertex_buffer = try shaders.VertexBuffer.init(.{
.usage = .vertex,
.target_queue = .graphics,
.array_capacity = 4,
.name = "QuadVB",
});
errdefer vertex_buffer.deinit(engine);
try vertex_buffer.write(engine, .{
errdefer vertex_buffer.deinit();
try vertex_buffer.write(.{
.elements = &.{
.init(
.init(0, 0, 0),
@@ -213,33 +216,33 @@ pub fn init(allocator: std.mem.Allocator, io: std.Io, engine: *Engine, swapchain
},
});
var index_buffer = try shaders.IndexBuffer.init(engine, .{
var index_buffer = try shaders.IndexBuffer.init(.{
.usage = .index,
.target_queue = .graphics,
.array_capacity = 6,
.name = "QuadIB",
});
errdefer index_buffer.deinit(engine);
try index_buffer.write(engine, .{
errdefer index_buffer.deinit();
try index_buffer.write(.{
.elements = &.{ 0, 1, 2, 2, 1, 3 },
});
var global_uniforms = try shaders.GlobalUniformsBuffer.init(engine, .{
var global_uniforms = try shaders.GlobalUniformsBuffer.init(.{
.usage = .uniform,
.target_queue = .graphics,
.name = "GlobalUniforms",
});
errdefer global_uniforms.deinit(engine);
errdefer global_uniforms.deinit();
var global_uniforms_staging_buffer = try StagingBuffer.init(engine, .{
var global_uniforms_staging_buffer = try StagingBuffer.init(.{
.capacity = @sizeOf(shaders.GlobalUniforms),
.target_queue = .graphics,
});
errdefer global_uniforms_staging_buffer.deinit(engine);
errdefer global_uniforms_staging_buffer.deinit();
const global_uniforms_transfer_semaphores = blk: {
var semaphores: std.ArrayList(vk.Semaphore) = try .initCapacity(allocator, swapchain.swapchain_images.len);
errdefer semaphores.deinit(allocator);
var semaphores: std.ArrayList(vk.Semaphore) = try .initCapacity(allocator_general, swapchain.swapchain_images.len);
errdefer semaphores.deinit(allocator_general);
errdefer for (semaphores.items) |x| engine.destroySemaphore(x);
@@ -249,30 +252,30 @@ pub fn init(allocator: std.mem.Allocator, io: std.Io, engine: *Engine, swapchain
semaphores.appendAssumeCapacity(semaphore);
}
break :blk try semaphores.toOwnedSlice(allocator);
break :blk try semaphores.toOwnedSlice(allocator_general);
};
errdefer {
for (global_uniforms_transfer_semaphores) |semaphore| {
engine.destroySemaphore(semaphore);
}
allocator.free(global_uniforms_transfer_semaphores);
allocator_general.free(global_uniforms_transfer_semaphores);
}
var point_lights = try shaders.PointLightBuffer.init(engine, .{
var point_lights = try shaders.PointLightBuffer.init(.{
.usage = .storage,
.target_queue = .graphics,
.array_capacity = max_point_lights,
.name = "PointLights",
});
errdefer point_lights.deinit(engine);
errdefer point_lights.deinit();
var directional_lights = try shaders.DirectionalLightBuffer.init(engine, .{
var directional_lights = try shaders.DirectionalLightBuffer.init(.{
.usage = .storage,
.target_queue = .graphics,
.array_capacity = max_directional_lights,
.name = "DirectionalLights",
});
errdefer directional_lights.deinit(engine);
errdefer directional_lights.deinit();
const pipeline = try engine.createGraphicsPipeline(.{
.stages = &.{
@@ -444,14 +447,14 @@ pub fn init(allocator: std.mem.Allocator, io: std.Io, engine: *Engine, swapchain
});
engine.setObjectName(global_descriptor_set, "DS Global", .{});
const block_grass = try blocks.getOrLoad(engine, &materials, &textures, &stbi, "Grass.json", allocator, io);
const block_dirt = try blocks.getOrLoad(engine, &materials, &textures, &stbi, "Dirt.json", allocator, io);
const block_stone = try blocks.getOrLoad(engine, &materials, &textures, &stbi, "Stone.json", allocator, io);
const block_bedrock = try blocks.getOrLoad(engine, &materials, &textures, &stbi, "Bedrock.json", allocator, io);
const block_grass = try blocks.getOrLoad(&materials, &textures, &stbi, "Grass.json");
const block_dirt = try blocks.getOrLoad(&materials, &textures, &stbi, "Dirt.json");
const block_stone = try blocks.getOrLoad(&materials, &textures, &stbi, "Stone.json");
const block_bedrock = try blocks.getOrLoad(&materials, &textures, &stbi, "Bedrock.json");
// VOLATILE Load all assets before this point
const descriptor_images = try allocator.alloc(vk.DescriptorImageInfo, textures.array.items.len);
const descriptor_images = try allocator_frame.alloc(vk.DescriptorImageInfo, textures.array.items.len);
for (textures.array.items, descriptor_images) |texture, *info| {
info.* = .{
.sampler = .null_handle,
@@ -459,7 +462,6 @@ pub fn init(allocator: std.mem.Allocator, io: std.Io, engine: *Engine, swapchain
.image_layout = .shader_read_only_optimal,
};
}
defer allocator.free(descriptor_images);
try engine.updateDescriptorSets(.{
.writes = &.{
@@ -537,9 +539,9 @@ pub fn init(allocator: std.mem.Allocator, io: std.Io, engine: *Engine, swapchain
errdefer {
var it = chunks.valueIterator();
while (it.next()) |chunk| {
chunk.deinit(engine, descriptor_pool);
chunk.deinit(descriptor_pool);
}
chunks.deinit(allocator);
chunks.deinit(allocator_general);
}
const world_seed = engine.random.int(u64);
@@ -557,10 +559,10 @@ pub fn init(allocator: std.mem.Allocator, io: std.Io, engine: *Engine, swapchain
@floatFromInt(chunk_coords3[2]),
).mulScalar(16);
try chunks.ensureUnusedCapacity(allocator, 1);
try chunks.ensureUnusedCapacity(allocator_general, 1);
chunks.putAssumeCapacityNoClobber(
chunk_coords3,
try Chunk.init(engine, .{
try Chunk.init(.{
.origin = origin,
.descriptor_pool = descriptor_pool,
.per_batch_descriptor_set_layout = per_batch_descriptor_set_layout,
@@ -611,18 +613,18 @@ pub fn init(allocator: std.mem.Allocator, io: std.Io, engine: *Engine, swapchain
while (chunk_it.next()) |entry| {
const x, const y, const z = entry.key_ptr.*;
const chunk = entry.value_ptr;
try chunk.refresh(engine, &blocks, .{
try chunk.refresh(&blocks, .{
.positive_x = chunks.getPtr(.{ x + 1, y, z }),
.negative_x = chunks.getPtr(.{ x - 1, y, z }),
.positive_y = chunks.getPtr(.{ x, y + 1, z }),
.negative_y = chunks.getPtr(.{ x, y - 1, z }),
.positive_z = chunks.getPtr(.{ x, y, z + 1 }),
.negative_z = chunks.getPtr(.{ x, y, z - 1 }),
}, allocator);
});
}
const point_lights_data: []const shaders.PointLight = &.{};
try point_lights.write(engine, .{
try point_lights.write(.{
.header = @intCast(point_lights_data.len),
.elements = point_lights_data,
});
@@ -633,36 +635,25 @@ pub fn init(allocator: std.mem.Allocator, io: std.Io, engine: *Engine, swapchain
.color = .init(0.3, 0.3, 0.3),
},
};
try directional_lights.write(engine, .{
try directional_lights.write(.{
.header = @intCast(directional_lights_data.len),
.elements = directional_lights_data,
});
var skybox = try Skybox.load(
"skybox.hdr",
engine,
swapchain,
&stbi,
512,
global_uniforms.buffer,
allocator,
io,
);
errdefer skybox.deinit(engine);
errdefer skybox.deinit();
var gui = try Gui.init(
allocator,
engine,
swapchain,
global_uniforms.buffer,
);
errdefer gui.deinit(engine, allocator);
errdefer gui.deinit();
return .{
.allocator = allocator,
.io = io,
.engine = engine,
.swapchain = swapchain,
.global_descriptor_set_layout = global_descriptor_set_layout,
.per_batch_descriptor_set_layout = per_batch_descriptor_set_layout,
.descriptor_pool = descriptor_pool,
@@ -679,7 +670,7 @@ pub fn init(allocator: std.mem.Allocator, io: std.Io, engine: *Engine, swapchain
.point_lights = point_lights,
.directional_lights = directional_lights,
.sampler = sampler,
.deferred_command_buffers = try .initCapacity(allocator, 4),
.deferred_command_buffers = try .initCapacity(allocator_general, 4),
.stbi = stbi,
.blocks = blocks,
@@ -694,49 +685,54 @@ pub fn init(allocator: std.mem.Allocator, io: std.Io, engine: *Engine, swapchain
}
pub fn deinit(self: *Game) void {
const allocator_general = ctx.allocator_general;
const engine = ctx.engine;
std.log.scoped(.deinit).debug("Deinitializing {*}", .{self});
for (self.deferred_command_buffers.items) |*command_buffer| {
command_buffer.deinit(self.engine);
command_buffer.deinit();
}
self.deferred_command_buffers.deinit(self.allocator);
self.deferred_command_buffers.deinit(allocator_general);
self.vertex_buffer.deinit(self.engine);
self.index_buffer.deinit(self.engine);
self.vertex_buffer.deinit();
self.index_buffer.deinit();
self.chunks.deinit(self.engine, self.descriptor_pool, self.allocator);
self.skybox.deinit(self.engine);
self.gui.deinit(self.engine, self.allocator);
self.chunks.deinit(self.descriptor_pool);
self.skybox.deinit();
self.gui.deinit();
self.global_uniforms.deinit(self.engine);
self.global_uniforms_staging_buffer.deinit(self.engine);
self.point_lights.deinit(self.engine);
self.directional_lights.deinit(self.engine);
self.global_uniforms.deinit();
self.global_uniforms_staging_buffer.deinit();
self.point_lights.deinit();
self.directional_lights.deinit();
for (self.global_uniforms_transfer_semaphores) |semaphore| {
self.engine.destroySemaphore(semaphore);
engine.destroySemaphore(semaphore);
}
self.allocator.free(self.global_uniforms_transfer_semaphores);
allocator_general.free(self.global_uniforms_transfer_semaphores);
self.engine.destroyDescriptorPool(self.descriptor_pool);
self.engine.destroySampler(self.sampler);
self.engine.destroyPipeline(self.pipeline);
self.engine.destroyPipelineLayout(self.pipeline_layout);
self.engine.destroyDescriptorSetLayout(self.per_batch_descriptor_set_layout);
self.engine.destroyDescriptorSetLayout(self.global_descriptor_set_layout);
engine.destroyDescriptorPool(self.descriptor_pool);
engine.destroySampler(self.sampler);
engine.destroyPipeline(self.pipeline);
engine.destroyPipelineLayout(self.pipeline_layout);
engine.destroyDescriptorSetLayout(self.per_batch_descriptor_set_layout);
engine.destroyDescriptorSetLayout(self.global_descriptor_set_layout);
self.textures.deinit(self.engine, self.allocator);
self.materials.deinit(self.engine, self.allocator);
self.blocks.deinit(self.allocator);
self.textures.deinit();
self.materials.deinit();
self.blocks.deinit();
self.stbi.deinit();
self.* = undefined;
}
pub fn update(self: *Game, dt: f32) void {
const swapchain = ctx.swapchain;
self.gui.beginFrame();
self.player.update(dt, &self.chunks);
const extent = self.swapchain.extent;
const extent = swapchain.extent;
const framebuffer_size = vm.Vector2.init(
@floatFromInt(extent.width),
@floatFromInt(extent.height),
@@ -791,8 +787,12 @@ pub fn onMouseUp(self: *Game, button: glfw.MouseButton) void {
}
fn render(self: *Game) !void {
const allocator_general = ctx.allocator_general;
const window = ctx.window;
const swapchain = ctx.swapchain;
const framebuffer_width, const framebuffer_height = blk: {
const w, const h = self.engine.mode.surface.window.getFramebufferSize();
const w, const h = window.getFramebufferSize();
break :blk [_]u32{ @intCast(w), @intCast(h) };
};
@@ -800,25 +800,25 @@ fn render(self: *Game) !void {
return;
}
if (framebuffer_width != self.swapchain.extent.width or framebuffer_height != self.swapchain.extent.height) {
if (framebuffer_width != swapchain.extent.width or framebuffer_height != swapchain.extent.height) {
try self.recreateSwapchain();
}
if (self.swapchain.swapchain == .null_handle) {
if (swapchain.swapchain == .null_handle) {
return;
}
self.swapchain.acquire(self.engine) catch |err| switch (err) {
swapchain.acquire() catch |err| switch (err) {
error.OutOfDateKHR => return self.recreateSwapchain(),
else => return err,
};
for (self.deferred_command_buffers.items) |*deferred_command_buffer| {
deferred_command_buffer.deinit(self.engine);
deferred_command_buffer.deinit();
}
self.deferred_command_buffers.clearRetainingCapacity();
const extent = self.swapchain.extent;
const extent = swapchain.extent;
const framebuffer_size = vm.Vector2.init(
@floatFromInt(extent.width),
@@ -867,12 +867,12 @@ fn render(self: *Game) !void {
.ambientLight = ambient_light,
};
const staging_memory = try self.global_uniforms_staging_buffer.map(self.engine);
const staging_memory = try self.global_uniforms_staging_buffer.map();
@memcpy(staging_memory, std.mem.asBytes(&global_uniforms_data));
self.global_uniforms_staging_buffer.unmap(self.engine);
self.global_uniforms_staging_buffer.unmap();
try self.deferred_command_buffers.ensureUnusedCapacity(self.allocator, 1);
const global_uniforms_transfer_command_buffer = try CommandBuffer.init(self.engine, .transfer);
try self.deferred_command_buffers.ensureUnusedCapacity(allocator_general, 1);
const global_uniforms_transfer_command_buffer = try CommandBuffer.init(.transfer);
self.deferred_command_buffers.appendAssumeCapacity(global_uniforms_transfer_command_buffer);
try global_uniforms_transfer_command_buffer.beginCommandBuffer();
@@ -889,17 +889,17 @@ fn render(self: *Game) !void {
);
try global_uniforms_transfer_command_buffer.endCommandBuffer();
try global_uniforms_transfer_command_buffer.submit(self.engine, .{
.signal_semaphores = &.{self.global_uniforms_transfer_semaphores[self.swapchain.image_index.?]},
try global_uniforms_transfer_command_buffer.submit(.{
.signal_semaphores = &.{self.global_uniforms_transfer_semaphores[swapchain.image_index.?]},
});
try self.deferred_command_buffers.ensureUnusedCapacity(self.allocator, 1);
const command_buffer: CommandBuffer = try .init(self.engine, .graphics);
try self.deferred_command_buffers.ensureUnusedCapacity(allocator_general, 1);
const command_buffer: CommandBuffer = try .init(.graphics);
self.deferred_command_buffers.appendAssumeCapacity(command_buffer);
try command_buffer.beginCommandBuffer();
{
const swapchain_image = &self.swapchain.swapchain_images[self.swapchain.image_index.?];
const swapchain_image = &swapchain.swapchain_images[swapchain.image_index.?];
command_buffer.pipelineBarrier(.{
.src_stage_mask = .{ .color_attachment_output_bit = true },
@@ -942,7 +942,7 @@ fn render(self: *Game) !void {
.new_layout = .depth_stencil_attachment_optimal,
.src_queue_family_index = vk.QUEUE_FAMILY_IGNORED,
.dst_queue_family_index = vk.QUEUE_FAMILY_IGNORED,
.image = self.swapchain.depth_texture.?.image,
.image = swapchain.depth_texture.?.image,
.subresource_range = .{
.aspect_mask = .{ .depth_bit = true },
.base_mip_level = 0,
@@ -975,7 +975,7 @@ fn render(self: *Game) !void {
},
},
.depth_attachment = .{
.image_view = self.swapchain.depth_texture.?.image_view,
.image_view = swapchain.depth_texture.?.image_view,
.image_layout = .depth_attachment_optimal,
.load_op = .clear,
.store_op = .dont_care,
@@ -1026,7 +1026,7 @@ fn render(self: *Game) !void {
// --- RENDER GUI ---
try self.gui.draw(self.engine, command_buffer);
try self.gui.draw(command_buffer);
// --- FINALIZE ---
@@ -1056,11 +1056,11 @@ fn render(self: *Game) !void {
}
try command_buffer.endCommandBuffer();
self.swapchain.present(self.engine, .{
swapchain.present(.{
.command_buffer = command_buffer,
.wait_semaphores = &.{
.{
.semaphore = self.global_uniforms_transfer_semaphores[self.swapchain.image_index.?],
.semaphore = self.global_uniforms_transfer_semaphores[swapchain.image_index.?],
.stage_flags = .{ .vertex_shader_bit = true },
},
},
@@ -1071,10 +1071,13 @@ fn render(self: *Game) !void {
}
fn recreateSwapchain(self: *Game) !void {
try self.swapchain.recreate(self.engine);
const engine = ctx.engine;
const swapchain = ctx.swapchain;
try swapchain.recreate();
for (self.global_uniforms_transfer_semaphores, 0..) |*semaphore, i| {
self.engine.destroySemaphore(semaphore.*);
semaphore.* = try self.engine.createSemaphore();
self.engine.setObjectName(semaphore.*, "S Transfer[{d}]", .{i});
engine.destroySemaphore(semaphore.*);
semaphore.* = try engine.createSemaphore();
engine.setObjectName(semaphore.*, "S Transfer[{d}]", .{i});
}
}