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

20
src/AppContext.zig Normal file
View File

@@ -0,0 +1,20 @@
const AppContext = @This();
const std = @import("std");
const glfw = @import("zglfw");
const Atoms = @import("engine/Atoms.zig");
const Engine = @import("engine/Engine.zig");
const Swapchain = @import("engine/Swapchain.zig");
const VkAllocator = @import("engine/VkAllocator.zig");
pub var allocator_general: std.mem.Allocator = undefined;
pub var allocator_frame: std.mem.Allocator = undefined;
pub var allocator_persistent: std.mem.Allocator = undefined;
pub var io: std.Io = undefined;
pub var vk_allocator: *VkAllocator = undefined;
pub var window: *glfw.Window = undefined;
pub var atoms: *Atoms = undefined;
pub var engine: *Engine = undefined;
pub var swapchain: *Swapchain = undefined;

View File

@@ -2,6 +2,7 @@ const Chunks = @This();
const std = @import("std"); const std = @import("std");
const c = @import("const.zig"); const c = @import("const.zig");
const ctx = @import("AppContext.zig");
const math = @import("math.zig"); const math = @import("math.zig");
const vk = @import("vulkan"); const vk = @import("vulkan");
const vm = @import("vecmath"); const vm = @import("vecmath");
@@ -24,12 +25,14 @@ pub const RaycastHit = struct {
side: voxels.Orientation, side: voxels.Orientation,
}; };
pub fn deinit(self: *Chunks, engine: *Engine, descriptor_pool: vk.DescriptorPool, allocator: std.mem.Allocator) void { pub fn deinit(self: *Chunks, descriptor_pool: vk.DescriptorPool) void {
const allocator_general = ctx.allocator_general;
var it = self.chunks.valueIterator(); var it = self.chunks.valueIterator();
while (it.next()) |chunk| { while (it.next()) |chunk| {
chunk.deinit(engine, descriptor_pool); chunk.deinit(descriptor_pool);
} }
self.chunks.deinit(allocator); self.chunks.deinit(allocator_general);
self.* = undefined; self.* = undefined;
} }
@@ -58,11 +61,9 @@ pub fn setVoxelAt(
self: *Chunks, self: *Chunks,
vx: vm.Vector3Int, vx: vm.Vector3Int,
id: Blocks.Id, id: Blocks.Id,
engine: *Engine,
blocks: *const Blocks, blocks: *const Blocks,
descriptor_pool: vk.DescriptorPool, descriptor_pool: vk.DescriptorPool,
per_batch_descriptor_set_layout: vk.DescriptorSetLayout, per_batch_descriptor_set_layout: vk.DescriptorSetLayout,
allocator: std.mem.Allocator,
) !void { ) !void {
const min_ck = vm.Vector3Int.initScalar(std.math.minInt(i16)); const min_ck = vm.Vector3Int.initScalar(std.math.minInt(i16));
const max_ck = vm.Vector3Int.initScalar(std.math.maxInt(i16)); const max_ck = vm.Vector3Int.initScalar(std.math.maxInt(i16));
@@ -76,91 +77,91 @@ pub fn setVoxelAt(
const y: i16 = @intCast(ck.y); const y: i16 = @intCast(ck.y);
const z: i16 = @intCast(ck.z); const z: i16 = @intCast(ck.z);
const chunk = try self.getOrCreateChunk(ck, engine, descriptor_pool, per_batch_descriptor_set_layout, allocator); const chunk = try self.getOrCreateChunk(ck, descriptor_pool, per_batch_descriptor_set_layout);
const ckvx = vx.modScalar(c.vx_per_ck); const ckvx = vx.modScalar(c.vx_per_ck);
const block = &chunk.blocks[@intCast(ckvx.z)][@intCast(ckvx.y)][@intCast(ckvx.x)]; const block = &chunk.blocks[@intCast(ckvx.z)][@intCast(ckvx.y)][@intCast(ckvx.x)];
if (block.* != id) { if (block.* != id) {
block.* = id; block.* = id;
try chunk.refresh(engine, blocks, .{ try chunk.refresh(blocks, .{
.positive_x = self.chunks.getPtr(.{ x + 1, y, z }), .positive_x = self.chunks.getPtr(.{ x + 1, y, z }),
.negative_x = self.chunks.getPtr(.{ x - 1, y, z }), .negative_x = self.chunks.getPtr(.{ x - 1, y, z }),
.positive_y = self.chunks.getPtr(.{ x, y + 1, z }), .positive_y = self.chunks.getPtr(.{ x, y + 1, z }),
.negative_y = self.chunks.getPtr(.{ x, y - 1, z }), .negative_y = self.chunks.getPtr(.{ x, y - 1, z }),
.positive_z = self.chunks.getPtr(.{ x, y, z + 1 }), .positive_z = self.chunks.getPtr(.{ x, y, z + 1 }),
.negative_z = self.chunks.getPtr(.{ x, y, z - 1 }), .negative_z = self.chunks.getPtr(.{ x, y, z - 1 }),
}, allocator); });
if (ckvx.x == 0) { if (ckvx.x == 0) {
if (self.chunks.getPtr(.{ x - 1, y, z })) |neighbor| { if (self.chunks.getPtr(.{ x - 1, y, z })) |neighbor| {
try neighbor.refresh(engine, blocks, .{ try neighbor.refresh(blocks, .{
.positive_x = self.chunks.getPtr(.{ x, y, z }), .positive_x = self.chunks.getPtr(.{ x, y, z }),
.negative_x = self.chunks.getPtr(.{ x - 2, y, z }), .negative_x = self.chunks.getPtr(.{ x - 2, y, z }),
.positive_y = self.chunks.getPtr(.{ x - 1, y + 1, z }), .positive_y = self.chunks.getPtr(.{ x - 1, y + 1, z }),
.negative_y = self.chunks.getPtr(.{ x - 1, y - 1, z }), .negative_y = self.chunks.getPtr(.{ x - 1, y - 1, z }),
.positive_z = self.chunks.getPtr(.{ x - 1, y, z + 1 }), .positive_z = self.chunks.getPtr(.{ x - 1, y, z + 1 }),
.negative_z = self.chunks.getPtr(.{ x - 1, y, z - 1 }), .negative_z = self.chunks.getPtr(.{ x - 1, y, z - 1 }),
}, allocator); });
} }
} else if (ckvx.x == c.vx_per_ck - 1) { } else if (ckvx.x == c.vx_per_ck - 1) {
if (self.chunks.getPtr(.{ x + 1, y, z })) |neighbor| { if (self.chunks.getPtr(.{ x + 1, y, z })) |neighbor| {
try neighbor.refresh(engine, blocks, .{ try neighbor.refresh(blocks, .{
.positive_x = self.chunks.getPtr(.{ x + 2, y, z }), .positive_x = self.chunks.getPtr(.{ x + 2, y, z }),
.negative_x = self.chunks.getPtr(.{ x, y, z }), .negative_x = self.chunks.getPtr(.{ x, y, z }),
.positive_y = self.chunks.getPtr(.{ x + 1, y + 1, z }), .positive_y = self.chunks.getPtr(.{ x + 1, y + 1, z }),
.negative_y = self.chunks.getPtr(.{ x + 1, y - 1, z }), .negative_y = self.chunks.getPtr(.{ x + 1, y - 1, z }),
.positive_z = self.chunks.getPtr(.{ x + 1, y, z + 1 }), .positive_z = self.chunks.getPtr(.{ x + 1, y, z + 1 }),
.negative_z = self.chunks.getPtr(.{ x + 1, y, z - 1 }), .negative_z = self.chunks.getPtr(.{ x + 1, y, z - 1 }),
}, allocator); });
} }
} }
if (ckvx.y == 0) { if (ckvx.y == 0) {
if (self.chunks.getPtr(.{ x, y - 1, z })) |neighbor| { if (self.chunks.getPtr(.{ x, y - 1, z })) |neighbor| {
try neighbor.refresh(engine, blocks, .{ try neighbor.refresh(blocks, .{
.positive_x = self.chunks.getPtr(.{ x + 1, y - 1, z }), .positive_x = self.chunks.getPtr(.{ x + 1, y - 1, z }),
.negative_x = self.chunks.getPtr(.{ x - 1, y - 1, z }), .negative_x = self.chunks.getPtr(.{ x - 1, y - 1, z }),
.positive_y = self.chunks.getPtr(.{ x, y, z }), .positive_y = self.chunks.getPtr(.{ x, y, z }),
.negative_y = self.chunks.getPtr(.{ x, y - 2, z }), .negative_y = self.chunks.getPtr(.{ x, y - 2, z }),
.positive_z = self.chunks.getPtr(.{ x, y - 1, z + 1 }), .positive_z = self.chunks.getPtr(.{ x, y - 1, z + 1 }),
.negative_z = self.chunks.getPtr(.{ x, y - 1, z - 1 }), .negative_z = self.chunks.getPtr(.{ x, y - 1, z - 1 }),
}, allocator); });
} }
} else if (ckvx.y == c.vx_per_ck - 1) { } else if (ckvx.y == c.vx_per_ck - 1) {
if (self.chunks.getPtr(.{ x, y + 1, z })) |neighbor| { if (self.chunks.getPtr(.{ x, y + 1, z })) |neighbor| {
try neighbor.refresh(engine, blocks, .{ try neighbor.refresh(blocks, .{
.positive_x = self.chunks.getPtr(.{ x + 1, y + 1, z }), .positive_x = self.chunks.getPtr(.{ x + 1, y + 1, z }),
.negative_x = self.chunks.getPtr(.{ x - 1, y + 1, z }), .negative_x = self.chunks.getPtr(.{ x - 1, y + 1, z }),
.positive_y = self.chunks.getPtr(.{ x, y + 2, z }), .positive_y = self.chunks.getPtr(.{ x, y + 2, z }),
.negative_y = self.chunks.getPtr(.{ x, y, z }), .negative_y = self.chunks.getPtr(.{ x, y, z }),
.positive_z = self.chunks.getPtr(.{ x, y + 1, z + 1 }), .positive_z = self.chunks.getPtr(.{ x, y + 1, z + 1 }),
.negative_z = self.chunks.getPtr(.{ x, y + 1, z - 1 }), .negative_z = self.chunks.getPtr(.{ x, y + 1, z - 1 }),
}, allocator); });
} }
} }
if (ckvx.z == 0) { if (ckvx.z == 0) {
if (self.chunks.getPtr(.{ x, y, z - 1 })) |neighbor| { if (self.chunks.getPtr(.{ x, y, z - 1 })) |neighbor| {
try neighbor.refresh(engine, blocks, .{ try neighbor.refresh(blocks, .{
.positive_x = self.chunks.getPtr(.{ x + 1, y, z - 1 }), .positive_x = self.chunks.getPtr(.{ x + 1, y, z - 1 }),
.negative_x = self.chunks.getPtr(.{ x - 1, y, z - 1 }), .negative_x = self.chunks.getPtr(.{ x - 1, y, z - 1 }),
.positive_y = self.chunks.getPtr(.{ x, y + 1, z - 1 }), .positive_y = self.chunks.getPtr(.{ x, y + 1, z - 1 }),
.negative_y = self.chunks.getPtr(.{ x, y - 1, z - 1 }), .negative_y = self.chunks.getPtr(.{ x, y - 1, z - 1 }),
.positive_z = self.chunks.getPtr(.{ x, y, z }), .positive_z = self.chunks.getPtr(.{ x, y, z }),
.negative_z = self.chunks.getPtr(.{ x, y, z - 2 }), .negative_z = self.chunks.getPtr(.{ x, y, z - 2 }),
}, allocator); });
} }
} else if (ckvx.z == c.vx_per_ck - 1) { } else if (ckvx.z == c.vx_per_ck - 1) {
if (self.chunks.getPtr(.{ x, y, z + 1 })) |neighbor| { if (self.chunks.getPtr(.{ x, y, z + 1 })) |neighbor| {
try neighbor.refresh(engine, blocks, .{ try neighbor.refresh(blocks, .{
.positive_x = self.chunks.getPtr(.{ x + 1, y, z + 1 }), .positive_x = self.chunks.getPtr(.{ x + 1, y, z + 1 }),
.negative_x = self.chunks.getPtr(.{ x - 1, y, z + 1 }), .negative_x = self.chunks.getPtr(.{ x - 1, y, z + 1 }),
.positive_y = self.chunks.getPtr(.{ x, y + 1, z + 1 }), .positive_y = self.chunks.getPtr(.{ x, y + 1, z + 1 }),
.negative_y = self.chunks.getPtr(.{ x, y - 1, z + 1 }), .negative_y = self.chunks.getPtr(.{ x, y - 1, z + 1 }),
.positive_z = self.chunks.getPtr(.{ x, y, z + 2 }), .positive_z = self.chunks.getPtr(.{ x, y, z + 2 }),
.negative_z = self.chunks.getPtr(.{ x, y, z }), .negative_z = self.chunks.getPtr(.{ x, y, z }),
}, allocator); });
} }
} }
} }
@@ -667,11 +668,11 @@ pub fn raycast(self: *const Chunks, origin_sv: vm.Vector3Int, ray_sv: vm.Vector3
fn getOrCreateChunk( fn getOrCreateChunk(
self: *Chunks, self: *Chunks,
ck: vm.Vector3Int, ck: vm.Vector3Int,
engine: *Engine,
descriptor_pool: vk.DescriptorPool, descriptor_pool: vk.DescriptorPool,
per_batch_descriptor_set_layout: vk.DescriptorSetLayout, per_batch_descriptor_set_layout: vk.DescriptorSetLayout,
allocator: std.mem.Allocator,
) !*Chunk { ) !*Chunk {
const allocator_general = ctx.allocator_general;
const min_ck = vm.Vector3Int.initScalar(std.math.minInt(i16)); const min_ck = vm.Vector3Int.initScalar(std.math.minInt(i16));
const max_ck = vm.Vector3Int.initScalar(std.math.maxInt(i16)); const max_ck = vm.Vector3Int.initScalar(std.math.maxInt(i16));
std.debug.assert((ck.x >= min_ck.x) | (ck.y >= min_ck.y) | (ck.z >= min_ck.z) | (ck.x <= max_ck.x) | (ck.y <= max_ck.y) | (ck.z <= max_ck.z)); std.debug.assert((ck.x >= min_ck.x) | (ck.y >= min_ck.y) | (ck.z >= min_ck.z) | (ck.x <= max_ck.x) | (ck.y <= max_ck.y) | (ck.z <= max_ck.z));
@@ -682,7 +683,7 @@ fn getOrCreateChunk(
@intCast(ck.z), @intCast(ck.z),
}; };
const entry = try self.chunks.getOrPut(allocator, key); const entry = try self.chunks.getOrPut(allocator_general, key);
if (entry.found_existing) { if (entry.found_existing) {
return entry.value_ptr; return entry.value_ptr;
@@ -690,7 +691,7 @@ fn getOrCreateChunk(
errdefer _ = self.chunks.remove(key); errdefer _ = self.chunks.remove(key);
const origin = ck.asFloat().mulScalar(c.vx_per_ck); const origin = ck.asFloat().mulScalar(c.vx_per_ck);
const chunk = try Chunk.init(engine, .{ const chunk = try Chunk.init(.{
.origin = origin, .origin = origin,
.descriptor_pool = descriptor_pool, .descriptor_pool = descriptor_pool,
.per_batch_descriptor_set_layout = per_batch_descriptor_set_layout, .per_batch_descriptor_set_layout = per_batch_descriptor_set_layout,

View File

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

View File

@@ -1,6 +1,7 @@
const Gui = @This(); const Gui = @This();
const std = @import("std"); const std = @import("std");
const ctx = @import("AppContext.zig");
const shaders = @import("shaders.zig"); const shaders = @import("shaders.zig");
const vk = @import("vulkan"); const vk = @import("vulkan");
const vm = @import("vecmath"); const vm = @import("vecmath");
@@ -74,46 +75,47 @@ pub const max_text_draws = 4096;
pub const max_batches = 1024; pub const max_batches = 1024;
pub fn init( pub fn init(
allocator: std.mem.Allocator,
engine: *Engine,
swapchain: *Swapchain,
global_uniforms_buffer: vk.Buffer, global_uniforms_buffer: vk.Buffer,
) !Gui { ) !Gui {
var box_gpu_buffer: GenericBuffer(void, Draw.Box) = try .init(engine, .{ const allocator_general = ctx.allocator_general;
const engine = ctx.engine;
const swapchain = ctx.swapchain;
var box_gpu_buffer: GenericBuffer(void, Draw.Box) = try .init(.{
.usage = .storage, .usage = .storage,
.target_queue = .graphics, .target_queue = .graphics,
.array_capacity = max_box_draws, .array_capacity = max_box_draws,
.name = "GUI box draws", .name = "GUI box draws",
}); });
errdefer box_gpu_buffer.deinit(engine); errdefer box_gpu_buffer.deinit();
var text_gpu_buffer: GenericBuffer(void, Draw.Text) = try .init(engine, .{ var text_gpu_buffer: GenericBuffer(void, Draw.Text) = try .init(.{
.usage = .storage, .usage = .storage,
.target_queue = .graphics, .target_queue = .graphics,
.array_capacity = max_text_draws, .array_capacity = max_text_draws,
.name = "GUI text draws", .name = "GUI text draws",
}); });
errdefer text_gpu_buffer.deinit(engine); errdefer text_gpu_buffer.deinit();
var image_gpu_buffer: GenericBuffer(void, Draw.Image) = try .init(engine, .{ var image_gpu_buffer: GenericBuffer(void, Draw.Image) = try .init(.{
.usage = .storage, .usage = .storage,
.target_queue = .graphics, .target_queue = .graphics,
.array_capacity = max_image_draws, .array_capacity = max_image_draws,
.name = "GUI image draws", .name = "GUI image draws",
}); });
errdefer image_gpu_buffer.deinit(engine); errdefer image_gpu_buffer.deinit();
var box_cpu_buffer: std.ArrayList(Draw.Box) = try .initCapacity(allocator, max_box_draws); var box_cpu_buffer: std.ArrayList(Draw.Box) = try .initCapacity(allocator_general, max_box_draws);
errdefer box_cpu_buffer.deinit(allocator); errdefer box_cpu_buffer.deinit(allocator_general);
var text_cpu_buffer: std.ArrayList(Draw.Text) = try .initCapacity(allocator, max_text_draws); var text_cpu_buffer: std.ArrayList(Draw.Text) = try .initCapacity(allocator_general, max_text_draws);
errdefer text_cpu_buffer.deinit(allocator); errdefer text_cpu_buffer.deinit(allocator_general);
var image_cpu_buffer: std.ArrayList(Draw.Image) = try .initCapacity(allocator, max_image_draws); var image_cpu_buffer: std.ArrayList(Draw.Image) = try .initCapacity(allocator_general, max_image_draws);
errdefer image_cpu_buffer.deinit(allocator); errdefer image_cpu_buffer.deinit(allocator_general);
var batches: std.ArrayList(Batch) = try .initCapacity(allocator, max_batches); var batches: std.ArrayList(Batch) = try .initCapacity(allocator_general, max_batches);
errdefer batches.deinit(allocator); errdefer batches.deinit(allocator_general);
const sampler = try engine.createSampler(.{ const sampler = try engine.createSampler(.{
.mag_filter = .linear, .mag_filter = .linear,
@@ -169,14 +171,14 @@ pub fn init(
defer engine.destroyShaderModule(box_fragment_shader); defer engine.destroyShaderModule(box_fragment_shader);
engine.setObjectName(box_fragment_shader, "SM gui_box_frag", .{}); engine.setObjectName(box_fragment_shader, "SM gui_box_frag", .{});
var index_buffer = try shaders.IndexBuffer.init(engine, .{ var index_buffer = try shaders.IndexBuffer.init(.{
.usage = .index, .usage = .index,
.target_queue = .graphics, .target_queue = .graphics,
.array_capacity = 6, .array_capacity = 6,
.name = "QuadIB", .name = "QuadIB",
}); });
errdefer index_buffer.deinit(engine); errdefer index_buffer.deinit();
try index_buffer.write(engine, .{ try index_buffer.write(.{
.elements = &.{ 0, 1, 2, 2, 1, 3 }, .elements = &.{ 0, 1, 2, 2, 1, 3 },
}); });
@@ -362,25 +364,28 @@ pub fn init(
}; };
} }
pub fn deinit(self: *Gui, engine: *Engine, allocator: std.mem.Allocator) void { pub fn deinit(self: *Gui) void {
std.log.scoped(.deinit).debug("Deinitializing {*} with {*} and Allocator{{{*},{*}}}", .{ self, engine, allocator.ptr, allocator.vtable }); const allocator_general = ctx.allocator_general;
const engine = ctx.engine;
std.log.scoped(.deinit).debug("Deinitializing {*}", .{self});
engine.destroyDescriptorPool(self.descriptor_pool); engine.destroyDescriptorPool(self.descriptor_pool);
engine.destroyPipeline(self.box_pipeline); engine.destroyPipeline(self.box_pipeline);
self.index_buffer.deinit(engine); self.index_buffer.deinit();
engine.destroyPipelineLayout(self.box_pipeline_layout); engine.destroyPipelineLayout(self.box_pipeline_layout);
engine.destroyDescriptorSetLayout(self.box_descriptor_set_layout); engine.destroyDescriptorSetLayout(self.box_descriptor_set_layout);
engine.destroySampler(self.sampler); engine.destroySampler(self.sampler);
self.batches.deinit(allocator); self.batches.deinit(allocator_general);
self.image_cpu_buffer.deinit(allocator); self.image_cpu_buffer.deinit(allocator_general);
self.text_cpu_buffer.deinit(allocator); self.text_cpu_buffer.deinit(allocator_general);
self.box_cpu_buffer.deinit(allocator); self.box_cpu_buffer.deinit(allocator_general);
self.image_gpu_buffer.deinit(engine); self.image_gpu_buffer.deinit();
self.text_gpu_buffer.deinit(engine); self.text_gpu_buffer.deinit();
self.box_gpu_buffer.deinit(engine); self.box_gpu_buffer.deinit();
self.* = undefined; self.* = undefined;
} }
@@ -425,12 +430,12 @@ fn extendOrAddBatch(self: *Gui, draw_type: std.meta.DeclEnum(Draw), instance: u3
} }
} }
pub fn draw(self: *const Gui, engine: *Engine, command_buffer: CommandBuffer) !void { pub fn draw(self: *const Gui, command_buffer: CommandBuffer) !void {
command_buffer.bindIndexBuffer(self.index_buffer.buffer, 0, .uint16); command_buffer.bindIndexBuffer(self.index_buffer.buffer, 0, .uint16);
try self.box_gpu_buffer.write(engine, .{ .elements = self.box_cpu_buffer.items }); try self.box_gpu_buffer.write(.{ .elements = self.box_cpu_buffer.items });
try self.text_gpu_buffer.write(engine, .{ .elements = self.text_cpu_buffer.items }); try self.text_gpu_buffer.write(.{ .elements = self.text_cpu_buffer.items });
try self.image_gpu_buffer.write(engine, .{ .elements = self.image_cpu_buffer.items }); try self.image_gpu_buffer.write(.{ .elements = self.image_cpu_buffer.items });
for (self.batches.items) |batch| { for (self.batches.items) |batch| {
switch (batch.draw_type) { switch (batch.draw_type) {

View File

@@ -253,11 +253,9 @@ pub fn onMouseDown(self: *Player, button: glfw.MouseButton, game: *Game) void {
game.chunks.setVoxelAt( game.chunks.setVoxelAt(
raycast_hit.voxel, raycast_hit.voxel,
.air, .air,
game.engine,
&game.blocks, &game.blocks,
game.descriptor_pool, game.descriptor_pool,
game.per_batch_descriptor_set_layout, game.per_batch_descriptor_set_layout,
game.allocator,
) catch |err| { ) catch |err| {
std.log.err("Error while destroying voxel {f}: {}", .{ raycast_hit.voxel, err }); std.log.err("Error while destroying voxel {f}: {}", .{ raycast_hit.voxel, err });
}; };
@@ -265,13 +263,10 @@ pub fn onMouseDown(self: *Player, button: glfw.MouseButton, game: *Game) void {
.right => blk: { .right => blk: {
const target_vx = raycast_hit.voxel.add(raycast_hit.side.getSignVector()); const target_vx = raycast_hit.voxel.add(raycast_hit.side.getSignVector());
const id = game.blocks.getOrLoad( const id = game.blocks.getOrLoad(
game.engine,
&game.materials, &game.materials,
&game.textures, &game.textures,
&game.stbi, &game.stbi,
blocks[self.block_index], blocks[self.block_index],
game.allocator,
game.io,
) catch |err| { ) catch |err| {
std.log.err("Error while placing voxel at {f}: {}", .{ target_vx, err }); std.log.err("Error while placing voxel at {f}: {}", .{ target_vx, err });
break :blk; break :blk;
@@ -279,11 +274,9 @@ pub fn onMouseDown(self: *Player, button: glfw.MouseButton, game: *Game) void {
game.chunks.setVoxelAt( game.chunks.setVoxelAt(
target_vx, target_vx,
id, id,
game.engine,
&game.blocks, &game.blocks,
game.descriptor_pool, game.descriptor_pool,
game.per_batch_descriptor_set_layout, game.per_batch_descriptor_set_layout,
game.allocator,
) catch |err| { ) catch |err| {
std.log.err("Error while placing voxel at {f}: {}", .{ target_vx, err }); std.log.err("Error while placing voxel at {f}: {}", .{ target_vx, err });
}; };

View File

@@ -3,6 +3,7 @@
const Blocks = @This(); const Blocks = @This();
const std = @import("std"); const std = @import("std");
const ctx = @import("../AppContext.zig");
const media = @import("media"); const media = @import("media");
const voxels = @import("../voxels.zig"); const voxels = @import("../voxels.zig");
@@ -76,23 +77,26 @@ pub const Definition = struct {
} }
}; };
/// Maps a key value to a block definition ID. /// Maps a key value to a block definition ID. Preallocated with
/// `allocator_general`.
map: std.AutoHashMapUnmanaged(Key, Id), map: std.AutoHashMapUnmanaged(Key, Id),
/// Stores all `Definition` structs and maps a block definition ID to a /// Stores all `Definition` structs and maps a block definition ID to a
/// `Definition` struct. /// `Definition` struct. Preallocated with `allocator_general`.
array: std.ArrayList(Definition), array: std.ArrayList(Definition),
/// With `@sizeOf(Definition) == 24` and `max_blocks = 4096`, the block /// With `@sizeOf(Definition) == 24` and `max_blocks = 4096`, the block
/// definitions should take ~95.4 kiB in RAM. /// definitions should take ~95.4 kiB in RAM.
pub const max_blocks = 4096; pub const max_blocks = 4096;
pub fn init(allocator: std.mem.Allocator) !Blocks { pub fn init() !Blocks {
var map: std.AutoHashMapUnmanaged(Key, Id) = .empty; const allocator_general = ctx.allocator_general;
errdefer map.deinit(allocator);
try map.ensureTotalCapacity(allocator, max_blocks);
var array: std.ArrayList(Definition) = try .initCapacity(allocator, max_blocks); var map: std.AutoHashMapUnmanaged(Key, Id) = .empty;
errdefer array.deinit(allocator); errdefer map.deinit(allocator_general);
try map.ensureTotalCapacity(allocator_general, max_blocks);
var array: std.ArrayList(Definition) = try .initCapacity(allocator_general, max_blocks);
errdefer array.deinit(allocator_general);
// VOLATILE Synchronize with explicit values on top of `Id` type. // VOLATILE Synchronize with explicit values on top of `Id` type.
@@ -104,11 +108,13 @@ pub fn init(allocator: std.mem.Allocator) !Blocks {
}; };
} }
pub fn deinit(self: *Blocks, allocator: std.mem.Allocator) void { pub fn deinit(self: *Blocks) void {
std.log.scoped(.deinit).debug("Deinitializing {*} with Allocator{{{*},{*}}}", .{ self, allocator.ptr, allocator.vtable }); const allocator_general = ctx.allocator_general;
self.array.deinit(allocator); std.log.scoped(.deinit).debug("Deinitializing {*}", .{self});
self.map.deinit(allocator);
self.array.deinit(allocator_general);
self.map.deinit(allocator_general);
self.* = undefined; self.* = undefined;
} }
@@ -132,18 +138,15 @@ pub fn getAtom(self: *const Blocks, filename: Atom) ?Id {
pub fn getOrLoad( pub fn getOrLoad(
self: *Blocks, self: *Blocks,
engine: *Engine,
materials: *Materials, materials: *Materials,
textures: *Textures, textures: *Textures,
stbi: *media.stbi, stbi: *media.stbi,
filename: []const u8, filename: []const u8,
temp_allocator: std.mem.Allocator,
io: std.Io,
) !Id { ) !Id {
const key: Key = .{ const key: Key = .{
// If the material already exists, then the atom must exist and the // If the material already exists, then the atom must exist and the
// following line will not return any error. // following line will not return any error.
.filename = try .fromString(filename, io), .filename = try .fromString(filename),
}; };
// We don't use `getOrPutAssumeCapacity` method, because we might already be // We don't use `getOrPutAssumeCapacity` method, because we might already be
@@ -155,7 +158,7 @@ pub fn getOrLoad(
const id = Id.fromIndexSafe(self.array.items.len) catch |err| switch (err) { const id = Id.fromIndexSafe(self.array.items.len) catch |err| switch (err) {
error.Overflow => return error.OutOfBlocks, error.Overflow => return error.OutOfBlocks,
}; };
const def = try loadBlock(engine, materials, textures, stbi, filename, temp_allocator, io); const def = try loadBlock(materials, textures, stbi, filename);
self.map.putAssumeCapacityNoClobber(key, id); self.map.putAssumeCapacityNoClobber(key, id);
self.array.appendAssumeCapacity(def); self.array.appendAssumeCapacity(def);
@@ -166,13 +169,10 @@ pub fn getOrLoad(
pub fn getOrLoadAtom( pub fn getOrLoadAtom(
self: *Blocks, self: *Blocks,
engine: *Engine,
materials: *Materials, materials: *Materials,
textures: *Textures, textures: *Textures,
stbi: *media.stbi, stbi: *media.stbi,
filename: Atom, filename: Atom,
temp_allocator: std.mem.Allocator,
io: std.Io,
) !Id { ) !Id {
const key: Key = .{ const key: Key = .{
.filename = filename, .filename = filename,
@@ -187,7 +187,7 @@ pub fn getOrLoadAtom(
const id = Id.fromIndexSafe(self.array.items.len) catch |err| switch (err) { const id = Id.fromIndexSafe(self.array.items.len) catch |err| switch (err) {
error.Overflow => return error.OutOfBlocks, error.Overflow => return error.OutOfBlocks,
}; };
const def = try loadBlock(engine, materials, textures, stbi, filename.toString(), temp_allocator, io); const def = try loadBlock(materials, textures, stbi, filename.toString());
self.map.putAssumeCapacityNoClobber(key, id); self.map.putAssumeCapacityNoClobber(key, id);
self.array.appendAssumeCapacity(def); self.array.appendAssumeCapacity(def);
@@ -198,13 +198,12 @@ pub fn getOrLoadAtom(
pub fn loadAll( pub fn loadAll(
self: *Blocks, self: *Blocks,
engine: *Engine,
materials: *Materials, materials: *Materials,
textures: *Textures, textures: *Textures,
stbi: *media.stbi, stbi: *media.stbi,
temp_allocator: std.mem.Allocator,
io: std.Io,
) void { ) void {
const io = ctx.io;
const cwd = std.Io.Dir.cwd(); const cwd = std.Io.Dir.cwd();
var dir = cwd.openDir(io, "assets/blocks", .{ .iterate = true }) catch |err| { var dir = cwd.openDir(io, "assets/blocks", .{ .iterate = true }) catch |err| {
@@ -223,21 +222,21 @@ pub fn loadAll(
continue; continue;
} }
_ = self.getOrLoad(engine, materials, textures, stbi, entry.name, temp_allocator, io) catch |err| { _ = self.getOrLoad(materials, textures, stbi, entry.name) catch |err| {
std.log.err("Error while loading block definition entry {s}: {s}", .{ entry.name, @errorName(err) }); std.log.err("Error while loading block definition entry {s}: {s}", .{ entry.name, @errorName(err) });
}; };
} }
} }
fn loadBlock( fn loadBlock(
engine: *Engine,
materials: *Materials, materials: *Materials,
textures: *Textures, textures: *Textures,
stbi: *media.stbi, stbi: *media.stbi,
filename: []const u8, filename: []const u8,
temp_allocator: std.mem.Allocator,
io: std.Io,
) !Definition { ) !Definition {
const allocator_frame = ctx.allocator_frame;
const io = ctx.io;
const DefinitionJson = struct { const DefinitionJson = struct {
pub const Wall = struct { pub const Wall = struct {
material: ?[]const u8 = null, material: ?[]const u8 = null,
@@ -264,17 +263,13 @@ fn loadBlock(
defer file.close(io); defer file.close(io);
var file_reader = file.reader(io, &buffer); var file_reader = file.reader(io, &buffer);
var json_reader = std.json.Reader.init(temp_allocator, &file_reader.interface); var json_reader = std.json.Reader.init(allocator_frame, &file_reader.interface);
defer json_reader.deinit();
const parsed: std.json.Parsed(DefinitionJson) = try std.json.parseFromTokenSource(DefinitionJson, temp_allocator, &json_reader, .{ const def_json = try std.json.parseFromTokenSourceLeaky(DefinitionJson, allocator_frame, &json_reader, .{
.duplicate_field_behavior = .@"error", .duplicate_field_behavior = .@"error",
.ignore_unknown_fields = false, .ignore_unknown_fields = false,
.allocate = .alloc_if_needed, .allocate = .alloc_if_needed,
}); });
defer parsed.deinit();
const def_json = parsed.value;
const block: Definition = blk: { const block: Definition = blk: {
if (def_json.material) |name| { if (def_json.material) |name| {
@@ -283,7 +278,7 @@ fn loadBlock(
return error.ParseError; return error.ParseError;
} }
const material = try materials.getOrLoad(engine, textures, stbi, name, temp_allocator, io); const material = try materials.getOrLoad(textures, stbi, name);
break :blk .initUniform(material); break :blk .initUniform(material);
} }
@@ -293,7 +288,7 @@ fn loadBlock(
var ret: Definition.Walls = undefined; var ret: Definition.Walls = undefined;
inline for (@typeInfo(voxels.Orientation).@"enum".fields) |field| { inline for (@typeInfo(voxels.Orientation).@"enum".fields) |field| {
@field(ret, field.name) = .{ @field(ret, field.name) = .{
.material = try materials.getOrLoad(engine, textures, stbi, @field(walls, field.name).material, temp_allocator, io), .material = try materials.getOrLoad(textures, stbi, @field(walls, field.name).material),
.transform = @field(walls, field.name).transform, .transform = @field(walls, field.name).transform,
}; };
} }

View File

@@ -1,6 +1,7 @@
const Chunk = @This(); const Chunk = @This();
const std = @import("std"); const std = @import("std");
const ctx = @import("../AppContext.zig");
const math = @import("../math.zig"); const math = @import("../math.zig");
const shaders = @import("../shaders.zig"); const shaders = @import("../shaders.zig");
const vk = @import("vulkan"); const vk = @import("vulkan");
@@ -40,19 +41,21 @@ pub const InitInfo = struct {
pub const Neighbors = std.enums.EnumFieldStruct(voxels.Orientation, ?*const Chunk, null); pub const Neighbors = std.enums.EnumFieldStruct(voxels.Orientation, ?*const Chunk, null);
pub fn init(engine: *Engine, init_info: InitInfo) !Chunk { pub fn init(init_info: InitInfo) !Chunk {
const engine = ctx.engine;
const descriptor_set = try engine.allocateDescriptorSet(.{ const descriptor_set = try engine.allocateDescriptorSet(.{
.descriptor_pool = init_info.descriptor_pool, .descriptor_pool = init_info.descriptor_pool,
.set_layout = init_info.per_batch_descriptor_set_layout, .set_layout = init_info.per_batch_descriptor_set_layout,
}); });
errdefer engine.freeDescriptorSet(init_info.descriptor_pool, descriptor_set); errdefer engine.freeDescriptorSet(init_info.descriptor_pool, descriptor_set);
var object_buffer: ObjectUniformsBuffer = try .init(engine, .{ var object_buffer: ObjectUniformsBuffer = try .init(.{
.usage = .storage, .usage = .storage,
.target_queue = .graphics, .target_queue = .graphics,
.array_capacity = initial_capacity, .array_capacity = initial_capacity,
}); });
errdefer object_buffer.deinit(engine); errdefer object_buffer.deinit();
try engine.updateDescriptorSets(.{ try engine.updateDescriptorSets(.{
.writes = &.{ .writes = &.{
@@ -89,18 +92,22 @@ pub fn init(engine: *Engine, init_info: InitInfo) !Chunk {
}; };
} }
pub fn deinit(self: *Chunk, engine: *Engine, descriptor_pool: vk.DescriptorPool) void { pub fn deinit(self: *Chunk, 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) }); const engine = ctx.engine;
self.object_buffer.deinit(engine); std.log.scoped(.deinit).debug("Deinitializing chunk {d} with {s}#{X}", .{ self.chunk_id, @typeName(vk.DescriptorPool), @intFromEnum(descriptor_pool) });
self.object_buffer.deinit();
engine.freeDescriptorSet(descriptor_pool, self.descriptor_set); engine.freeDescriptorSet(descriptor_pool, self.descriptor_set);
self.* = undefined; self.* = undefined;
} }
pub fn refresh(self: *Chunk, engine: *Engine, blocks: *const Blocks, neighbors: Neighbors, temp_allocator: std.mem.Allocator) !void { pub fn refresh(self: *Chunk, blocks: *const Blocks, neighbors: Neighbors) !void {
var uniforms: std.ArrayList(shaders.ObjectUniforms) = try .initCapacity(temp_allocator, initial_capacity); const allocator_frame = ctx.allocator_frame;
defer uniforms.deinit(temp_allocator); const engine = ctx.engine;
var uniforms: std.ArrayList(shaders.ObjectUniforms) = try .initCapacity(allocator_frame, initial_capacity);
for (self.blocks, 0..) |plane, iz| { for (self.blocks, 0..) |plane, iz| {
const fz: f32 = @floatFromInt(iz); const fz: f32 = @floatFromInt(iz);
@@ -116,7 +123,7 @@ pub fn refresh(self: *Chunk, engine: *Engine, blocks: *const Blocks, neighbors:
const material = @field(block.walls, field.name).material; const material = @field(block.walls, field.name).material;
if (material != .empty and self.getOpposite(side, ix, iy, iz, blocks, neighbors) == .empty) { if (material != .empty and self.getOpposite(side, ix, iy, iz, blocks, neighbors) == .empty) {
const matrix = getMatrix(side, origin); const matrix = getMatrix(side, origin);
try uniforms.append(temp_allocator, .{ try uniforms.append(allocator_frame, .{
.matrixOStoWS = matrix, .matrixOStoWS = matrix,
.matrixOStoWSNormal = matrix, .matrixOStoWSNormal = matrix,
.material = material, .material = material,
@@ -130,7 +137,7 @@ pub fn refresh(self: *Chunk, engine: *Engine, blocks: *const Blocks, neighbors:
const object_count: u32 = @intCast(uniforms.items.len); const object_count: u32 = @intCast(uniforms.items.len);
if (self.object_buffer.array_capacity < object_count) { if (self.object_buffer.array_capacity < object_count) {
const desired_capacity = std.math.ceilPowerOfTwoAssert(u32, object_count); const desired_capacity = std.math.ceilPowerOfTwoAssert(u32, object_count);
const new_object_buffer: ObjectUniformsBuffer = try .init(engine, .{ const new_object_buffer: ObjectUniformsBuffer = try .init(.{
.usage = .storage, .usage = .storage,
.target_queue = .graphics, .target_queue = .graphics,
.array_capacity = desired_capacity, .array_capacity = desired_capacity,
@@ -156,13 +163,13 @@ pub fn refresh(self: *Chunk, engine: *Engine, blocks: *const Blocks, neighbors:
}, },
}); });
self.object_buffer.deinit(engine); self.object_buffer.deinit();
self.object_buffer = new_object_buffer; self.object_buffer = new_object_buffer;
engine.setObjectName(new_object_buffer.buffer, "B Chunk[{d}]", .{self.chunk_id}); engine.setObjectName(new_object_buffer.buffer, "B Chunk[{d}]", .{self.chunk_id});
engine.setObjectName(new_object_buffer.device_memory, "DM Chunk[{d}]", .{self.chunk_id}); engine.setObjectName(new_object_buffer.device_memory, "DM Chunk[{d}]", .{self.chunk_id});
} }
try self.object_buffer.write(engine, .{ .elements = uniforms.items }); try self.object_buffer.write(.{ .elements = uniforms.items });
self.object_count = object_count; self.object_count = object_count;
} }

View File

@@ -3,6 +3,7 @@
const Materials = @This(); const Materials = @This();
const std = @import("std"); const std = @import("std");
const ctx = @import("../AppContext.zig");
const media = @import("media"); const media = @import("media");
const shaders = @import("../shaders.zig"); const shaders = @import("../shaders.zig");
const vk = @import("vulkan"); const vk = @import("vulkan");
@@ -50,7 +51,7 @@ pub const Key = struct {
filename: Atom, filename: Atom,
}; };
/// Maps a key value to a material ID. /// Maps a key value to a material ID. Preallocated with `allocator_general`.
map: std.AutoHashMapUnmanaged(Key, Id), map: std.AutoHashMapUnmanaged(Key, Id),
/// Stores all material data in a single contiguous storage buffer. Use the /// Stores all material data in a single contiguous storage buffer. Use the
/// material ID as an index into this buffer. /// material ID as an index into this buffer.
@@ -62,22 +63,24 @@ material_count: usize,
/// storage buffer should take 208 kiB in VRAM. /// storage buffer should take 208 kiB in VRAM.
pub const max_materials = 4096; pub const max_materials = 4096;
pub fn init(engine: *Engine, allocator: std.mem.Allocator) !Materials { pub fn init() !Materials {
var map: std.AutoHashMapUnmanaged(Key, Id) = .empty; const allocator_general = ctx.allocator_general;
errdefer map.deinit(allocator);
try map.ensureTotalCapacity(allocator, max_materials);
var material_buffer = try shaders.MaterialBuffer.init(engine, .{ var map: std.AutoHashMapUnmanaged(Key, Id) = .empty;
errdefer map.deinit(allocator_general);
try map.ensureTotalCapacity(allocator_general, max_materials);
var material_buffer = try shaders.MaterialBuffer.init(.{
.usage = .storage, .usage = .storage,
.target_queue = .graphics, .target_queue = .graphics,
.array_capacity = max_materials, .array_capacity = max_materials,
.name = "Materials", .name = "Materials",
}); });
errdefer material_buffer.deinit(engine); errdefer material_buffer.deinit();
// VOLATILE Synchronize with explicit values on top of `Id` type. // VOLATILE Synchronize with explicit values on top of `Id` type.
try material_buffer.write(engine, .{ try material_buffer.write(.{
.element_offset = Id.empty.toInt(), .element_offset = Id.empty.toInt(),
.elements = &.{ .elements = &.{
.{ .{
@@ -103,11 +106,13 @@ pub fn init(engine: *Engine, allocator: std.mem.Allocator) !Materials {
}; };
} }
pub fn deinit(self: *Materials, engine: *Engine, allocator: std.mem.Allocator) void { pub fn deinit(self: *Materials) void {
std.log.scoped(.deinit).debug("Deinitializing {*} with {*} and Allocator{{{*},{*}}}", .{ self, engine, allocator.ptr, allocator.vtable }); const allocator_general = ctx.allocator_general;
self.material_buffer.deinit(engine); std.log.scoped(.deinit).debug("Deinitializing {*}", .{self});
self.map.deinit(allocator);
self.material_buffer.deinit();
self.map.deinit(allocator_general);
self.* = undefined; self.* = undefined;
} }
@@ -151,18 +156,15 @@ pub fn getAtom(self: *const Materials, filename: Atom) ?Id {
/// error is not necessarily related to `temp_allocator`. /// error is not necessarily related to `temp_allocator`.
pub fn getOrLoad( pub fn getOrLoad(
self: *Materials, self: *Materials,
engine: *Engine,
textures: *Textures, textures: *Textures,
stbi: *media.stbi, stbi: *media.stbi,
maybe_filename: ?[]const u8, maybe_filename: ?[]const u8,
temp_allocator: std.mem.Allocator,
io: std.Io,
) !Id { ) !Id {
if (maybe_filename) |filename| { if (maybe_filename) |filename| {
const key: Key = .{ const key: Key = .{
// If the material already exists, then the atom must exist and the // If the material already exists, then the atom must exist and the
// following line will not return any error. // following line will not return any error.
.filename = try .fromString(filename, io), .filename = try .fromString(filename),
}; };
// We don't use `getOrPutAssumeCapacity` method, because we might already be // We don't use `getOrPutAssumeCapacity` method, because we might already be
@@ -174,7 +176,7 @@ pub fn getOrLoad(
const id = Id.fromIndexSafe(self.material_count) catch |err| switch (err) { const id = Id.fromIndexSafe(self.material_count) catch |err| switch (err) {
error.Overflow => return error.OutOfMaterials, error.Overflow => return error.OutOfMaterials,
}; };
try self.loadMaterial(engine, textures, stbi, filename, id.toInt(), temp_allocator, io); try self.loadMaterial(textures, stbi, filename, id.toInt());
self.map.putAssumeCapacityNoClobber(key, id); self.map.putAssumeCapacityNoClobber(key, id);
self.material_count += 1; self.material_count += 1;
@@ -199,12 +201,9 @@ pub fn getOrLoad(
/// error is not necessarily related to `temp_allocator`. /// error is not necessarily related to `temp_allocator`.
pub fn getOrLoadAtom( pub fn getOrLoadAtom(
self: *Materials, self: *Materials,
engine: *Engine,
textures: *Textures, textures: *Textures,
stbi: *media.stbi, stbi: *media.stbi,
filename: Atom, filename: Atom,
temp_allocator: std.mem.Allocator,
io: std.Io,
) !Id { ) !Id {
if (filename != .empty) { if (filename != .empty) {
const key: Key = .{ const key: Key = .{
@@ -220,7 +219,7 @@ pub fn getOrLoadAtom(
const id = Id.fromIndexSafe(self.material_count) catch |err| switch (err) { const id = Id.fromIndexSafe(self.material_count) catch |err| switch (err) {
error.Overflow => return error.OutOfMaterials, error.Overflow => return error.OutOfMaterials,
}; };
try self.loadMaterial(engine, textures, stbi, filename.toString(), id.toInt(), temp_allocator, io); try self.loadMaterial(textures, stbi, filename.toString(), id.toInt());
self.map.putAssumeCapacityNoClobber(key, id); self.map.putAssumeCapacityNoClobber(key, id);
self.material_count += 1; self.material_count += 1;
@@ -242,14 +241,20 @@ pub fn getOrLoadAtom(
/// deinitialized or reset after this function returns. Note that during loading /// deinitialized or reset after this function returns. Note that during loading
/// the engine will make its own persistent allocations, so an out of memory /// the engine will make its own persistent allocations, so an out of memory
/// error is not necessarily related to `temp_allocator`. /// error is not necessarily related to `temp_allocator`.
pub fn loadAll(self: *Materials, engine: *Engine, textures: *Textures, stbi: *media.stbi, temp_allocator: std.mem.Allocator) void { pub fn loadAll(
const cwd = std.fs.cwd(); self: *Materials,
textures: *Textures,
stbi: *media.stbi,
) void {
const io = ctx.io;
var dir = cwd.openDir("assets/materials", .{ .iterate = true }) catch |err| { const cwd = std.Io.Dir.cwd();
var dir = cwd.openDir(io, "assets/materials", .{ .iterate = true }) catch |err| {
std.log.err("Error while opening metarials directory: {s}", .{@errorName(err)}); std.log.err("Error while opening metarials directory: {s}", .{@errorName(err)});
return; return;
}; };
defer dir.close(); defer dir.close(io);
var it = dir.iterate(); var it = dir.iterate();
while (it.next() catch |err| { while (it.next() catch |err| {
@@ -261,13 +266,16 @@ pub fn loadAll(self: *Materials, engine: *Engine, textures: *Textures, stbi: *me
continue; continue;
} }
_ = self.getOrLoad(engine, textures, stbi, entry.name, temp_allocator) catch |err| { _ = self.getOrLoad(textures, stbi, entry.name) catch |err| {
std.log.err("Error while loading material entry {s}: {s}", .{ entry.name, @errorName(err) }); std.log.err("Error while loading material entry {s}: {s}", .{ entry.name, @errorName(err) });
}; };
} }
} }
fn loadMaterial(self: *Materials, engine: *Engine, textures: *Textures, stbi: *media.stbi, filename: []const u8, index: u32, temp_allocator: std.mem.Allocator, io: std.Io) !void { fn loadMaterial(self: *Materials, textures: *Textures, stbi: *media.stbi, filename: []const u8, index: u32) !void {
const allocator_frame = ctx.allocator_frame;
const io = ctx.io;
const MaterialJson = struct { const MaterialJson = struct {
baseColor: [3]f32 = .{ 1, 1, 1 }, baseColor: [3]f32 = .{ 1, 1, 1 },
baseColorTexture: ?[]const u8 = null, baseColorTexture: ?[]const u8 = null,
@@ -296,19 +304,15 @@ fn loadMaterial(self: *Materials, engine: *Engine, textures: *Textures, stbi: *m
defer file.close(io); defer file.close(io);
var file_reader = file.reader(io, &buffer); var file_reader = file.reader(io, &buffer);
var json_reader = std.json.Reader.init(temp_allocator, &file_reader.interface); var json_reader = std.json.Reader.init(allocator_frame, &file_reader.interface);
defer json_reader.deinit();
const parsed: std.json.Parsed(MaterialJson) = try std.json.parseFromTokenSource(MaterialJson, temp_allocator, &json_reader, .{ const material_json = try std.json.parseFromTokenSourceLeaky(MaterialJson, allocator_frame, &json_reader, .{
.duplicate_field_behavior = .@"error", .duplicate_field_behavior = .@"error",
.ignore_unknown_fields = false, .ignore_unknown_fields = false,
.allocate = .alloc_if_needed, .allocate = .alloc_if_needed,
}); });
defer parsed.deinit();
const material_json = parsed.value; try self.material_buffer.write(.{
try self.material_buffer.write(engine, .{
.element_offset = index, .element_offset = index,
.elements = &.{ .elements = &.{
.{ .{
@@ -319,10 +323,10 @@ fn loadMaterial(self: *Materials, engine: *Engine, textures: *Textures, stbi: *m
.normal_scale = material_json.normalScale, .normal_scale = material_json.normalScale,
.occlusion_texture_strength = material_json.occlusionTextureStrength, .occlusion_texture_strength = material_json.occlusionTextureStrength,
.roughness = material_json.roughness, .roughness = material_json.roughness,
.base_color_texture = try textures.getOrLoad(engine, stbi, material_json.baseColorTexture, .base_color, io), .base_color_texture = try textures.getOrLoad(stbi, material_json.baseColorTexture, .base_color),
.emissive_texture = try textures.getOrLoad(engine, stbi, material_json.emissiveTexture, .emissive, io), .emissive_texture = try textures.getOrLoad(stbi, material_json.emissiveTexture, .emissive),
.normal_texture = try textures.getOrLoad(engine, stbi, material_json.normalTexture, .normal, io), .normal_texture = try textures.getOrLoad(stbi, material_json.normalTexture, .normal),
.occlusion_roughness_metallic_texture = try textures.getOrLoad(engine, stbi, material_json.occlusionRoughnessMetallicTexture, .occlusion_roughness_metallic, io), .occlusion_roughness_metallic_texture = try textures.getOrLoad(stbi, material_json.occlusionRoughnessMetallicTexture, .occlusion_roughness_metallic),
}, },
}, },
}); });

View File

@@ -3,6 +3,7 @@
const Textures = @This(); const Textures = @This();
const std = @import("std"); const std = @import("std");
const ctx = @import("../AppContext.zig");
const media = @import("media"); const media = @import("media");
const Atom = @import("../engine/Atom.zig").Atom; const Atom = @import("../engine/Atom.zig").Atom;
@@ -56,31 +57,34 @@ pub const Key = struct {
usage: Texture.Usage, usage: Texture.Usage,
}; };
/// Maps a key value to a texture ID. /// Maps a key value to a texture ID. Preallocated with `allocator_general`.
map: std.AutoHashMapUnmanaged(Key, Id), map: std.AutoHashMapUnmanaged(Key, Id),
/// Stores all `Texture` structs and maps a texture ID to a `Texture` struct. /// Stores all `Texture` structs and maps a texture ID to a `Texture` struct.
/// Preallocated with `allocator_general`.
array: std.ArrayList(Texture), array: std.ArrayList(Texture),
/// 4096 textures of usage `.base_color` and 16×16 dimensions should take 4 MiB /// 4096 textures of usage `.base_color` and 16×16 dimensions should take 4 MiB
/// in VRAM. /// in VRAM.
pub const max_textures = 4096; pub const max_textures = 4096;
pub fn init(engine: *Engine, allocator: std.mem.Allocator) !Textures { pub fn init() !Textures {
var map: std.AutoHashMapUnmanaged(Key, Id) = .empty; const allocator_general = ctx.allocator_general;
errdefer map.deinit(allocator);
try map.ensureTotalCapacity(allocator, max_textures);
var array: std.ArrayList(Texture) = try .initCapacity(allocator, max_textures); var map: std.AutoHashMapUnmanaged(Key, Id) = .empty;
errdefer map.deinit(allocator_general);
try map.ensureTotalCapacity(allocator_general, max_textures);
var array: std.ArrayList(Texture) = try .initCapacity(allocator_general, max_textures);
errdefer { errdefer {
for (array.items) |*texture| { for (array.items) |*texture| {
texture.deinit(engine); texture.deinit();
} }
array.deinit(allocator); array.deinit(allocator_general);
} }
// VOLATILE Synchronize with explicit values on top of `Id` type. // VOLATILE Synchronize with explicit values on top of `Id` type.
const empty_base_color_texture = try Texture.init(engine, .{ const empty_base_color_texture = try Texture.init(.{
.width = 1, .width = 1,
.height = 1, .height = 1,
.usage = .base_color, .usage = .base_color,
@@ -89,7 +93,7 @@ pub fn init(engine: *Engine, allocator: std.mem.Allocator) !Textures {
}); });
array.appendAssumeCapacity(empty_base_color_texture); array.appendAssumeCapacity(empty_base_color_texture);
const empty_emissive_texture = try Texture.init(engine, .{ const empty_emissive_texture = try Texture.init(.{
.width = 1, .width = 1,
.height = 1, .height = 1,
.usage = .emissive, .usage = .emissive,
@@ -98,7 +102,7 @@ pub fn init(engine: *Engine, allocator: std.mem.Allocator) !Textures {
}); });
array.appendAssumeCapacity(empty_emissive_texture); array.appendAssumeCapacity(empty_emissive_texture);
const empty_normal_texture = try Texture.init(engine, .{ const empty_normal_texture = try Texture.init(.{
.width = 1, .width = 1,
.height = 1, .height = 1,
.usage = .normal, .usage = .normal,
@@ -107,7 +111,7 @@ pub fn init(engine: *Engine, allocator: std.mem.Allocator) !Textures {
}); });
array.appendAssumeCapacity(empty_normal_texture); array.appendAssumeCapacity(empty_normal_texture);
const empty_occlusuion_roughness_metallic_texture = try Texture.init(engine, .{ const empty_occlusuion_roughness_metallic_texture = try Texture.init(.{
.width = 1, .width = 1,
.height = 1, .height = 1,
.usage = .occlusion_roughness_metallic, .usage = .occlusion_roughness_metallic,
@@ -116,10 +120,10 @@ pub fn init(engine: *Engine, allocator: std.mem.Allocator) !Textures {
}); });
array.appendAssumeCapacity(empty_occlusuion_roughness_metallic_texture); array.appendAssumeCapacity(empty_occlusuion_roughness_metallic_texture);
try empty_base_color_texture.writeSamples(u8, engine, &.{ 255, 255, 255, 255 }); try empty_base_color_texture.writeSamples(u8, &.{ 255, 255, 255, 255 });
try empty_emissive_texture.writeSamples(f16, engine, &.{ 1.0, 1.0, 1.0, 1.0 }); try empty_emissive_texture.writeSamples(f16, &.{ 1.0, 1.0, 1.0, 1.0 });
try empty_normal_texture.writeSamples(i8, engine, &.{ 0, 0, 127, 127 }); try empty_normal_texture.writeSamples(i8, &.{ 0, 0, 127, 127 });
try empty_occlusuion_roughness_metallic_texture.writeSamples(u8, engine, &.{ 255, 255, 255, 255 }); try empty_occlusuion_roughness_metallic_texture.writeSamples(u8, &.{ 255, 255, 255, 255 });
return .{ return .{
.map = map, .map = map,
@@ -127,14 +131,16 @@ pub fn init(engine: *Engine, allocator: std.mem.Allocator) !Textures {
}; };
} }
pub fn deinit(self: *Textures, engine: *Engine, allocator: std.mem.Allocator) void { pub fn deinit(self: *Textures) void {
std.log.scoped(.deinit).debug("Deinitializing {*} with {*} and Allocator{{{*},{*}}}", .{ self, engine, allocator.ptr, allocator.vtable }); const allocator_general = ctx.allocator_general;
std.log.scoped(.deinit).debug("Deinitializing {*}", .{self});
for (self.array.items) |*texture| { for (self.array.items) |*texture| {
texture.deinit(engine); texture.deinit();
} }
self.array.deinit(allocator); self.array.deinit(allocator_general);
self.map.deinit(allocator); self.map.deinit(allocator_general);
self.* = undefined; self.* = undefined;
} }
@@ -174,17 +180,15 @@ pub fn getAtom(self: *const Textures, filename: Atom, usage: Texture.Usage) ?Id
/// usage. /// usage.
pub fn getOrLoad( pub fn getOrLoad(
self: *Textures, self: *Textures,
engine: *Engine,
stbi: *media.stbi, stbi: *media.stbi,
maybe_filename: ?[]const u8, maybe_filename: ?[]const u8,
usage: Texture.Usage, usage: Texture.Usage,
io: std.Io,
) !Id { ) !Id {
if (maybe_filename) |filename| { if (maybe_filename) |filename| {
const key: Key = .{ const key: Key = .{
// If the texture already exists, then the atom must exist and the // If the texture already exists, then the atom must exist and the
// following line will not return any error. // following line will not return any error.
.filename = try .fromString(filename, io), .filename = try .fromString(filename),
.usage = usage, .usage = usage,
}; };
@@ -197,7 +201,7 @@ pub fn getOrLoad(
const id = Id.fromIndexSafe(self.array.items.len) catch |err| switch (err) { const id = Id.fromIndexSafe(self.array.items.len) catch |err| switch (err) {
error.Overflow => return error.OutOfTextures, error.Overflow => return error.OutOfTextures,
}; };
const texture = try loadTexture(engine, stbi, filename, usage, io); const texture = try loadTexture(stbi, filename, usage);
self.map.putAssumeCapacityNoClobber(key, id); self.map.putAssumeCapacityNoClobber(key, id);
self.array.appendAssumeCapacity(texture); self.array.appendAssumeCapacity(texture);
@@ -216,11 +220,9 @@ pub fn getOrLoad(
/// usage. /// usage.
pub fn getOrLoadAtom( pub fn getOrLoadAtom(
self: *Textures, self: *Textures,
engine: *Engine,
stbi: *media.stbi, stbi: *media.stbi,
filename: Atom, filename: Atom,
usage: Texture.Usage, usage: Texture.Usage,
io: std.Io,
) !Id { ) !Id {
if (filename != .empty) { if (filename != .empty) {
const key: Key = .{ const key: Key = .{
@@ -237,7 +239,7 @@ pub fn getOrLoadAtom(
const id = Id.fromIndexSafe(self.array.items.len) catch |err| switch (err) { const id = Id.fromIndexSafe(self.array.items.len) catch |err| switch (err) {
error.Overflow => return error.OutOfTextures, error.Overflow => return error.OutOfTextures,
}; };
const texture = try loadTexture(engine, stbi, filename.toString(), usage, io); const texture = try loadTexture(stbi, filename.toString(), usage);
self.map.putAssumeCapacityNoClobber(key, id); self.map.putAssumeCapacityNoClobber(key, id);
self.array.appendAssumeCapacity(texture); self.array.appendAssumeCapacity(texture);
@@ -250,12 +252,12 @@ pub fn getOrLoadAtom(
} }
fn loadTexture( fn loadTexture(
engine: *Engine,
stbi: *media.stbi, stbi: *media.stbi,
filename: []const u8, filename: []const u8,
usage: Texture.Usage, usage: Texture.Usage,
io: std.Io,
) !Texture { ) !Texture {
const io = ctx.io;
std.log.debug("Loading texture \"{s}\" as {s}...", .{ filename, @tagName(usage) }); std.log.debug("Loading texture \"{s}\" as {s}...", .{ filename, @tagName(usage) });
const cwd = std.Io.Dir.cwd(); const cwd = std.Io.Dir.cwd();
@@ -285,16 +287,16 @@ fn loadTexture(
} }
} }
var texture = try Texture.init(engine, .{ var texture = try Texture.init(.{
.width = img.width, .width = img.width,
.height = img.height, .height = img.height,
.usage = usage, .usage = usage,
.target_queue = .graphics, .target_queue = .graphics,
.name = filename, .name = filename,
}); });
errdefer texture.deinit(engine); errdefer texture.deinit();
try texture.writeRaw(engine, @ptrCast(data)); try texture.writeRaw(@ptrCast(data));
return texture; return texture;
} }

View File

@@ -1,16 +1,9 @@
//! Module for string interning. A string can be converted to a stable integer
//! constant, called an *atom*. The value of an atom 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 to convert 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"); const std = @import("std");
const ctx = @import("../AppContext.zig");
pub const Atom = enum(u16) { pub const Atom = enum(u16) {
// VOLATILE Synchronize explicit values with `init` implementation. // VOLATILE Synchronize explicit values with `Atoms.init` implementation.
/// Atom representing an empty string, i.e. `""`. /// Atom representing an empty string, i.e. `""`.
empty, empty,
@@ -36,29 +29,32 @@ pub const Atom = enum(u16) {
/// Turn a string into an atom. Returns either an existing atom or makes a /// 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 /// new one, if necessary. This will always produce a valid atom. Will not
/// return any error if the atom already exists. /// return any error if the atom already exists.
pub fn fromString(string: []const u8, io: std.Io) error{ Canceled, OutOfMemory, OutOfAtoms }!Atom { pub fn fromString(string: []const u8) error{ OutOfMemory, OutOfAtoms }!Atom {
try mutex.lock(io); const allocator_general = ctx.allocator_general;
defer mutex.unlock(io); const allocator_persistent = ctx.allocator_persistent;
const io = ctx.io;
const atoms = ctx.atoms;
std.debug.assert(initialized); atoms.mutex.lockUncancelable(io);
defer atoms.mutex.unlock(io);
const entry = try map.getOrPut(allocator, string); const entry = try atoms.map.getOrPut(allocator_general, string);
if (entry.found_existing) { if (entry.found_existing) {
return entry.value_ptr.*; return entry.value_ptr.*;
} else { } else {
errdefer _ = map.remove(string); errdefer _ = atoms.map.remove(string);
const atom = Atom.fromIndexSafe(array.items.len) catch |err| switch (err) { const atom = Atom.fromIndexSafe(atoms.array.items.len) catch |err| switch (err) {
error.Overflow => return error.OutOfAtoms, error.Overflow => return error.OutOfAtoms,
}; };
try array.ensureUnusedCapacity(allocator, 1); try atoms.array.ensureUnusedCapacity(allocator_general, 1);
const owned_string = try toOwnedString(string); const owned_string = try allocator_persistent.dupeZ(u8, string);
entry.key_ptr.* = owned_string; entry.key_ptr.* = owned_string;
entry.value_ptr.* = atom; entry.value_ptr.* = atom;
array.appendAssumeCapacity(owned_string); atoms.array.appendAssumeCapacity(owned_string);
return atom; return atom;
} }
} }
@@ -66,13 +62,14 @@ pub const Atom = enum(u16) {
/// Turn a string into an atom, if the string has been already registered as /// 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 /// an atom. Returns `null` otherwise. This will always produce a valid
/// atom. /// atom.
pub fn fromStringIfExists(string: []const u8, io: std.Io) error{Canceled}!?Atom { pub fn fromStringIfExists(string: []const u8) ?Atom {
try mutex.lock(io); const io = ctx.io;
defer mutex.unlock(io); const atoms = ctx.atoms;
std.debug.assert(initialized); atoms.mutex.lockUncancelable(io);
defer atoms.mutex.unlock(io);
return map.get(string); return atoms.map.get(string);
} }
/// Cast an atom into an integer. /// Cast an atom into an integer.
@@ -81,87 +78,13 @@ pub const Atom = enum(u16) {
} }
/// Cast an atom into a string. The caller asserts that the atom is valid. /// Cast an atom into a string. The caller asserts that the atom is valid.
pub fn toString(self: Atom, io: std.Io) error{Canceled}![:0]const u8 { pub fn toString(self: Atom) [:0]const u8 {
try mutex.lock(io); const io = ctx.io;
defer mutex.unlock(io); const atoms = ctx.atoms;
std.debug.assert(initialized); atoms.mutex.lockUncancelable(io);
defer atoms.mutex.unlock(io);
return array.items[self.toInt()]; return atoms.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.Io.Mutex = .init;
pub fn init(_allocator: std.mem.Allocator, io: std.Io) !void {
try mutex.lock(io);
defer mutex.unlock(io);
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(io: std.Io) void {
mutex.lockUncancelable(io);
defer mutex.unlock(io);
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, io: std.Io) void {
mutex.lockUncancelable(io);
defer mutex.unlock(io);
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;
}

80
src/engine/Atoms.zig Normal file
View File

@@ -0,0 +1,80 @@
//! Module for string interning. A string can be converted to a stable integer
//! constant, called an *atom*. The value of an atom 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 to convert string IDs into numbers, so that they can be
//! compared more easily. The users of this module should instead import
//! `@import("Atom.zig").Atom` and use the methods available in the `Atom` type.
//!
//! This module is intended to be initialized once and persist until the end of
//! the whole application. Trying to use it in any other way will result in
//! weird behavior.
const Atoms = @This();
const std = @import("std");
const ctx = @import("../AppContext.zig");
const Atom = @import("Atom.zig").Atom;
/// Maps a string value to an atom value. Uses `allocator_general`.
map: std.StringHashMapUnmanaged(Atom),
/// Maps an atom value to a string. Uses `allocator_general`.
array: std.ArrayList([:0]const u8),
/// Protects all reads and writes to `map` and `array`.
mutex: std.Io.Mutex,
pub fn init() !*Atoms {
const allocator_general = ctx.allocator_general;
const allocator_persistent = ctx.allocator_persistent;
const atoms = try allocator_persistent.create(Atoms);
errdefer allocator_persistent.destroy(atoms);
atoms.* = .{
.map = .empty,
.array = .empty,
.mutex = .init,
};
// VOLATILE Synchronize with explicit values on top of `Atom` type.
try atoms.map.put(allocator_general, "", .empty);
try atoms.array.append(allocator_general, "");
return atoms;
}
pub fn deinit(self: *Atoms) void {
const allocator_general = ctx.allocator_general;
const io = ctx.io;
{
// VOLATILE Unlock mutex before `self.* = undefined` line.
self.mutex.lockUncancelable(io);
defer self.mutex.unlock(io);
std.log.scoped(.deinit).debug("Deinitializing {*}", .{self});
self.map.deinit(allocator_general);
self.array.deinit(allocator_general);
}
self.* = undefined;
}
/// Dump all atoms in a readable format. Use for debugging. Does not flush the
/// writer.
pub fn dump(self: *const Atoms, writer: *std.Io.Writer) void {
const io = ctx.io;
self.mutex.lockUncancelable(io);
defer self.mutex.unlock(io);
for (self.array.items, 0..) |string, i| {
const atom: u16 = @intCast(i);
writer.print("0x{X:0<4} {s}\n", .{ atom, string });
}
}

View File

@@ -9,28 +9,31 @@
const CommandBuffer = @This(); const CommandBuffer = @This();
const std = @import("std"); const std = @import("std");
const ctx = @import("../AppContext.zig");
const vk = @import("vulkan"); const vk = @import("vulkan");
const Engine = @import("Engine.zig"); const Engine = @import("Engine.zig");
const QueueType = @import("QueueType.zig").QueueType; const QueueType = @import("QueueType.zig").QueueType;
proxy: vk.CommandBufferProxy, proxy: vk.CommandBufferProxy,
allocator: std.mem.Allocator,
queue_type: QueueType, queue_type: QueueType,
pub fn init(engine: *Engine, queue_type: QueueType) !CommandBuffer { pub fn init(queue_type: QueueType) !CommandBuffer {
const engine = ctx.engine;
const handle = try engine.allocateCommandBuffer(.{ const handle = try engine.allocateCommandBuffer(.{
.queue_type = queue_type, .queue_type = queue_type,
.level = .primary, .level = .primary,
}); });
return .{ return .{
.proxy = .init(handle, engine.device.wrapper), .proxy = .init(handle, engine.device.wrapper),
.allocator = engine.vk_allocator.allocator,
.queue_type = queue_type, .queue_type = queue_type,
}; };
} }
pub fn deinit(self: *CommandBuffer, engine: *Engine) void { pub fn deinit(self: *CommandBuffer) void {
const engine = ctx.engine;
engine.freeCommandBuffer(.{ engine.freeCommandBuffer(.{
.queue_type = self.queue_type, .queue_type = self.queue_type,
.command_buffer = self.proxy.handle, .command_buffer = self.proxy.handle,
@@ -38,7 +41,9 @@ pub fn deinit(self: *CommandBuffer, engine: *Engine) void {
self.* = undefined; self.* = undefined;
} }
pub fn submit(self: CommandBuffer, engine: *Engine, submit_info: Engine.SubmitInfo) !void { pub fn submit(self: CommandBuffer, submit_info: Engine.SubmitInfo) !void {
const engine = ctx.engine;
try engine.queueSubmit(self.queue_type, self.proxy.handle, submit_info); try engine.queueSubmit(self.queue_type, self.proxy.handle, submit_info);
} }
@@ -96,11 +101,9 @@ pub fn beginCommandBuffer(self: CommandBuffer) !void {
} }
pub fn beginRendering(self: CommandBuffer, rendering_info: RenderingInfo) !void { pub fn beginRendering(self: CommandBuffer, rendering_info: RenderingInfo) !void {
var arena: std.heap.ArenaAllocator = .init(self.allocator); const allocator_frame = ctx.allocator_frame;
defer arena.deinit();
const allocator = arena.allocator();
const color_attachments = try allocator.alloc(vk.RenderingAttachmentInfo, rendering_info.color_attachments.len); const color_attachments = try allocator_frame.alloc(vk.RenderingAttachmentInfo, rendering_info.color_attachments.len);
for (color_attachments, rendering_info.color_attachments) |*x, color_attachment| { for (color_attachments, rendering_info.color_attachments) |*x, color_attachment| {
x.* = .{ x.* = .{
.image_view = color_attachment.image_view, .image_view = color_attachment.image_view,
@@ -187,9 +190,11 @@ pub fn bindIndexBuffer(self: CommandBuffer, buffer: vk.Buffer, offset: u64, inde
} }
pub fn bindVertexBuffers(self: CommandBuffer, first_binding: u32, bindings: []const VertexBufferBinding) !void { pub fn bindVertexBuffers(self: CommandBuffer, first_binding: u32, bindings: []const VertexBufferBinding) !void {
const allocator_frame = ctx.allocator_frame;
var vertex_buffer_bindings = std.MultiArrayList(VertexBufferBinding){}; var vertex_buffer_bindings = std.MultiArrayList(VertexBufferBinding){};
defer vertex_buffer_bindings.deinit(self.allocator); defer vertex_buffer_bindings.deinit(allocator_frame);
try vertex_buffer_bindings.ensureTotalCapacity(self.allocator, bindings.len); try vertex_buffer_bindings.ensureTotalCapacity(allocator_frame, bindings.len);
for (bindings) |binding| { for (bindings) |binding| {
vertex_buffer_bindings.appendAssumeCapacity(binding); vertex_buffer_bindings.appendAssumeCapacity(binding);

View File

@@ -2,6 +2,7 @@ const Engine = @This();
const std = @import("std"); const std = @import("std");
const c = @import("../const.zig"); const c = @import("../const.zig");
const ctx = @import("../AppContext.zig");
const glfw = @import("zglfw"); const glfw = @import("zglfw");
const vk = @import("vulkan"); const vk = @import("vulkan");
@@ -10,17 +11,6 @@ const QueueType = @import("QueueType.zig").QueueType;
const VkAllocator = @import("VkAllocator.zig"); const VkAllocator = @import("VkAllocator.zig");
const WaitSemaphore = @import("WaitSemaphore.zig"); const WaitSemaphore = @import("WaitSemaphore.zig");
pub const Mode = union(enum) {
headless: void,
surface: struct {
window: *glfw.Window,
surface: vk.SurfaceKHR,
presentation_queue: Queue,
},
};
pub const ModeTag = std.meta.Tag(Mode);
const QueueAllocator = struct { const QueueAllocator = struct {
queue_families_properties_buffer: [max_queue_families]vk.QueueFamilyProperties, queue_families_properties_buffer: [max_queue_families]vk.QueueFamilyProperties,
queue_families_in_use: [max_queue_families]u32, queue_families_in_use: [max_queue_families]u32,
@@ -69,18 +59,15 @@ const QueueAllocations = struct {
graphics_queue: Queue.Allocation, graphics_queue: Queue.Allocation,
compute_queue: Queue.Allocation, compute_queue: Queue.Allocation,
transfer_queue: Queue.Allocation, transfer_queue: Queue.Allocation,
/// Defined only in `surface` mode, `undefined` otherwise.
presentation_queue: Queue.Allocation, presentation_queue: Queue.Allocation,
}; };
const DebugUtilsMessenger = if (debug) vk.DebugUtilsMessengerEXT else void; const DebugUtilsMessenger = if (debug) vk.DebugUtilsMessengerEXT else void;
mode: Mode,
vk_allocator: *VkAllocator,
base: vk.BaseWrapper, base: vk.BaseWrapper,
instance: vk.InstanceProxy, instance: vk.InstanceProxy,
device: vk.DeviceProxy, device: vk.DeviceProxy,
surface: vk.SurfaceKHR,
debug_utils_messenger: DebugUtilsMessenger, debug_utils_messenger: DebugUtilsMessenger,
physical_device: vk.PhysicalDevice, physical_device: vk.PhysicalDevice,
@@ -90,6 +77,7 @@ memory_heaps: std.ArrayList(vk.MemoryHeap),
graphics_queue: Queue, graphics_queue: Queue,
compute_queue: Queue, compute_queue: Queue,
transfer_queue: Queue, transfer_queue: Queue,
presentation_queue: Queue,
graphics_command_pool: vk.CommandPool, graphics_command_pool: vk.CommandPool,
compute_command_pool: vk.CommandPool, compute_command_pool: vk.CommandPool,
@@ -99,15 +87,12 @@ prng_ptr: *std.Random.Pcg,
random: std.Random, random: std.Random,
const debug = @import("builtin").mode == .Debug; const debug = @import("builtin").mode == .Debug;
const vk_version: vk.Version = vk.makeApiVersion(
const vk_application_info: vk.ApplicationInfo = .{ 0,
.p_application_name = c.app_name, c.app_version.major,
.application_version = @bitCast(vk_version), c.app_version.minor,
.p_engine_name = c.app_name, c.app_version.patch,
.engine_version = @bitCast(vk_version), );
.api_version = @bitCast(vk.API_VERSION_1_4),
};
const vk_debug_utils_messenger_create_info: vk.DebugUtilsMessengerCreateInfoEXT = .{ const vk_debug_utils_messenger_create_info: vk.DebugUtilsMessengerCreateInfoEXT = .{
.message_severity = .{ .message_severity = .{
.verbose_bit_ext = false, .verbose_bit_ext = false,
@@ -124,16 +109,13 @@ const vk_debug_utils_messenger_create_info: vk.DebugUtilsMessengerCreateInfoEXT
.p_user_data = null, .p_user_data = null,
}; };
const vk_version: vk.Version = vk.makeApiVersion( pub fn init() !*Engine {
0, const allocator_persistent = ctx.allocator_persistent;
c.app_version.major, const io = ctx.io;
c.app_version.minor, const window = ctx.window;
c.app_version.patch,
);
pub fn init(allocator: std.mem.Allocator, io: std.Io, maybe_window: ?*glfw.Window) !Engine { const engine = try allocator_persistent.create(Engine);
const vk_allocator = try VkAllocator.init(allocator, io); errdefer allocator_persistent.destroy(engine);
errdefer vk_allocator.deinit();
// --- LOAD BASE DISPATCH -------------------------------------------------- // --- LOAD BASE DISPATCH --------------------------------------------------
@@ -149,35 +131,39 @@ pub fn init(allocator: std.mem.Allocator, io: std.Io, maybe_window: ?*glfw.Windo
try enabled_extensions.appendBounded(vk.extensions.ext_debug_utils.name); try enabled_extensions.appendBounded(vk.extensions.ext_debug_utils.name);
} }
if (maybe_window != null) {
const glfw_instance_extensions = try glfw.getRequiredInstanceExtensions(); const glfw_instance_extensions = try glfw.getRequiredInstanceExtensions();
try enabled_extensions.appendSliceBounded(glfw_instance_extensions); try enabled_extensions.appendSliceBounded(glfw_instance_extensions);
}
const enabled_layers: []const [*:0]const u8 = if (debug) &.{"VK_LAYER_KHRONOS_validation"} else &.{}; const enabled_layers: []const [*:0]const u8 = if (debug) &.{"VK_LAYER_KHRONOS_validation"} else &.{};
break :blk try base.createInstance(&.{ break :blk try base.createInstance(&.{
.p_application_info = &vk_application_info, .p_application_info = &.{
.p_application_name = c.app_name,
.application_version = vk_version.toU32(),
.p_engine_name = c.app_name,
.engine_version = vk_version.toU32(),
.api_version = @bitCast(vk.API_VERSION_1_4),
},
.enabled_layer_count = enabled_layers.len, .enabled_layer_count = enabled_layers.len,
.pp_enabled_layer_names = enabled_layers.ptr, .pp_enabled_layer_names = enabled_layers.ptr,
.enabled_extension_count = @intCast(enabled_extensions.items.len), .enabled_extension_count = @intCast(enabled_extensions.items.len),
.pp_enabled_extension_names = enabled_extensions.items.ptr, .pp_enabled_extension_names = enabled_extensions.items.ptr,
.p_next = if (debug) &vk_debug_utils_messenger_create_info else null, .p_next = if (debug) &vk_debug_utils_messenger_create_info else null,
}, &vk_allocator.interface); }, ctx.vk_allocator.capture());
}; };
const instance_wrapper_ptr = try allocator.create(vk.InstanceWrapper); const instance_wrapper_ptr = try allocator_persistent.create(vk.InstanceWrapper);
errdefer allocator.destroy(instance_wrapper_ptr); errdefer allocator_persistent.destroy(instance_wrapper_ptr);
instance_wrapper_ptr.* = .load(instance_handle, base.dispatch.vkGetInstanceProcAddr.?); instance_wrapper_ptr.* = .load(instance_handle, base.dispatch.vkGetInstanceProcAddr.?);
const instance = vk.InstanceProxy.init(instance_handle, instance_wrapper_ptr); const instance = vk.InstanceProxy.init(instance_handle, instance_wrapper_ptr);
errdefer instance.destroyInstance(&vk_allocator.interface); errdefer instance.destroyInstance(ctx.vk_allocator.capture());
// --- CREATE DEBUG UTILS MESSENGER ---------------------------------------- // --- CREATE DEBUG UTILS MESSENGER ----------------------------------------
const debug_utils_messenger: DebugUtilsMessenger = if (debug) try instance.createDebugUtilsMessengerEXT(&vk_debug_utils_messenger_create_info, &vk_allocator.interface) else {}; const debug_utils_messenger: DebugUtilsMessenger = if (debug) try instance.createDebugUtilsMessengerEXT(&vk_debug_utils_messenger_create_info, ctx.vk_allocator.capture()) else {};
errdefer { errdefer {
if (debug) instance.destroyDebugUtilsMessengerEXT(debug_utils_messenger, &vk_allocator.interface); if (debug) instance.destroyDebugUtilsMessengerEXT(debug_utils_messenger, ctx.vk_allocator.capture());
} }
// --- CHOOSE PHYSICAL DEVICE, GET ITS MEMORY PROPERTIES ------------------- // --- CHOOSE PHYSICAL DEVICE, GET ITS MEMORY PROPERTIES -------------------
@@ -208,20 +194,20 @@ pub fn init(allocator: std.mem.Allocator, io: std.Io, maybe_window: ?*glfw.Windo
const physical_device_memory_properties = instance.getPhysicalDeviceMemoryProperties(physical_device); const physical_device_memory_properties = instance.getPhysicalDeviceMemoryProperties(physical_device);
var memory_types = try std.ArrayList(vk.MemoryType).initCapacity(allocator, vk.MAX_MEMORY_TYPES); var memory_types = try std.ArrayList(vk.MemoryType).initCapacity(allocator_persistent, vk.MAX_MEMORY_TYPES);
errdefer memory_types.deinit(allocator); errdefer memory_types.deinit(allocator_persistent);
memory_types.appendSliceAssumeCapacity(physical_device_memory_properties.memory_types[0..physical_device_memory_properties.memory_type_count]); memory_types.appendSliceAssumeCapacity(physical_device_memory_properties.memory_types[0..physical_device_memory_properties.memory_type_count]);
var memory_heaps = try std.ArrayList(vk.MemoryHeap).initCapacity(allocator, vk.MAX_MEMORY_HEAPS); var memory_heaps = try std.ArrayList(vk.MemoryHeap).initCapacity(allocator_persistent, vk.MAX_MEMORY_HEAPS);
errdefer memory_heaps.deinit(allocator); errdefer memory_heaps.deinit(allocator_persistent);
memory_heaps.appendSliceAssumeCapacity(physical_device_memory_properties.memory_heaps[0..physical_device_memory_properties.memory_heap_count]); memory_heaps.appendSliceAssumeCapacity(physical_device_memory_properties.memory_heaps[0..physical_device_memory_properties.memory_heap_count]);
// --- CREATE SURFACE ------------------------------------------------------ // --- CREATE SURFACE ------------------------------------------------------
var surface: vk.SurfaceKHR = undefined; var surface: vk.SurfaceKHR = undefined;
if (maybe_window) |window| try glfw.createWindowSurface(instance_handle, window, &vk_allocator.interface, &surface); try glfw.createWindowSurface(instance_handle, window, ctx.vk_allocator.capture(), &surface);
errdefer { errdefer {
if (maybe_window != null) instance.destroySurfaceKHR(surface, &vk_allocator.interface); instance.destroySurfaceKHR(surface, ctx.vk_allocator.capture());
} }
// --- ALLOCATE QUEUES ----------------------------------------------------- // --- ALLOCATE QUEUES -----------------------------------------------------
@@ -238,12 +224,12 @@ pub fn init(allocator: std.mem.Allocator, io: std.Io, maybe_window: ?*glfw.Windo
const graphics_queue_family = findGraphicsQueueFamily(queue_families_properties) orelse return error.NoGraphicsQueue; const graphics_queue_family = findGraphicsQueueFamily(queue_families_properties) orelse return error.NoGraphicsQueue;
const compute_queue_family = findComputeQueueFamily(queue_families_properties) orelse return error.NoComputeQueue; const compute_queue_family = findComputeQueueFamily(queue_families_properties) orelse return error.NoComputeQueue;
const transfer_queue_family = findTransferQueueFamily(queue_families_properties) orelse return error.NoTransferQueue; const transfer_queue_family = findTransferQueueFamily(queue_families_properties) orelse return error.NoTransferQueue;
const presentation_queue_family = if (maybe_window != null) (try findPresentationQueueFamily(queue_families_properties, instance, physical_device, surface) orelse return error.NoPresentationQueue) else undefined; const presentation_queue_family = try findPresentationQueueFamily(queue_families_properties, instance, physical_device, surface) orelse return error.NoPresentationQueue;
const graphics_queue_index = queue_allocator.allocateQueueIndex(graphics_queue_family); const graphics_queue_index = queue_allocator.allocateQueueIndex(graphics_queue_family);
const compute_queue_index = queue_allocator.allocateQueueIndex(compute_queue_family); const compute_queue_index = queue_allocator.allocateQueueIndex(compute_queue_family);
const transfer_queue_index = queue_allocator.allocateQueueIndex(transfer_queue_family); const transfer_queue_index = queue_allocator.allocateQueueIndex(transfer_queue_family);
const presentation_queue_index = if (maybe_window != null) queue_allocator.allocateQueueIndex(presentation_queue_family) else undefined; const presentation_queue_index = queue_allocator.allocateQueueIndex(presentation_queue_family);
const queue_priorities = [_]f32{1.0} ** max_queues; const queue_priorities = [_]f32{1.0} ** max_queues;
@@ -261,7 +247,7 @@ pub fn init(allocator: std.mem.Allocator, io: std.Io, maybe_window: ?*glfw.Windo
.graphics_queue = .{ .family = graphics_queue_family, .index = graphics_queue_index }, .graphics_queue = .{ .family = graphics_queue_family, .index = graphics_queue_index },
.compute_queue = .{ .family = compute_queue_family, .index = compute_queue_index }, .compute_queue = .{ .family = compute_queue_family, .index = compute_queue_index },
.transfer_queue = .{ .family = transfer_queue_family, .index = transfer_queue_index }, .transfer_queue = .{ .family = transfer_queue_family, .index = transfer_queue_index },
.presentation_queue = if (maybe_window != null) .{ .family = presentation_queue_family, .index = presentation_queue_index } else undefined, .presentation_queue = .{ .family = presentation_queue_family, .index = presentation_queue_index },
}; };
}; };
@@ -271,9 +257,7 @@ pub fn init(allocator: std.mem.Allocator, io: std.Io, maybe_window: ?*glfw.Windo
var enabled_extensions_buffer: [16][*:0]const u8 = undefined; var enabled_extensions_buffer: [16][*:0]const u8 = undefined;
var enabled_extensions: std.ArrayList([*:0]const u8) = .initBuffer(&enabled_extensions_buffer); var enabled_extensions: std.ArrayList([*:0]const u8) = .initBuffer(&enabled_extensions_buffer);
if (maybe_window != null) {
try enabled_extensions.appendBounded(vk.extensions.khr_swapchain.name); try enabled_extensions.appendBounded(vk.extensions.khr_swapchain.name);
}
var enabled_features_vulkan10: vk.PhysicalDeviceFeatures2 = .{ var enabled_features_vulkan10: vk.PhysicalDeviceFeatures2 = .{
.features = .{ .features = .{
@@ -319,35 +303,35 @@ pub fn init(allocator: std.mem.Allocator, io: std.Io, maybe_window: ?*glfw.Windo
.p_queue_create_infos = queue_create_info.items.ptr, .p_queue_create_infos = queue_create_info.items.ptr,
.enabled_extension_count = @intCast(enabled_extensions.items.len), .enabled_extension_count = @intCast(enabled_extensions.items.len),
.pp_enabled_extension_names = enabled_extensions.items.ptr, .pp_enabled_extension_names = enabled_extensions.items.ptr,
}, &vk_allocator.interface); }, ctx.vk_allocator.capture());
}; };
const device_wrapper_ptr = try allocator.create(vk.DeviceWrapper); const device_wrapper_ptr = try allocator_persistent.create(vk.DeviceWrapper);
errdefer allocator.destroy(device_wrapper_ptr); errdefer allocator_persistent.destroy(device_wrapper_ptr);
device_wrapper_ptr.* = .load(device_handle, instance.wrapper.dispatch.vkGetDeviceProcAddr.?); device_wrapper_ptr.* = .load(device_handle, instance.wrapper.dispatch.vkGetDeviceProcAddr.?);
const device = vk.DeviceProxy.init(device_handle, device_wrapper_ptr); const device = vk.DeviceProxy.init(device_handle, device_wrapper_ptr);
errdefer device.destroyDevice(&vk_allocator.interface); errdefer device.destroyDevice(ctx.vk_allocator.capture());
// --- CREATE COMMAND POOLS, GET QUEUES ------------------------------------ // --- CREATE COMMAND POOLS, GET QUEUES ------------------------------------
const graphics_command_pool = try device.createCommandPool(&.{ const graphics_command_pool = try device.createCommandPool(&.{
.flags = .{ .transient_bit = true }, .flags = .{ .transient_bit = true },
.queue_family_index = queue_allocations.graphics_queue.family, .queue_family_index = queue_allocations.graphics_queue.family,
}, &vk_allocator.interface); }, ctx.vk_allocator.capture());
errdefer device.destroyCommandPool(graphics_command_pool, &vk_allocator.interface); errdefer device.destroyCommandPool(graphics_command_pool, ctx.vk_allocator.capture());
const compute_command_pool = try device.createCommandPool(&.{ const compute_command_pool = try device.createCommandPool(&.{
.flags = .{ .transient_bit = true }, .flags = .{ .transient_bit = true },
.queue_family_index = queue_allocations.compute_queue.family, .queue_family_index = queue_allocations.compute_queue.family,
}, &vk_allocator.interface); }, ctx.vk_allocator.capture());
errdefer device.destroyCommandPool(compute_command_pool, &vk_allocator.interface); errdefer device.destroyCommandPool(compute_command_pool, ctx.vk_allocator.capture());
const transfer_command_pool = try device.createCommandPool(&.{ const transfer_command_pool = try device.createCommandPool(&.{
.flags = .{ .transient_bit = true }, .flags = .{ .transient_bit = true },
.queue_family_index = queue_allocations.transfer_queue.family, .queue_family_index = queue_allocations.transfer_queue.family,
}, &vk_allocator.interface); }, ctx.vk_allocator.capture());
errdefer device.destroyCommandPool(transfer_command_pool, &vk_allocator.interface); errdefer device.destroyCommandPool(transfer_command_pool, ctx.vk_allocator.capture());
const graphics_queue = Queue.init( const graphics_queue = Queue.init(
device.getDeviceQueue( device.getDeviceQueue(
@@ -370,18 +354,18 @@ pub fn init(allocator: std.mem.Allocator, io: std.Io, maybe_window: ?*glfw.Windo
), ),
queue_allocations.transfer_queue, queue_allocations.transfer_queue,
); );
const presentation_queue: Queue = if (maybe_window != null) .init( const presentation_queue = Queue.init(
device.getDeviceQueue( device.getDeviceQueue(
queue_allocations.presentation_queue.family, queue_allocations.presentation_queue.family,
queue_allocations.presentation_queue.index, queue_allocations.presentation_queue.index,
), ),
queue_allocations.presentation_queue, queue_allocations.presentation_queue,
) else undefined; );
// --- CREATE AND SEED RNG ------------------------------------------------- // --- CREATE AND SEED RNG -------------------------------------------------
const prng_ptr = try allocator.create(std.Random.Pcg); const prng_ptr = try allocator_persistent.create(std.Random.Pcg);
errdefer allocator.destroy(prng_ptr); errdefer allocator_persistent.destroy(prng_ptr);
const timestamp: u64 = @bitCast(@as(i64, @truncate(std.Io.Timestamp.now(io, .awake).nanoseconds))); const timestamp: u64 = @bitCast(@as(i64, @truncate(std.Io.Timestamp.now(io, .awake).nanoseconds)));
prng_ptr.* = .init(timestamp); prng_ptr.* = .init(timestamp);
@@ -389,17 +373,11 @@ pub fn init(allocator: std.mem.Allocator, io: std.Io, maybe_window: ?*glfw.Windo
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
return .{ engine.* = .{
.mode = if (maybe_window) |window| .{ .surface = .{
.window = window,
.surface = surface,
.presentation_queue = presentation_queue,
} } else .{ .headless = {} },
.vk_allocator = vk_allocator,
.base = base, .base = base,
.instance = instance, .instance = instance,
.device = device, .device = device,
.surface = surface,
.debug_utils_messenger = debug_utils_messenger, .debug_utils_messenger = debug_utils_messenger,
.physical_device = physical_device, .physical_device = physical_device,
@@ -409,6 +387,7 @@ pub fn init(allocator: std.mem.Allocator, io: std.Io, maybe_window: ?*glfw.Windo
.graphics_queue = graphics_queue, .graphics_queue = graphics_queue,
.compute_queue = compute_queue, .compute_queue = compute_queue,
.transfer_queue = transfer_queue, .transfer_queue = transfer_queue,
.presentation_queue = presentation_queue,
.graphics_command_pool = graphics_command_pool, .graphics_command_pool = graphics_command_pool,
.compute_command_pool = compute_command_pool, .compute_command_pool = compute_command_pool,
@@ -417,35 +396,23 @@ pub fn init(allocator: std.mem.Allocator, io: std.Io, maybe_window: ?*glfw.Windo
.prng_ptr = prng_ptr, .prng_ptr = prng_ptr,
.random = random, .random = random,
}; };
return engine;
} }
pub fn deinit(self: *Engine) void { pub fn deinit(self: *Engine) void {
std.log.scoped(.deinit).debug("Deinitializing {*}", .{self}); std.log.scoped(.deinit).debug("Deinitializing {*}", .{self});
const allocator = self.vk_allocator.allocator; self.device.destroyCommandPool(self.graphics_command_pool, ctx.vk_allocator.capture());
self.device.destroyCommandPool(self.compute_command_pool, ctx.vk_allocator.capture());
self.device.destroyCommandPool(self.transfer_command_pool, ctx.vk_allocator.capture());
allocator.destroy(self.prng_ptr); self.device.destroyDevice(ctx.vk_allocator.capture());
self.instance.destroySurfaceKHR(self.surface, ctx.vk_allocator.capture());
self.device.destroyCommandPool(self.graphics_command_pool, &self.vk_allocator.interface); if (debug) self.instance.destroyDebugUtilsMessengerEXT(self.debug_utils_messenger, ctx.vk_allocator.capture());
self.device.destroyCommandPool(self.compute_command_pool, &self.vk_allocator.interface);
self.device.destroyCommandPool(self.transfer_command_pool, &self.vk_allocator.interface);
self.device.destroyDevice(&self.vk_allocator.interface); self.instance.destroyInstance(ctx.vk_allocator.capture());
allocator.destroy(self.device.wrapper);
if (std.meta.activeTag(self.mode) == .surface) {
self.instance.destroySurfaceKHR(self.mode.surface.surface, &self.vk_allocator.interface);
}
self.memory_heaps.deinit(allocator);
self.memory_types.deinit(allocator);
if (debug) self.instance.destroyDebugUtilsMessengerEXT(self.debug_utils_messenger, &self.vk_allocator.interface);
self.instance.destroyInstance(&self.vk_allocator.interface);
allocator.destroy(self.instance.wrapper);
self.vk_allocator.deinit();
self.* = undefined; self.* = undefined;
} }
@@ -458,7 +425,7 @@ pub fn allocate(self: *const Engine, memory_requirements: vk.MemoryRequirements,
return try self.device.allocateMemory(&.{ return try self.device.allocateMemory(&.{
.allocation_size = memory_requirements.size, .allocation_size = memory_requirements.size,
.memory_type_index = @truncate(i), .memory_type_index = @truncate(i),
}, &self.vk_allocator.interface); }, ctx.vk_allocator.capture());
} }
} }
@@ -536,6 +503,8 @@ pub fn queueSubmit(
command_buffer: vk.CommandBuffer, command_buffer: vk.CommandBuffer,
submit_info: SubmitInfo, submit_info: SubmitInfo,
) !void { ) !void {
const allocator_frame = ctx.allocator_frame;
const queue = switch (queue_type) { const queue = switch (queue_type) {
.graphics => self.graphics_queue.handle, .graphics => self.graphics_queue.handle,
.compute => self.compute_queue.handle, .compute => self.compute_queue.handle,
@@ -545,8 +514,8 @@ pub fn queueSubmit(
const command_buffers = [_]vk.CommandBuffer{command_buffer}; const command_buffers = [_]vk.CommandBuffer{command_buffer};
var wait_semaphores = std.MultiArrayList(WaitSemaphore){}; var wait_semaphores = std.MultiArrayList(WaitSemaphore){};
defer wait_semaphores.deinit(self.vk_allocator.allocator); defer wait_semaphores.deinit(allocator_frame);
try wait_semaphores.ensureTotalCapacity(self.vk_allocator.allocator, submit_info.wait_semaphores.len); try wait_semaphores.ensureTotalCapacity(allocator_frame, submit_info.wait_semaphores.len);
for (submit_info.wait_semaphores) |wait_semaphore| { for (submit_info.wait_semaphores) |wait_semaphore| {
wait_semaphores.appendAssumeCapacity(wait_semaphore); wait_semaphores.appendAssumeCapacity(wait_semaphore);
@@ -577,7 +546,7 @@ pub fn queuePresent(self: *Engine, present_info: PresentInfo) !vk.Result {
const swapchains = [_]vk.SwapchainKHR{present_info.swapchain}; const swapchains = [_]vk.SwapchainKHR{present_info.swapchain};
const image_indices = [_]u32{present_info.image_index}; const image_indices = [_]u32{present_info.image_index};
const res = try self.device.queuePresentKHR(self.mode.surface.presentation_queue.handle, &.{ const res = try self.device.queuePresentKHR(self.presentation_queue.handle, &.{
.wait_semaphore_count = @intCast(present_info.wait_semaphores.len), .wait_semaphore_count = @intCast(present_info.wait_semaphores.len),
.p_wait_semaphores = present_info.wait_semaphores.ptr, .p_wait_semaphores = present_info.wait_semaphores.ptr,
.swapchain_count = swapchains.len, .swapchain_count = swapchains.len,
@@ -974,13 +943,11 @@ pub fn bindImageMemory(self: *Engine, image: vk.Image, memory: vk.DeviceMemory,
} }
pub fn createBuffer(self: *Engine, create_info: BufferCreateInfo) !vk.Buffer { pub fn createBuffer(self: *Engine, create_info: BufferCreateInfo) !vk.Buffer {
var arena = self.makeArena(); const allocator_frame = ctx.allocator_frame;
defer arena.deinit();
const allocator = arena.allocator();
var queue_family_indices_set: std.AutoArrayHashMapUnmanaged(u32, void) = .{}; var queue_family_indices_set: std.AutoArrayHashMapUnmanaged(u32, void) = .{};
for (create_info.queue_family_indices) |queue_family_index| { for (create_info.queue_family_indices) |queue_family_index| {
try queue_family_indices_set.put(allocator, queue_family_index, {}); try queue_family_indices_set.put(allocator_frame, queue_family_index, {});
} }
const queue_family_indices = queue_family_indices_set.keys(); const queue_family_indices = queue_family_indices_set.keys();
@@ -992,14 +959,12 @@ pub fn createBuffer(self: *Engine, create_info: BufferCreateInfo) !vk.Buffer {
.sharing_mode = if (queue_family_indices.len > 1) .concurrent else .exclusive, .sharing_mode = if (queue_family_indices.len > 1) .concurrent else .exclusive,
.queue_family_index_count = if (queue_family_indices.len > 1) @intCast(queue_family_indices.len) else 0, .queue_family_index_count = if (queue_family_indices.len > 1) @intCast(queue_family_indices.len) else 0,
.p_queue_family_indices = if (queue_family_indices.len > 1) queue_family_indices.ptr else null, .p_queue_family_indices = if (queue_family_indices.len > 1) queue_family_indices.ptr else null,
}, &self.vk_allocator.interface); }, ctx.vk_allocator.capture());
return buffer; return buffer;
} }
pub fn createDescriptorSetLayout(self: *Engine, create_info: DescriptorSetLayoutCreateInfo) !vk.DescriptorSetLayout { pub fn createDescriptorSetLayout(self: *Engine, create_info: DescriptorSetLayoutCreateInfo) !vk.DescriptorSetLayout {
var arena = self.makeArena(); const allocator_frame = ctx.allocator_frame;
defer arena.deinit();
const allocator = arena.allocator();
const has_binding_flags = blk: { const has_binding_flags = blk: {
for (create_info.bindings) |binding| { for (create_info.bindings) |binding| {
@@ -1013,7 +978,7 @@ pub fn createDescriptorSetLayout(self: *Engine, create_info: DescriptorSetLayout
var p_next: ?*const anyopaque = null; var p_next: ?*const anyopaque = null;
if (has_binding_flags) { if (has_binding_flags) {
const descriptor_set_layout_bindings_flags = try allocator.alloc(vk.DescriptorBindingFlags, create_info.bindings.len); const descriptor_set_layout_bindings_flags = try allocator_frame.alloc(vk.DescriptorBindingFlags, create_info.bindings.len);
for (descriptor_set_layout_bindings_flags, create_info.bindings) |*x, binding| { for (descriptor_set_layout_bindings_flags, create_info.bindings) |*x, binding| {
x.* = binding.flags; x.* = binding.flags;
} }
@@ -1024,7 +989,7 @@ pub fn createDescriptorSetLayout(self: *Engine, create_info: DescriptorSetLayout
}; };
} }
const bindings = try allocator.alloc(vk.DescriptorSetLayoutBinding, create_info.bindings.len); const bindings = try allocator_frame.alloc(vk.DescriptorSetLayoutBinding, create_info.bindings.len);
for (bindings, create_info.bindings) |*x, binding| { for (bindings, create_info.bindings) |*x, binding| {
x.* = .{ x.* = .{
.binding = binding.binding, .binding = binding.binding,
@@ -1040,7 +1005,7 @@ pub fn createDescriptorSetLayout(self: *Engine, create_info: DescriptorSetLayout
.flags = create_info.flags, .flags = create_info.flags,
.binding_count = @intCast(bindings.len), .binding_count = @intCast(bindings.len),
.p_bindings = bindings.ptr, .p_bindings = bindings.ptr,
}, &self.vk_allocator.interface); }, ctx.vk_allocator.capture());
return descriptor_set_layout; return descriptor_set_layout;
} }
@@ -1050,21 +1015,19 @@ pub fn createDescriptorPool(self: *Engine, create_info: DescriptorPoolCreateInfo
.max_sets = create_info.max_sets, .max_sets = create_info.max_sets,
.pool_size_count = @intCast(create_info.pool_sizes.len), .pool_size_count = @intCast(create_info.pool_sizes.len),
.p_pool_sizes = create_info.pool_sizes.ptr, .p_pool_sizes = create_info.pool_sizes.ptr,
}, &self.vk_allocator.interface); }, ctx.vk_allocator.capture());
return descriptor_pool; return descriptor_pool;
} }
pub fn createFence(self: *Engine, create_info: vk.FenceCreateInfo) !vk.Fence { pub fn createFence(self: *Engine, create_info: vk.FenceCreateInfo) !vk.Fence {
const fence = try self.device.createFence(&create_info, &self.vk_allocator.interface); const fence = try self.device.createFence(&create_info, ctx.vk_allocator.capture());
return fence; return fence;
} }
pub fn createGraphicsPipeline(self: *Engine, create_info: GraphicsPipelineCreateInfo) !vk.Pipeline { pub fn createGraphicsPipeline(self: *Engine, create_info: GraphicsPipelineCreateInfo) !vk.Pipeline {
var arena = self.makeArena(); const allocator_frame = ctx.allocator_frame;
defer arena.deinit();
const allocator = arena.allocator();
const stages = try allocator.alloc(vk.PipelineShaderStageCreateInfo, create_info.stages.len); const stages = try allocator_frame.alloc(vk.PipelineShaderStageCreateInfo, create_info.stages.len);
for (stages, create_info.stages) |*x, stage| { for (stages, create_info.stages) |*x, stage| {
x.* = .{ x.* = .{
.flags = stage.flags, .flags = stage.flags,
@@ -1073,7 +1036,7 @@ pub fn createGraphicsPipeline(self: *Engine, create_info: GraphicsPipelineCreate
.module = stage.module, .module = stage.module,
.p_specialization_info = blk: { .p_specialization_info = blk: {
if (stage.specialization_info) |y| { if (stage.specialization_info) |y| {
const specialization_info = try allocator.create(vk.SpecializationInfo); const specialization_info = try allocator_frame.create(vk.SpecializationInfo);
specialization_info.* = .{ specialization_info.* = .{
.map_entry_count = @intCast(y.map_entries.len), .map_entry_count = @intCast(y.map_entries.len),
.p_map_entries = y.map_entries.ptr, .p_map_entries = y.map_entries.ptr,
@@ -1142,18 +1105,16 @@ pub fn createGraphicsPipeline(self: *Engine, create_info: GraphicsPipelineCreate
}; };
var pipelines: [1]vk.Pipeline = undefined; var pipelines: [1]vk.Pipeline = undefined;
_ = try self.device.createGraphicsPipelines(.null_handle, &graphics_pipeline_create_infos, &self.vk_allocator.interface, &pipelines); _ = try self.device.createGraphicsPipelines(.null_handle, &graphics_pipeline_create_infos, ctx.vk_allocator.capture(), &pipelines);
return pipelines[0]; return pipelines[0];
} }
pub fn createImage(self: *Engine, create_info: ImageCreateInfo) !vk.Image { pub fn createImage(self: *Engine, create_info: ImageCreateInfo) !vk.Image {
var arena = self.makeArena(); const allocator_frame = ctx.allocator_frame;
defer arena.deinit();
const allocator = arena.allocator();
var queue_family_indices_set: std.AutoArrayHashMapUnmanaged(u32, void) = .{}; var queue_family_indices_set: std.AutoArrayHashMapUnmanaged(u32, void) = .{};
for (create_info.queue_family_indices) |queue_family_index| { for (create_info.queue_family_indices) |queue_family_index| {
try queue_family_indices_set.put(allocator, queue_family_index, {}); try queue_family_indices_set.put(allocator_frame, queue_family_index, {});
} }
const queue_family_indices = queue_family_indices_set.keys(); const queue_family_indices = queue_family_indices_set.keys();
@@ -1172,7 +1133,7 @@ pub fn createImage(self: *Engine, create_info: ImageCreateInfo) !vk.Image {
.queue_family_index_count = if (queue_family_indices.len > 1) @intCast(queue_family_indices.len) else 0, .queue_family_index_count = if (queue_family_indices.len > 1) @intCast(queue_family_indices.len) else 0,
.p_queue_family_indices = if (queue_family_indices.len > 1) queue_family_indices.ptr else null, .p_queue_family_indices = if (queue_family_indices.len > 1) queue_family_indices.ptr else null,
.initial_layout = create_info.initial_layout, .initial_layout = create_info.initial_layout,
}, &self.vk_allocator.interface); }, ctx.vk_allocator.capture());
return image; return image;
} }
@@ -1184,7 +1145,7 @@ pub fn createImageView(self: *Engine, create_info: ImageViewCreateInfo) !vk.Imag
.format = create_info.format, .format = create_info.format,
.components = create_info.components, .components = create_info.components,
.subresource_range = create_info.subresource_range, .subresource_range = create_info.subresource_range,
}, &self.vk_allocator.interface); }, ctx.vk_allocator.capture());
return image_view; return image_view;
} }
@@ -1195,17 +1156,17 @@ pub fn createPipelineLayout(self: *Engine, create_info: PipelineLayoutCreateInfo
.p_set_layouts = create_info.set_layouts.ptr, .p_set_layouts = create_info.set_layouts.ptr,
.push_constant_range_count = @intCast(create_info.push_constant_ranges.len), .push_constant_range_count = @intCast(create_info.push_constant_ranges.len),
.p_push_constant_ranges = create_info.push_constant_ranges.ptr, .p_push_constant_ranges = create_info.push_constant_ranges.ptr,
}, &self.vk_allocator.interface); }, ctx.vk_allocator.capture());
return pipeline_layout; return pipeline_layout;
} }
pub fn createSampler(self: *Engine, create_info: vk.SamplerCreateInfo) !vk.Sampler { pub fn createSampler(self: *Engine, create_info: vk.SamplerCreateInfo) !vk.Sampler {
const sampler = try self.device.createSampler(&create_info, &self.vk_allocator.interface); const sampler = try self.device.createSampler(&create_info, ctx.vk_allocator.capture());
return sampler; return sampler;
} }
pub fn createSemaphore(self: *Engine) !vk.Semaphore { pub fn createSemaphore(self: *Engine) !vk.Semaphore {
const semaphore = try self.device.createSemaphore(&.{}, &self.vk_allocator.interface); const semaphore = try self.device.createSemaphore(&.{}, ctx.vk_allocator.capture());
return semaphore; return semaphore;
} }
@@ -1214,18 +1175,16 @@ pub fn createShaderModule(self: *Engine, create_info: ShaderModuleCreateInfo) !v
.flags = create_info.flags, .flags = create_info.flags,
.code_size = create_info.code.len, .code_size = create_info.code.len,
.p_code = @ptrCast(create_info.code.ptr), .p_code = @ptrCast(create_info.code.ptr),
}, &self.vk_allocator.interface); }, ctx.vk_allocator.capture());
return shader_module; return shader_module;
} }
pub fn createSwapchain(self: *Engine, create_info: SwapchainCreateInfo) !vk.SwapchainKHR { pub fn createSwapchain(self: *Engine, create_info: SwapchainCreateInfo) !vk.SwapchainKHR {
var arena = self.makeArena(); const allocator_frame = ctx.allocator_frame;
defer arena.deinit();
const allocator = arena.allocator();
var queue_family_indices_set: std.AutoArrayHashMapUnmanaged(u32, void) = .{}; var queue_family_indices_set: std.AutoArrayHashMapUnmanaged(u32, void) = .{};
for (create_info.queue_family_indices) |queue_family_index| { for (create_info.queue_family_indices) |queue_family_index| {
try queue_family_indices_set.put(allocator, queue_family_index, {}); try queue_family_indices_set.put(allocator_frame, queue_family_index, {});
} }
const queue_family_indices = queue_family_indices_set.keys(); const queue_family_indices = queue_family_indices_set.keys();
@@ -1247,56 +1206,56 @@ pub fn createSwapchain(self: *Engine, create_info: SwapchainCreateInfo) !vk.Swap
.present_mode = create_info.present_mode, .present_mode = create_info.present_mode,
.clipped = create_info.clipped, .clipped = create_info.clipped,
.old_swapchain = create_info.old_swapchain, .old_swapchain = create_info.old_swapchain,
}, &self.vk_allocator.interface); }, ctx.vk_allocator.capture());
return swapchain; return swapchain;
} }
pub fn destroyBuffer(self: *Engine, buffer: vk.Buffer) void { pub fn destroyBuffer(self: *Engine, buffer: vk.Buffer) void {
self.device.destroyBuffer(buffer, &self.vk_allocator.interface); self.device.destroyBuffer(buffer, ctx.vk_allocator.capture());
} }
pub fn destroyDescriptorPool(self: *Engine, descriptor_pool: vk.DescriptorPool) void { pub fn destroyDescriptorPool(self: *Engine, descriptor_pool: vk.DescriptorPool) void {
self.device.destroyDescriptorPool(descriptor_pool, &self.vk_allocator.interface); self.device.destroyDescriptorPool(descriptor_pool, ctx.vk_allocator.capture());
} }
pub fn destroyDescriptorSetLayout(self: *Engine, descriptor_set_layout: vk.DescriptorSetLayout) void { pub fn destroyDescriptorSetLayout(self: *Engine, descriptor_set_layout: vk.DescriptorSetLayout) void {
self.device.destroyDescriptorSetLayout(descriptor_set_layout, &self.vk_allocator.interface); self.device.destroyDescriptorSetLayout(descriptor_set_layout, ctx.vk_allocator.capture());
} }
pub fn destroyFence(self: *Engine, fence: vk.Fence) void { pub fn destroyFence(self: *Engine, fence: vk.Fence) void {
self.device.destroyFence(fence, &self.vk_allocator.interface); self.device.destroyFence(fence, ctx.vk_allocator.capture());
} }
pub fn destroyImage(self: *Engine, image: vk.Image) void { pub fn destroyImage(self: *Engine, image: vk.Image) void {
self.device.destroyImage(image, &self.vk_allocator.interface); self.device.destroyImage(image, ctx.vk_allocator.capture());
} }
pub fn destroyImageView(self: *Engine, image_view: vk.ImageView) void { pub fn destroyImageView(self: *Engine, image_view: vk.ImageView) void {
self.device.destroyImageView(image_view, &self.vk_allocator.interface); self.device.destroyImageView(image_view, ctx.vk_allocator.capture());
} }
pub fn destroyPipeline(self: *Engine, pipeline: vk.Pipeline) void { pub fn destroyPipeline(self: *Engine, pipeline: vk.Pipeline) void {
self.device.destroyPipeline(pipeline, &self.vk_allocator.interface); self.device.destroyPipeline(pipeline, ctx.vk_allocator.capture());
} }
pub fn destroyPipelineLayout(self: *Engine, pipeline_layout: vk.PipelineLayout) void { pub fn destroyPipelineLayout(self: *Engine, pipeline_layout: vk.PipelineLayout) void {
self.device.destroyPipelineLayout(pipeline_layout, &self.vk_allocator.interface); self.device.destroyPipelineLayout(pipeline_layout, ctx.vk_allocator.capture());
} }
pub fn destroySampler(self: *Engine, sampler: vk.Sampler) void { pub fn destroySampler(self: *Engine, sampler: vk.Sampler) void {
self.device.destroySampler(sampler, &self.vk_allocator.interface); self.device.destroySampler(sampler, ctx.vk_allocator.capture());
} }
pub fn destroySemaphore(self: *Engine, semaphore: vk.Semaphore) void { pub fn destroySemaphore(self: *Engine, semaphore: vk.Semaphore) void {
self.device.destroySemaphore(semaphore, &self.vk_allocator.interface); self.device.destroySemaphore(semaphore, ctx.vk_allocator.capture());
} }
pub fn destroyShaderModule(self: *Engine, shader_module: vk.ShaderModule) void { pub fn destroyShaderModule(self: *Engine, shader_module: vk.ShaderModule) void {
self.device.destroyShaderModule(shader_module, &self.vk_allocator.interface); self.device.destroyShaderModule(shader_module, ctx.vk_allocator.capture());
} }
pub fn destroySwapchain(self: *Engine, swapchain: vk.SwapchainKHR) void { pub fn destroySwapchain(self: *Engine, swapchain: vk.SwapchainKHR) void {
self.device.destroySwapchainKHR(swapchain, &self.vk_allocator.interface); self.device.destroySwapchainKHR(swapchain, ctx.vk_allocator.capture());
} }
pub fn deviceWaitIdle(self: *Engine) !void { pub fn deviceWaitIdle(self: *Engine) !void {
@@ -1315,7 +1274,7 @@ pub fn freeDescriptorSet(self: *Engine, descriptor_pool: vk.DescriptorPool, desc
} }
pub fn freeMemory(self: *Engine, device_memory: vk.DeviceMemory) void { pub fn freeMemory(self: *Engine, device_memory: vk.DeviceMemory) void {
self.device.freeMemory(device_memory, &self.vk_allocator.interface); self.device.freeMemory(device_memory, ctx.vk_allocator.capture());
} }
pub fn getBufferMemoryRequirements(self: *Engine, buffer: vk.Buffer) vk.MemoryRequirements { pub fn getBufferMemoryRequirements(self: *Engine, buffer: vk.Buffer) vk.MemoryRequirements {
@@ -1327,12 +1286,12 @@ pub fn getImageMemoryRequirements(self: *Engine, image: vk.Image) vk.MemoryRequi
} }
pub fn getPhysicalDeviceSurfaceCapabilities(self: *Engine) !vk.SurfaceCapabilitiesKHR { pub fn getPhysicalDeviceSurfaceCapabilities(self: *Engine) !vk.SurfaceCapabilitiesKHR {
const surface_capabilities = try self.instance.getPhysicalDeviceSurfaceCapabilitiesKHR(self.physical_device, self.mode.surface.surface); const surface_capabilities = try self.instance.getPhysicalDeviceSurfaceCapabilitiesKHR(self.physical_device, self.surface);
return surface_capabilities; return surface_capabilities;
} }
pub fn getPhysicalDeviceSurfaceFormatsAlloc(self: *Engine, allocator: std.mem.Allocator) ![]vk.SurfaceFormatKHR { pub fn getPhysicalDeviceSurfaceFormatsAlloc(self: *Engine, allocator: std.mem.Allocator) ![]vk.SurfaceFormatKHR {
const surface_formats = try self.instance.getPhysicalDeviceSurfaceFormatsAllocKHR(self.physical_device, self.mode.surface.surface, allocator); const surface_formats = try self.instance.getPhysicalDeviceSurfaceFormatsAllocKHR(self.physical_device, self.surface, allocator);
return surface_formats; return surface_formats;
} }
@@ -1356,11 +1315,9 @@ pub fn unmapMemory(self: *Engine, memory: vk.DeviceMemory) void {
} }
pub fn updateDescriptorSets(self: *Engine, update_info: DescriptorSetsUpdateInfo) !void { pub fn updateDescriptorSets(self: *Engine, update_info: DescriptorSetsUpdateInfo) !void {
var arena = self.makeArena(); const allocator_frame = ctx.allocator_frame;
defer arena.deinit();
const allocator = arena.allocator();
const descriptor_writes = try allocator.alloc(vk.WriteDescriptorSet, update_info.writes.len); const descriptor_writes = try allocator_frame.alloc(vk.WriteDescriptorSet, update_info.writes.len);
for (descriptor_writes, update_info.writes) |*x, write| { for (descriptor_writes, update_info.writes) |*x, write| {
x.* = switch (write.descriptor_infos) { x.* = switch (write.descriptor_infos) {
.image => |y| .{ .image => |y| .{

View File

@@ -1,5 +1,6 @@
const std = @import("std"); const std = @import("std");
const ctx = @import("../AppContext.zig");
const vk = @import("vulkan"); const vk = @import("vulkan");
const CommandBuffer = @import("CommandBuffer.zig"); const CommandBuffer = @import("CommandBuffer.zig");
@@ -51,7 +52,9 @@ pub fn GenericBuffer(comptime Header: type, comptime Element: type) type {
elements: []const Element = &.{}, elements: []const Element = &.{},
}; };
pub fn init(engine: *Engine, init_info: InitInfo) !Self { pub fn init(init_info: InitInfo) !Self {
const engine = ctx.engine;
const array_size = try std.math.mul(u32, init_info.array_capacity, element_size); const array_size = try std.math.mul(u32, init_info.array_capacity, element_size);
const size = try std.math.add(u32, array_offset, array_size); const size = try std.math.add(u32, array_offset, array_size);
@@ -88,8 +91,10 @@ pub fn GenericBuffer(comptime Header: type, comptime Element: type) type {
}; };
} }
pub fn deinit(self: *Self, engine: *Engine) void { pub fn deinit(self: *Self) void {
std.log.scoped(.deinit).debug("Deinitializing {*} with {*}", .{ self, engine }); const engine = ctx.engine;
std.log.scoped(.deinit).debug("Deinitializing {*}", .{self});
engine.freeMemory(self.device_memory); engine.freeMemory(self.device_memory);
engine.destroyBuffer(self.buffer); engine.destroyBuffer(self.buffer);
@@ -97,7 +102,9 @@ pub fn GenericBuffer(comptime Header: type, comptime Element: type) type {
self.* = undefined; self.* = undefined;
} }
pub fn write(self: Self, engine: *Engine, write_info: WriteInfo) !void { pub fn write(self: Self, write_info: WriteInfo) !void {
const engine = ctx.engine;
const element_count = std.math.cast(u32, write_info.elements.len) orelse return error.Overflow; const element_count = std.math.cast(u32, write_info.elements.len) orelse return error.Overflow;
std.debug.assert(write_info.element_offset + element_count <= self.array_capacity); std.debug.assert(write_info.element_offset + element_count <= self.array_capacity);
@@ -141,21 +148,21 @@ pub fn GenericBuffer(comptime Header: type, comptime Element: type) type {
std.debug.assert(regions.items.len > 0); std.debug.assert(regions.items.len > 0);
} }
var staging_buffer: StagingBuffer = try .init(engine, .{ var staging_buffer: StagingBuffer = try .init(.{
.target_queue = self.target_queue, .target_queue = self.target_queue,
.capacity = write_size, .capacity = write_size,
}); });
defer staging_buffer.deinit(engine); defer staging_buffer.deinit();
const staging_memory = try staging_buffer.map(engine); const staging_memory = try staging_buffer.map();
if (write_info.header) |header| { if (write_info.header) |header| {
@memcpy(staging_memory[0..header_size], std.mem.asBytes(&header)); @memcpy(staging_memory[0..header_size], std.mem.asBytes(&header));
} }
@memcpy(staging_memory[array_write_offset..write_size], std.mem.sliceAsBytes(write_info.elements)); @memcpy(staging_memory[array_write_offset..write_size], std.mem.sliceAsBytes(write_info.elements));
staging_buffer.unmap(engine); staging_buffer.unmap();
var command_buffer: CommandBuffer = try .init(engine, .transfer); var command_buffer: CommandBuffer = try .init(.transfer);
defer command_buffer.deinit(engine); defer command_buffer.deinit();
try command_buffer.beginCommandBuffer(); try command_buffer.beginCommandBuffer();
command_buffer.copyBuffer(staging_buffer.buffer, self.buffer, regions.items); command_buffer.copyBuffer(staging_buffer.buffer, self.buffer, regions.items);
@@ -164,11 +171,13 @@ pub fn GenericBuffer(comptime Header: type, comptime Element: type) type {
const fence = try engine.createFence(.{}); const fence = try engine.createFence(.{});
defer engine.destroyFence(fence); defer engine.destroyFence(fence);
try command_buffer.submit(engine, .{ .fence = fence }); try command_buffer.submit(.{ .fence = fence });
try engine.waitForFence(fence); try engine.waitForFence(fence);
} }
pub fn writeRaw(self: Self, engine: *Engine, data_offset: u32, data: []const u8) !void { pub fn writeRaw(self: Self, data_offset: u32, data: []const u8) !void {
const engine = ctx.engine;
const array_size = self.array_capacity * element_size; const array_size = self.array_capacity * element_size;
const size = array_offset + array_size; const size = array_offset + array_size;

View File

@@ -1,20 +0,0 @@
const QueueSharingMode = @This();
const vk = @import("vulkan");
threadlocal var qsm: QueueSharingMode = undefined;
buffer: [2]u32,
sharing_mode: vk.SharingMode,
queue_family_index_count: u32,
p_queue_family_indices: ?[*]const u32,
pub fn resolve(a: u32, b: u32) *const QueueSharingMode {
const self = &qsm;
const same = a == b;
self.buffer = .{ a, b };
self.sharing_mode = if (same) .exclusive else .concurrent;
self.queue_family_index_count = if (same) 0 else 2;
self.p_queue_family_indices = if (same) null else &self.buffer;
return self;
}

View File

@@ -1,6 +1,7 @@
const Skybox = @This(); const Skybox = @This();
const std = @import("std"); const std = @import("std");
const ctx = @import("../AppContext.zig");
const media = @import("media"); const media = @import("media");
const shaders = @import("../shaders.zig"); const shaders = @import("../shaders.zig");
const vk = @import("vulkan"); const vk = @import("vulkan");
@@ -29,14 +30,15 @@ pipeline: vk.Pipeline,
pub fn load( pub fn load(
filename: []const u8, filename: []const u8,
engine: *Engine,
swapchain: *Swapchain,
stbi: *media.stbi, stbi: *media.stbi,
cube_size: u32, cube_size: u32,
global_uniforms_buffer: vk.Buffer, global_uniforms_buffer: vk.Buffer,
temp_allocator: std.mem.Allocator,
io: std.Io,
) !Skybox { ) !Skybox {
const allocator_frame = ctx.allocator_frame;
const io = ctx.io;
const engine = ctx.engine;
const swapchain = ctx.swapchain;
std.log.debug("Loading skybox \"{s}\"...", .{filename}); std.log.debug("Loading skybox \"{s}\"...", .{filename});
// --- LOAD IMAGE FROM MEMORY ---------------------------------------------- // --- LOAD IMAGE FROM MEMORY ----------------------------------------------
@@ -46,8 +48,7 @@ pub fn load(
var dir = try cwd.openDir(io, "assets", .{}); var dir = try cwd.openDir(io, "assets", .{});
defer dir.close(io); defer dir.close(io);
const file_buf = try dir.readFileAlloc(io, filename, temp_allocator, .unlimited); const file_buf = try dir.readFileAlloc(io, filename, allocator_frame, .unlimited);
defer temp_allocator.free(file_buf);
const img = try stbi.loadHdrBuf(file_buf); const img = try stbi.loadHdrBuf(file_buf);
defer stbi.freeHdr(img); defer stbi.freeHdr(img);
@@ -68,15 +69,15 @@ pub fn load(
// --- LOAD IMAGE INTO STAGING BUFFER -------------------------------------- // --- LOAD IMAGE INTO STAGING BUFFER --------------------------------------
var staging_buffer = try StagingBuffer.init(engine, .{ var staging_buffer = try StagingBuffer.init(.{
.capacity = @intCast(img.width * img.height * @sizeOf(vm.ColorHdr)), .capacity = @intCast(img.width * img.height * @sizeOf(vm.ColorHdr)),
.target_queue = .compute, .target_queue = .compute,
}); });
defer staging_buffer.deinit(engine); defer staging_buffer.deinit();
const staging_memory = try staging_buffer.map(engine); const staging_memory = try staging_buffer.map();
@memcpy(staging_memory, @as([*]const u8, @ptrCast(img.data))); @memcpy(staging_memory, @as([*]const u8, @ptrCast(img.data)));
staging_buffer.unmap(engine); staging_buffer.unmap();
// --- CREATE EQUIRECTANGULAR IMAGE ---------------------------------------- // --- CREATE EQUIRECTANGULAR IMAGE ----------------------------------------
@@ -126,8 +127,8 @@ pub fn load(
// --- TRANSITION TO TRANSFER_DST_OPTIMAL AND COPY ------------------------- // --- TRANSITION TO TRANSFER_DST_OPTIMAL AND COPY -------------------------
var transfer_command_buffer = try CommandBuffer.init(engine, .transfer); var transfer_command_buffer = try CommandBuffer.init(.transfer);
defer transfer_command_buffer.deinit(engine); defer transfer_command_buffer.deinit();
try transfer_command_buffer.beginCommandBuffer(); try transfer_command_buffer.beginCommandBuffer();
transfer_command_buffer.pipelineBarrier(.{ transfer_command_buffer.pipelineBarrier(.{
@@ -174,14 +175,14 @@ pub fn load(
); );
try transfer_command_buffer.endCommandBuffer(); try transfer_command_buffer.endCommandBuffer();
try transfer_command_buffer.submit(engine, .{ try transfer_command_buffer.submit(.{
.signal_semaphores = &.{semaphore_transfer_transition}, .signal_semaphores = &.{semaphore_transfer_transition},
}); });
// --- TRANSITION TO SHADER_READ_ONLY_OPTIMAL ------------------------------ // --- TRANSITION TO SHADER_READ_ONLY_OPTIMAL ------------------------------
var transition1_command_buffer = try CommandBuffer.init(engine, .compute); var transition1_command_buffer = try CommandBuffer.init(.compute);
defer transition1_command_buffer.deinit(engine); defer transition1_command_buffer.deinit();
try transition1_command_buffer.beginCommandBuffer(); try transition1_command_buffer.beginCommandBuffer();
transition1_command_buffer.pipelineBarrier(.{ transition1_command_buffer.pipelineBarrier(.{
@@ -208,7 +209,7 @@ pub fn load(
}); });
try transition1_command_buffer.endCommandBuffer(); try transition1_command_buffer.endCommandBuffer();
try transition1_command_buffer.submit(engine, .{ try transition1_command_buffer.submit(.{
.wait_semaphores = &.{.{ .semaphore = semaphore_transfer_transition }}, .wait_semaphores = &.{.{ .semaphore = semaphore_transfer_transition }},
.signal_semaphores = &.{semaphore_transition_compute}, .signal_semaphores = &.{semaphore_transition_compute},
}); });
@@ -326,7 +327,7 @@ pub fn load(
.base_pipeline_handle = .null_handle, .base_pipeline_handle = .null_handle,
.base_pipeline_index = -1, .base_pipeline_index = -1,
}, },
}, &engine.vk_allocator.interface, @ptrCast(&compute_pipeline)); }, ctx.vk_allocator.capture(), @ptrCast(&compute_pipeline));
defer engine.destroyPipeline(compute_pipeline); defer engine.destroyPipeline(compute_pipeline);
const compute_descriptor_pool = try engine.createDescriptorPool(.{ const compute_descriptor_pool = try engine.createDescriptorPool(.{
@@ -392,8 +393,8 @@ pub fn load(
// 1. Run compute shader // 1. Run compute shader
var compute_command_buffer = try CommandBuffer.init(engine, .compute); var compute_command_buffer = try CommandBuffer.init(.compute);
defer compute_command_buffer.deinit(engine); defer compute_command_buffer.deinit();
try compute_command_buffer.beginCommandBuffer(); try compute_command_buffer.beginCommandBuffer();
compute_command_buffer.pipelineBarrier(.{ compute_command_buffer.pipelineBarrier(.{
@@ -423,7 +424,7 @@ pub fn load(
compute_command_buffer.proxy.dispatch(@divExact(cube_size, 8), @divExact(cube_size, 8), 6); compute_command_buffer.proxy.dispatch(@divExact(cube_size, 8), @divExact(cube_size, 8), 6);
try compute_command_buffer.endCommandBuffer(); try compute_command_buffer.endCommandBuffer();
try compute_command_buffer.submit(engine, .{ try compute_command_buffer.submit(.{
.wait_semaphores = &.{ .wait_semaphores = &.{
.{ .{
.semaphore = semaphore_transition_compute, .semaphore = semaphore_transition_compute,
@@ -435,8 +436,8 @@ pub fn load(
// 2. Transition cubemap // 2. Transition cubemap
var transition2_command_buffer = try CommandBuffer.init(engine, .graphics); var transition2_command_buffer = try CommandBuffer.init(.graphics);
defer transition2_command_buffer.deinit(engine); defer transition2_command_buffer.deinit();
try transition2_command_buffer.beginCommandBuffer(); try transition2_command_buffer.beginCommandBuffer();
transition2_command_buffer.pipelineBarrier(.{ transition2_command_buffer.pipelineBarrier(.{
@@ -463,7 +464,7 @@ pub fn load(
}); });
try transition2_command_buffer.endCommandBuffer(); try transition2_command_buffer.endCommandBuffer();
try transition2_command_buffer.submit(engine, .{ try transition2_command_buffer.submit(.{
.wait_semaphores = &.{.{ .semaphore = semaphore_compute_transition }}, .wait_semaphores = &.{.{ .semaphore = semaphore_compute_transition }},
.fence = fence, .fence = fence,
}); });
@@ -474,12 +475,12 @@ pub fn load(
// --- SKYBOX PIPELINE ----------------------------------------------------- // --- SKYBOX PIPELINE -----------------------------------------------------
var vertex_buffer = try GenericBuffer(void, vm.Vector3).init(engine, .{ var vertex_buffer = try GenericBuffer(void, vm.Vector3).init(.{
.usage = .vertex, .usage = .vertex,
.target_queue = .graphics, .target_queue = .graphics,
.array_capacity = 8, .array_capacity = 8,
}); });
errdefer vertex_buffer.deinit(engine); errdefer vertex_buffer.deinit();
// 6━━━━7 // 6━━━━7
// ╱│ ╱┃ // ╱│ ╱┃
@@ -489,7 +490,7 @@ pub fn load(
// ┃╱ ┃╱ │╱ // ┃╱ ┃╱ │╱
// 0━━━━1 O────X // 0━━━━1 O────X
try vertex_buffer.write(engine, .{ try vertex_buffer.write(.{
.elements = &.{ .elements = &.{
.init(-1, -1, -1), .init(-1, -1, -1),
.init(1, -1, -1), .init(1, -1, -1),
@@ -502,14 +503,14 @@ pub fn load(
}, },
}); });
var index_buffer = try GenericBuffer(void, u16).init(engine, .{ var index_buffer = try GenericBuffer(void, u16).init(.{
.usage = .index, .usage = .index,
.target_queue = .graphics, .target_queue = .graphics,
.array_capacity = 36, .array_capacity = 36,
}); });
errdefer index_buffer.deinit(engine); errdefer index_buffer.deinit();
try index_buffer.write(engine, .{ try index_buffer.write(.{
.elements = &.{ .elements = &.{
// Positive X // Positive X
3, 1, 7, 3, 1, 7,
@@ -760,15 +761,17 @@ pub fn load(
}; };
} }
pub fn deinit(self: *Skybox, engine: *Engine) void { pub fn deinit(self: *Skybox) void {
const engine = ctx.engine;
engine.destroyPipeline(self.pipeline); engine.destroyPipeline(self.pipeline);
engine.destroyPipelineLayout(self.pipeline_layout); engine.destroyPipelineLayout(self.pipeline_layout);
engine.destroyDescriptorPool(self.descriptor_pool); engine.destroyDescriptorPool(self.descriptor_pool);
engine.destroyDescriptorSetLayout(self.descriptor_set_layout); engine.destroyDescriptorSetLayout(self.descriptor_set_layout);
engine.destroySampler(self.sampler); engine.destroySampler(self.sampler);
self.index_buffer.deinit(engine); self.index_buffer.deinit();
self.vertex_buffer.deinit(engine); self.vertex_buffer.deinit();
engine.destroyImageView(self.image_view); engine.destroyImageView(self.image_view);
engine.freeMemory(self.device_memory); engine.freeMemory(self.device_memory);

View File

@@ -1,6 +1,7 @@
const StagingBuffer = @This(); const StagingBuffer = @This();
const std = @import("std"); const std = @import("std");
const ctx = @import("../AppContext.zig");
const vk = @import("vulkan"); const vk = @import("vulkan");
const Engine = @import("Engine.zig"); const Engine = @import("Engine.zig");
@@ -15,7 +16,9 @@ pub const InitInfo = struct {
capacity: u32, capacity: u32,
}; };
pub fn init(engine: *Engine, init_info: InitInfo) !StagingBuffer { pub fn init(init_info: InitInfo) !StagingBuffer {
const engine = ctx.engine;
const target_queue_family = switch (init_info.target_queue) { const target_queue_family = switch (init_info.target_queue) {
.graphics => engine.graphics_queue.allocation.family, .graphics => engine.graphics_queue.allocation.family,
.compute => engine.compute_queue.allocation.family, .compute => engine.compute_queue.allocation.family,
@@ -50,8 +53,10 @@ pub fn init(engine: *Engine, init_info: InitInfo) !StagingBuffer {
}; };
} }
pub fn deinit(self: *StagingBuffer, engine: *Engine) void { pub fn deinit(self: *StagingBuffer) void {
std.log.scoped(.deinit).debug("Deinitializing {*} with {*}", .{ self, engine }); const engine = ctx.engine;
std.log.scoped(.deinit).debug("Deinitializing {*}", .{self});
engine.freeMemory(self.device_memory); engine.freeMemory(self.device_memory);
engine.destroyBuffer(self.buffer); engine.destroyBuffer(self.buffer);
@@ -59,16 +64,20 @@ pub fn deinit(self: *StagingBuffer, engine: *Engine) void {
self.* = undefined; self.* = undefined;
} }
pub fn map(self: StagingBuffer, engine: *Engine) ![]u8 { pub fn map(self: StagingBuffer) ![]u8 {
const mapped_memory = try self.mapPartial(engine, 0, self.capacity); const mapped_memory = try self.mapPartial(0, self.capacity);
return mapped_memory; return mapped_memory;
} }
pub fn mapPartial(self: StagingBuffer, engine: *Engine, offset: u32, len: u32) ![]u8 { pub fn mapPartial(self: StagingBuffer, offset: u32, len: u32) ![]u8 {
const engine = ctx.engine;
const mapped_memory = try engine.mapMemory(self.device_memory, offset, len, .{}); const mapped_memory = try engine.mapMemory(self.device_memory, offset, len, .{});
return mapped_memory; return mapped_memory;
} }
pub fn unmap(self: StagingBuffer, engine: *Engine) void { pub fn unmap(self: StagingBuffer) void {
const engine = ctx.engine;
engine.unmapMemory(self.device_memory); engine.unmapMemory(self.device_memory);
} }

View File

@@ -1,11 +1,11 @@
const Swapchain = @This(); const Swapchain = @This();
const std = @import("std"); const std = @import("std");
const ctx = @import("../AppContext.zig");
const vk = @import("vulkan"); const vk = @import("vulkan");
const CommandBuffer = @import("CommandBuffer.zig"); const CommandBuffer = @import("CommandBuffer.zig");
const Engine = @import("Engine.zig"); const Engine = @import("Engine.zig");
const QSM = @import("QueueSharingMode.zig");
const Texture = @import("Texture.zig"); const Texture = @import("Texture.zig");
const WaitSemaphore = @import("WaitSemaphore.zig"); const WaitSemaphore = @import("WaitSemaphore.zig");
@@ -13,6 +13,7 @@ params: Params,
extent: vk.Extent2D = .{ .width = 0, .height = 0 }, extent: vk.Extent2D = .{ .width = 0, .height = 0 },
swapchain: vk.SwapchainKHR = .null_handle, swapchain: vk.SwapchainKHR = .null_handle,
/// Allocated with `allocator_general`.
swapchain_images: []SwapchainImage = &.{}, swapchain_images: []SwapchainImage = &.{},
depth_texture: ?Texture = null, depth_texture: ?Texture = null,
image_index: ?u32 = null, image_index: ?u32 = null,
@@ -24,28 +25,33 @@ pub const PresentResult = enum {
suboptimal, suboptimal,
}; };
pub fn init(engine: *Engine) !Swapchain { pub fn init() !*Swapchain {
const params: Params = try .init(engine); const allocator_persistent = ctx.allocator_persistent;
var swapchain: Swapchain = .{
.params = params, const swapchain = try allocator_persistent.create(Swapchain);
errdefer allocator_persistent.destroy(swapchain);
swapchain.* = .{
.params = try .init(),
}; };
try recreate(&swapchain, engine); try swapchain.recreate();
return swapchain; return swapchain;
} }
pub fn deinit(self: *Swapchain, engine: *Engine) void { pub fn deinit(self: *Swapchain) void {
std.log.scoped(.deinit).debug("Deinitializing {*} with {*}", .{ self, engine }); const allocator_general = ctx.allocator_general;
const engine = ctx.engine;
const allocator = engine.vk_allocator.allocator; std.log.scoped(.deinit).debug("Deinitializing {*}", .{self});
for (self.swapchain_images) |*swapchain_image| { for (self.swapchain_images) |*swapchain_image| {
swapchain_image.deinit(engine); swapchain_image.deinit();
} }
allocator.free(self.swapchain_images); allocator_general.free(self.swapchain_images);
if (self.depth_texture) |*depth_texture| { if (self.depth_texture) |*depth_texture| {
depth_texture.deinit(engine); depth_texture.deinit();
} }
if (self.swapchain != .null_handle) { if (self.swapchain != .null_handle) {
@@ -58,11 +64,15 @@ pub fn deinit(self: *Swapchain, engine: *Engine) void {
self.* = undefined; self.* = undefined;
} }
pub fn recreate(self: *Swapchain, engine: *Engine) !void { pub fn recreate(self: *Swapchain) !void {
try engine.deviceWaitIdle(); // TODO LMAO const allocator_general = ctx.allocator_general;
const allocator_frame = ctx.allocator_frame;
const engine = ctx.engine;
const mode = &engine.mode.surface; // TODO This is very "LMAO just wait for all" way to synchronize. It might
const allocator = engine.vk_allocator.allocator; // be the only viable option, though. Either way, we should research what
// is "the way".
try engine.deviceWaitIdle();
const old_swapchain = self.swapchain; const old_swapchain = self.swapchain;
const old_swapchain_images = self.swapchain_images; const old_swapchain_images = self.swapchain_images;
@@ -70,7 +80,7 @@ pub fn recreate(self: *Swapchain, engine: *Engine) !void {
const old_semaphore_image_acquired = self.semaphore_image_acquired; const old_semaphore_image_acquired = self.semaphore_image_acquired;
const old_fence = self.fence; const old_fence = self.fence;
const extent = try getCurrentExtent(engine); const extent = try getCurrentExtent();
std.log.debug("Recreating swapchain with extent of {d}×{d}...", .{ extent.width, extent.height }); std.log.debug("Recreating swapchain with extent of {d}×{d}...", .{ extent.width, extent.height });
@@ -78,7 +88,7 @@ pub fn recreate(self: *Swapchain, engine: *Engine) !void {
const surface_capabilities = try engine.getPhysicalDeviceSurfaceCapabilities(); const surface_capabilities = try engine.getPhysicalDeviceSurfaceCapabilities();
const new_swapchain = try engine.createSwapchain(.{ const new_swapchain = try engine.createSwapchain(.{
.surface = mode.surface, .surface = engine.surface,
.min_image_count = self.params.image_count, .min_image_count = self.params.image_count,
.image_format = self.params.surface_format.format, .image_format = self.params.surface_format.format,
.image_color_space = self.params.surface_format.color_space, .image_color_space = self.params.surface_format.color_space,
@@ -87,7 +97,7 @@ pub fn recreate(self: *Swapchain, engine: *Engine) !void {
.image_usage = .{ .color_attachment_bit = true }, .image_usage = .{ .color_attachment_bit = true },
.queue_family_indices = &.{ .queue_family_indices = &.{
engine.graphics_queue.allocation.family, engine.graphics_queue.allocation.family,
mode.presentation_queue.allocation.family, engine.presentation_queue.allocation.family,
}, },
.pre_transform = surface_capabilities.current_transform, .pre_transform = surface_capabilities.current_transform,
.composite_alpha = .{ .opaque_bit_khr = true }, .composite_alpha = .{ .opaque_bit_khr = true },
@@ -105,13 +115,13 @@ pub fn recreate(self: *Swapchain, engine: *Engine) !void {
// null and deinit the old swapchain and images. // null and deinit the old swapchain and images.
for (old_swapchain_images) |*swapchain_image| { for (old_swapchain_images) |*swapchain_image| {
swapchain_image.deinit(engine); swapchain_image.deinit();
} }
allocator.free(self.swapchain_images); allocator_general.free(self.swapchain_images);
self.swapchain_images = &.{}; self.swapchain_images = &.{};
if (old_depth_texture.*) |*depth_texture| { if (old_depth_texture.*) |*depth_texture| {
depth_texture.deinit(engine); depth_texture.deinit();
} }
if (old_swapchain != .null_handle) { if (old_swapchain != .null_handle) {
@@ -134,28 +144,27 @@ pub fn recreate(self: *Swapchain, engine: *Engine) !void {
// --- CREATE DEPTH TEXTURE ------------------------------------------------ // --- CREATE DEPTH TEXTURE ------------------------------------------------
var new_depth_texture = try Texture.init(engine, .{ var new_depth_texture = try Texture.init(.{
.width = extent.width, .width = extent.width,
.height = extent.height, .height = extent.height,
.target_queue = .graphics, .target_queue = .graphics,
.usage = .depth, .usage = .depth,
.name = "@Depth", .name = "@Depth",
}); });
errdefer new_depth_texture.deinit(engine); errdefer new_depth_texture.deinit();
// --- CREATE NEW SWAPCHAIN IMAGES ----------------------------------------- // --- CREATE NEW SWAPCHAIN IMAGES -----------------------------------------
const new_swapchain_images = blk: { const new_swapchain_images = blk: {
const images = try engine.getSwapchainImagesAlloc(new_swapchain, allocator); const images = try engine.getSwapchainImagesAlloc(new_swapchain, allocator_frame);
defer allocator.free(images);
var swapchain_images: std.ArrayList(SwapchainImage) = try .initCapacity(allocator, images.len); var swapchain_images: std.ArrayList(SwapchainImage) = try .initCapacity(allocator_general, images.len);
errdefer swapchain_images.deinit(allocator); errdefer swapchain_images.deinit(allocator_general);
errdefer for (swapchain_images.items) |*x| x.deinit(engine); errdefer for (swapchain_images.items) |*x| x.deinit();
for (images, 0..) |image, index| { for (images, 0..) |image, index| {
swapchain_images.appendAssumeCapacity(try SwapchainImage.init(engine, .{ swapchain_images.appendAssumeCapacity(try SwapchainImage.init(.{
.image = image, .image = image,
.format = self.params.surface_format.format, .format = self.params.surface_format.format,
.extent = extent, .extent = extent,
@@ -164,13 +173,13 @@ pub fn recreate(self: *Swapchain, engine: *Engine) !void {
})); }));
} }
break :blk try swapchain_images.toOwnedSlice(allocator); break :blk try swapchain_images.toOwnedSlice(allocator_general);
}; };
errdefer { errdefer {
for (new_swapchain_images) |*swapchain_image| { for (new_swapchain_images) |*swapchain_image| {
swapchain_image.deinit(engine); swapchain_image.deinit();
} }
allocator.free(new_swapchain_images); allocator_general.free(new_swapchain_images);
} }
// --- CREATE SEMAPHORES AND FENCES ---------------------------------------- // --- CREATE SEMAPHORES AND FENCES ----------------------------------------
@@ -196,7 +205,9 @@ pub fn recreate(self: *Swapchain, engine: *Engine) !void {
self.fence = fence; self.fence = fence;
} }
pub fn acquire(self: *Swapchain, engine: *Engine) !void { pub fn acquire(self: *Swapchain) !void {
const engine = ctx.engine;
try engine.waitForFence(self.fence); try engine.waitForFence(self.fence);
try engine.resetFence(self.fence); try engine.resetFence(self.fence);
@@ -212,14 +223,15 @@ pub const PresentInfo = struct {
wait_semaphores: []const WaitSemaphore = &.{}, wait_semaphores: []const WaitSemaphore = &.{},
}; };
pub fn present(self: *Swapchain, engine: *Engine, present_info: PresentInfo) !void { pub fn present(self: *Swapchain, present_info: PresentInfo) !void {
const allocator = engine.vk_allocator.allocator; const allocator_frame = ctx.allocator_frame;
const engine = ctx.engine;
const current_swapchain_image = &self.swapchain_images[self.image_index.?]; const current_swapchain_image = &self.swapchain_images[self.image_index.?];
// --- SUBMIT COMMAND BUFFER ----------------------------------------------- // --- SUBMIT COMMAND BUFFER -----------------------------------------------
var wait_semaphores: std.ArrayList(WaitSemaphore) = try .initCapacity(allocator, 1 + present_info.wait_semaphores.len); var wait_semaphores: std.ArrayList(WaitSemaphore) = try .initCapacity(allocator_frame, 1 + present_info.wait_semaphores.len);
defer wait_semaphores.deinit(allocator);
wait_semaphores.appendAssumeCapacity(.{ wait_semaphores.appendAssumeCapacity(.{
.semaphore = current_swapchain_image.semaphore_image_acquired, .semaphore = current_swapchain_image.semaphore_image_acquired,
@@ -227,7 +239,7 @@ pub fn present(self: *Swapchain, engine: *Engine, present_info: PresentInfo) !vo
}); });
wait_semaphores.appendSliceAssumeCapacity(present_info.wait_semaphores); wait_semaphores.appendSliceAssumeCapacity(present_info.wait_semaphores);
try present_info.command_buffer.submit(engine, .{ try present_info.command_buffer.submit(.{
.wait_semaphores = wait_semaphores.items, .wait_semaphores = wait_semaphores.items,
.signal_semaphores = &.{current_swapchain_image.semaphore_render_finished}, .signal_semaphores = &.{current_swapchain_image.semaphore_render_finished},
.fence = self.fence, .fence = self.fence,
@@ -242,15 +254,17 @@ pub fn present(self: *Swapchain, engine: *Engine, present_info: PresentInfo) !vo
}); });
} }
fn getCurrentExtent(engine: *Engine) !vk.Extent2D { fn getCurrentExtent() !vk.Extent2D {
const mode = &engine.mode.surface; const window = ctx.window;
const engine = ctx.engine;
const surface_capabilities = try engine.getPhysicalDeviceSurfaceCapabilities(); const surface_capabilities = try engine.getPhysicalDeviceSurfaceCapabilities();
if (surface_capabilities.current_extent.width != std.math.maxInt(u32) and surface_capabilities.current_extent.height != std.math.maxInt(u32)) { if (surface_capabilities.current_extent.width != std.math.maxInt(u32) and surface_capabilities.current_extent.height != std.math.maxInt(u32)) {
return surface_capabilities.current_extent; return surface_capabilities.current_extent;
} }
const framebuffer_width, const framebuffer_height = mode.window.getFramebufferSize(); const framebuffer_width, const framebuffer_height = window.getFramebufferSize();
return .{ return .{
.width = std.math.clamp( .width = std.math.clamp(
@@ -270,14 +284,14 @@ const Params = struct {
surface_format: vk.SurfaceFormatKHR, surface_format: vk.SurfaceFormatKHR,
image_count: u32, image_count: u32,
pub fn init(engine: *Engine) !Params { pub fn init() !Params {
const allocator = engine.vk_allocator.allocator; const allocator_frame = ctx.allocator_frame;
const engine = ctx.engine;
const surface_capabilities = try engine.getPhysicalDeviceSurfaceCapabilities(); const surface_capabilities = try engine.getPhysicalDeviceSurfaceCapabilities();
const surface_format = blk: { const surface_format = blk: {
const surface_formats = try engine.getPhysicalDeviceSurfaceFormatsAlloc(allocator); const surface_formats = try engine.getPhysicalDeviceSurfaceFormatsAlloc(allocator_frame);
defer allocator.free(surface_formats);
// Look for 8-bit BGRA sRGB surface format. // Look for 8-bit BGRA sRGB surface format.
@@ -323,7 +337,9 @@ const SwapchainImage = struct {
index: usize, index: usize,
}; };
fn init(engine: *Engine, props: InitProps) !SwapchainImage { fn init(props: InitProps) !SwapchainImage {
const engine = ctx.engine;
const image_view = try engine.createImageView(.{ const image_view = try engine.createImageView(.{
.image = props.image, .image = props.image,
.view_type = .@"2d", .view_type = .@"2d",
@@ -355,8 +371,10 @@ const SwapchainImage = struct {
}; };
} }
fn deinit(self: *SwapchainImage, engine: *Engine) void { fn deinit(self: *SwapchainImage) void {
std.log.scoped(.deinit).debug("Deinitializing {*} with {*}", .{ self, engine }); const engine = ctx.engine;
std.log.scoped(.deinit).debug("Deinitializing {*}", .{self});
engine.destroySemaphore(self.semaphore_render_finished); engine.destroySemaphore(self.semaphore_render_finished);
engine.destroySemaphore(self.semaphore_image_acquired); engine.destroySemaphore(self.semaphore_image_acquired);

View File

@@ -1,6 +1,7 @@
const Texture = @This(); const Texture = @This();
const std = @import("std"); const std = @import("std");
const ctx = @import("../AppContext.zig");
const vk = @import("vulkan"); const vk = @import("vulkan");
const CommandBuffer = @import("CommandBuffer.zig"); const CommandBuffer = @import("CommandBuffer.zig");
@@ -79,18 +80,15 @@ width: u32,
height: u32, height: u32,
usage: Usage, usage: Usage,
pub fn init(engine: *Engine, init_info: InitInfo) !Texture { pub fn init(init_info: InitInfo) !Texture {
const engine = ctx.engine;
const target_queue_family = switch (init_info.target_queue) { const target_queue_family = switch (init_info.target_queue) {
.graphics => engine.graphics_queue.allocation.family, .graphics => engine.graphics_queue.allocation.family,
.compute => engine.compute_queue.allocation.family, .compute => engine.compute_queue.allocation.family,
}; };
const transfer_queue_family = engine.transfer_queue.allocation.family; const transfer_queue_family = engine.transfer_queue.allocation.family;
const queue_family_indices: []const u32 = if (init_info.usage == .depth)
&.{target_queue_family}
else
&.{ target_queue_family, transfer_queue_family };
const image = try engine.createImage(.{ const image = try engine.createImage(.{
.image_type = .@"2d", .image_type = .@"2d",
.format = init_info.usage.vkFormat(), .format = init_info.usage.vkFormat(),
@@ -108,7 +106,7 @@ pub fn init(engine: *Engine, init_info: InitInfo) !Texture {
.transfer_dst_bit = init_info.usage != .depth, .transfer_dst_bit = init_info.usage != .depth,
.sampled_bit = init_info.usage != .depth, .sampled_bit = init_info.usage != .depth,
}, },
.queue_family_indices = queue_family_indices, .queue_family_indices = &.{ target_queue_family, transfer_queue_family },
.initial_layout = .undefined, .initial_layout = .undefined,
}); });
errdefer engine.destroyImage(image); errdefer engine.destroyImage(image);
@@ -156,8 +154,10 @@ pub fn init(engine: *Engine, init_info: InitInfo) !Texture {
}; };
} }
pub fn deinit(self: *Texture, engine: *Engine) void { pub fn deinit(self: *Texture) void {
std.log.scoped(.deinit).debug("Deinitializing {*} with {*}", .{ self, engine }); const engine = ctx.engine;
std.log.scoped(.deinit).debug("Deinitializing {*}", .{self});
engine.destroyImageView(self.image_view); engine.destroyImageView(self.image_view);
engine.freeMemory(self.device_memory); engine.freeMemory(self.device_memory);
@@ -166,7 +166,9 @@ pub fn deinit(self: *Texture, engine: *Engine) void {
self.* = undefined; self.* = undefined;
} }
pub fn writeTexels(self: Texture, comptime TexelType: type, engine: *Engine, texels: []const TexelType) !void { pub fn writeTexels(self: Texture, comptime TexelType: type, texels: []const TexelType) !void {
const engine = ctx.engine;
const texel_count = try std.math.mul(u32, self.width, self.height); const texel_count = try std.math.mul(u32, self.width, self.height);
std.debug.assert(texels.len == texel_count); std.debug.assert(texels.len == texel_count);
switch (self.usage) { switch (self.usage) {
@@ -176,7 +178,7 @@ pub fn writeTexels(self: Texture, comptime TexelType: type, engine: *Engine, tex
try self.writeRaw(engine, std.mem.sliceAsBytes(texels)); try self.writeRaw(engine, std.mem.sliceAsBytes(texels));
} }
pub fn writeSamples(self: Texture, comptime SampleType: type, engine: *Engine, samples: []const SampleType) !void { pub fn writeSamples(self: Texture, comptime SampleType: type, samples: []const SampleType) !void {
const texel_count = try std.math.mul(u32, self.width, self.height); const texel_count = try std.math.mul(u32, self.width, self.height);
const sample_count = try std.math.mul(u32, texel_count, self.usage.samplesPerTexel()); const sample_count = try std.math.mul(u32, texel_count, self.usage.samplesPerTexel());
std.debug.assert(samples.len == sample_count); std.debug.assert(samples.len == sample_count);
@@ -184,29 +186,31 @@ pub fn writeSamples(self: Texture, comptime SampleType: type, engine: *Engine, s
inline else => |x| std.debug.assert(SampleType == x.SampleType()), inline else => |x| std.debug.assert(SampleType == x.SampleType()),
} }
try self.writeRaw(engine, std.mem.sliceAsBytes(samples)); try self.writeRaw(std.mem.sliceAsBytes(samples));
} }
pub fn writeRaw(self: Texture, engine: *Engine, data: []const u8) !void { pub fn writeRaw(self: Texture, data: []const u8) !void {
const engine = ctx.engine;
const texel_count = try std.math.mul(u32, self.width, self.height); const texel_count = try std.math.mul(u32, self.width, self.height);
const byte_length = try std.math.mul(u32, texel_count, self.usage.bytesPerTexel()); const byte_length = try std.math.mul(u32, texel_count, self.usage.bytesPerTexel());
std.debug.assert(data.len == byte_length); std.debug.assert(data.len == byte_length);
std.debug.assert(self.usage != .depth); std.debug.assert(self.usage != .depth);
var staging_buffer = try StagingBuffer.init(engine, .{ var staging_buffer = try StagingBuffer.init(.{
.capacity = @intCast(byte_length), .capacity = @intCast(byte_length),
.target_queue = self.target_queue, .target_queue = self.target_queue,
}); });
defer staging_buffer.deinit(engine); defer staging_buffer.deinit();
const staging_memory = try staging_buffer.map(engine); const staging_memory = try staging_buffer.map();
@memcpy(staging_memory, data); @memcpy(staging_memory, data);
staging_buffer.unmap(engine); staging_buffer.unmap();
// --- TRANSITION TO TRANSFER_DST_OPTIMAL AND COPY ----------------- // --- TRANSITION TO TRANSFER_DST_OPTIMAL AND COPY -----------------
var transfer_command_buffer: CommandBuffer = try .init(engine, .transfer); var transfer_command_buffer: CommandBuffer = try .init(.transfer);
defer transfer_command_buffer.deinit(engine); defer transfer_command_buffer.deinit();
try transfer_command_buffer.beginCommandBuffer(); try transfer_command_buffer.beginCommandBuffer();
@@ -259,14 +263,14 @@ pub fn writeRaw(self: Texture, engine: *Engine, data: []const u8) !void {
const semaphore = try engine.createSemaphore(); const semaphore = try engine.createSemaphore();
defer engine.destroySemaphore(semaphore); defer engine.destroySemaphore(semaphore);
try transfer_command_buffer.submit(engine, .{ try transfer_command_buffer.submit(.{
.signal_semaphores = &.{semaphore}, .signal_semaphores = &.{semaphore},
}); });
// --- TRANSITION TO SHADER_READ_ONLY_OPTIMAL ---------------------- // --- TRANSITION TO SHADER_READ_ONLY_OPTIMAL ----------------------
var graphics_command_buffer: CommandBuffer = try .init(engine, .graphics); var graphics_command_buffer: CommandBuffer = try .init(.graphics);
defer graphics_command_buffer.deinit(engine); defer graphics_command_buffer.deinit();
try graphics_command_buffer.beginCommandBuffer(); try graphics_command_buffer.beginCommandBuffer();
@@ -298,7 +302,7 @@ pub fn writeRaw(self: Texture, engine: *Engine, data: []const u8) !void {
const fence = try engine.createFence(.{}); const fence = try engine.createFence(.{});
defer engine.destroyFence(fence); defer engine.destroyFence(fence);
try graphics_command_buffer.submit(engine, .{ try graphics_command_buffer.submit(.{
.wait_semaphores = &.{.{ .semaphore = semaphore }}, .wait_semaphores = &.{.{ .semaphore = semaphore }},
.fence = fence, .fence = fence,
}); });

View File

@@ -1,13 +1,43 @@
const VkAllocator = @This(); const VkAllocator = @This();
const std = @import("std"); const std = @import("std");
const ctx = @import("../AppContext.zig");
const vk = @import("vulkan"); const vk = @import("vulkan");
allocator: std.mem.Allocator, const Allocation = struct {
allocations: std.AutoHashMapUnmanaged(*anyopaque, usize) = .empty, len: usize,
mutex: std.Io.Mutex = .init, stack_trace: [stack_frames]usize,
allocated_bytes: usize = 0,
io: std.Io, fn capture(len: usize) Allocation {
return .{
.len = len,
.stack_trace = stack_trace_buf,
};
}
fn dumpStackTrace(self: *Allocation) void {
const st = self.getStackTrace();
std.debug.dumpStackTrace(&st);
}
fn getStackTrace(self: *Allocation) std.debug.StackTrace {
var stack_trace_len: usize = 0;
while (stack_trace_len < self.stack_trace.len and self.stack_trace[stack_trace_len] != 0) {
stack_trace_len += 1;
}
return .{
.return_addresses = self.stack_trace[0..stack_trace_len],
.skipped = if (stack_trace_len < self.stack_trace.len) .none else .unknown,
};
}
};
threadlocal var stack_trace_buf: [stack_frames]usize = undefined;
/// Uses `allocator_general`.
allocations: std.AutoHashMapUnmanaged(*anyopaque, Allocation),
mutex: std.Io.Mutex,
allocated_bytes: usize,
interface: vk.AllocationCallbacks, interface: vk.AllocationCallbacks,
@@ -16,48 +46,66 @@ interface: vk.AllocationCallbacks,
// around it, but it would add much complexity. Instead, we allocate everything // around it, but it would add much complexity. Instead, we allocate everything
// with the same, relatively big alignment and hope for the best. // with the same, relatively big alignment and hope for the best.
const actual_alignment: std.mem.Alignment = .@"16"; const actual_alignment: std.mem.Alignment = .@"16";
const debug = @import("builtin").mode == .Debug;
const stack_frames: usize = if (debug and std.debug.sys_can_stack_trace) 6 else 0;
const log = std.log.scoped(.vk_allocator); const log = std.log.scoped(.vk_allocator);
pub fn init(allocator: std.mem.Allocator, io: std.Io) !*VkAllocator { pub fn init() !*VkAllocator {
// NOTE We allocate the structure to pin its address. const allocator_persistent = ctx.allocator_persistent;
const self = try allocator.create(VkAllocator);
self.* = .{ // NOTE We allocate the structure to pin its address.
.allocator = allocator, const vk_allocator = try allocator_persistent.create(VkAllocator);
errdefer allocator_persistent.destroy(vk_allocator);
vk_allocator.* = .{
.allocations = .empty,
.mutex = .init,
.allocated_bytes = 0,
.interface = .{ .interface = .{
.p_user_data = self, .p_user_data = vk_allocator,
.pfn_allocation = &allocationFunction, .pfn_allocation = &allocationFunction,
.pfn_reallocation = &reallocationFunction, .pfn_reallocation = &reallocationFunction,
.pfn_free = &freeFunction, .pfn_free = &freeFunction,
}, },
.io = io,
}; };
return self; return vk_allocator;
} }
pub fn deinit(self: *VkAllocator) void { pub fn deinit(self: *VkAllocator) void {
const allocator_general = ctx.allocator_general;
std.log.scoped(.deinit).debug("Deinitializing {*}", .{self}); std.log.scoped(.deinit).debug("Deinitializing {*}", .{self});
if (self.allocated_bytes > 0) { if (self.allocated_bytes > 0) {
log.warn("{d} byte(s) still allocated while deinitializing", .{self.allocated_bytes}); log.warn("{d} byte(s) still allocated while deinitializing", .{self.allocated_bytes});
} }
if (self.allocations.size > 0) { if (self.allocations.size > 0) {
log.warn("{d} allocation(s) still tracked while deinitializing", .{self.allocations.size}); log.warn("{d} allocation(s) still tracked while deinitializing", .{self.allocations.size});
var it = self.allocations.iterator(); var it = self.allocations.iterator();
var index: usize = 0; var index: usize = 0;
while (it.next()) |entry| : (index += 1) { while (it.next()) |entry| : (index += 1) {
log.warn("Leaked allocation ({d}/{d}) at 0x{x} of {d} byte(s)", .{ index + 1, self.allocations.size, @intFromPtr(entry.key_ptr.*), entry.value_ptr.* }); const ptr = entry.key_ptr.*;
const memory = @as([*]align(actual_alignment.toByteUnits()) u8, @ptrCast(@alignCast(entry.key_ptr.*)))[0..entry.value_ptr.*]; const allocation = entry.value_ptr;
self.allocator.free(memory);
log.warn("Leaked allocation ({d}/{d}) at 0x{x} of {d} byte(s)", .{ index + 1, self.allocations.size, @intFromPtr(ptr), allocation.len });
allocation.dumpStackTrace();
const memory = @as([*]align(actual_alignment.toByteUnits()) u8, @ptrCast(@alignCast(ptr)))[0..allocation.len];
allocator_general.free(memory);
} }
} }
const allocator = self.allocator; self.allocations.deinit(allocator_general);
self.allocations.deinit(allocator);
self.* = undefined; self.* = undefined;
allocator.destroy(self); }
pub noinline fn capture(self: *const VkAllocator) *const vk.AllocationCallbacks {
const ret_addr = @returnAddress();
const stack_trace = std.debug.captureCurrentStackTrace(.{ .first_address = ret_addr }, &stack_trace_buf);
@memset(stack_trace_buf[@min(stack_trace.return_addresses.len, stack_trace_buf.len)..], 0);
return &self.interface;
} }
fn allocationFunction( fn allocationFunction(
@@ -66,18 +114,21 @@ fn allocationFunction(
alignment: usize, alignment: usize,
_: vk.SystemAllocationScope, _: vk.SystemAllocationScope,
) callconv(vk.vulkan_call_conv) ?*anyopaque { ) callconv(vk.vulkan_call_conv) ?*anyopaque {
const allocator_general = ctx.allocator_general;
const io = ctx.io;
const self: *VkAllocator = @ptrCast(@alignCast(p_user_data.?)); const self: *VkAllocator = @ptrCast(@alignCast(p_user_data.?));
const desired_alignment = std.mem.Alignment.fromByteUnits(alignment); const desired_alignment = std.mem.Alignment.fromByteUnits(alignment);
std.debug.assert(std.mem.Alignment.compare(actual_alignment, .gte, desired_alignment)); std.debug.assert(std.mem.Alignment.compare(actual_alignment, .gte, desired_alignment));
self.mutex.lock(self.io) catch return null; self.mutex.lockUncancelable(io);
defer self.mutex.unlock(self.io); defer self.mutex.unlock(io);
self.allocations.ensureUnusedCapacity(self.allocator, 1) catch return null; self.allocations.ensureUnusedCapacity(allocator_general, 1) catch return null;
const memory = self.allocator.alignedAlloc(u8, actual_alignment, size) catch return null; const memory = allocator_general.alignedAlloc(u8, actual_alignment, size) catch return null;
self.allocations.putAssumeCapacity(memory.ptr, size); self.allocations.putAssumeCapacity(memory.ptr, .capture(size));
self.allocated_bytes += size; self.allocated_bytes += size;
//log.debug("Allocated {d} bytes(s) at 0x{x}", .{ size, @intFromPtr(memory.ptr) }); //log.debug("Allocated {d} bytes(s) at 0x{x}", .{ size, @intFromPtr(memory.ptr) });
@@ -91,34 +142,37 @@ fn reallocationFunction(
alignment: usize, alignment: usize,
_: vk.SystemAllocationScope, _: vk.SystemAllocationScope,
) callconv(vk.vulkan_call_conv) ?*anyopaque { ) callconv(vk.vulkan_call_conv) ?*anyopaque {
const allocator_general = ctx.allocator_general;
const io = ctx.io;
const self: *VkAllocator = @ptrCast(@alignCast(p_user_data.?)); const self: *VkAllocator = @ptrCast(@alignCast(p_user_data.?));
const desired_alignment = std.mem.Alignment.fromByteUnits(alignment); const desired_alignment = std.mem.Alignment.fromByteUnits(alignment);
std.debug.assert(std.mem.Alignment.compare(actual_alignment, .gte, desired_alignment)); std.debug.assert(std.mem.Alignment.compare(actual_alignment, .gte, desired_alignment));
self.mutex.lock(self.io) catch return null; self.mutex.lockUncancelable(io);
defer self.mutex.unlock(self.io); defer self.mutex.unlock(io);
// NOTE If we were pedantic, we would consider the fact that we might not // NOTE If we were pedantic, we would consider the fact that we might not
// need unused capacity if the memory doesn't get relocated. // need unused capacity if the memory doesn't get relocated.
self.allocations.ensureUnusedCapacity(self.allocator, 1) catch return null; self.allocations.ensureUnusedCapacity(allocator_general, 1) catch return null;
const old_memory = if (maybe_p_original) |p_original| blk_then: { const old_memory = if (maybe_p_original) |p_original| blk_then: {
const old_size = self.allocations.get(p_original).?; const old_size = self.allocations.get(p_original).?.len;
break :blk_then @as([*]align(actual_alignment.toByteUnits()) u8, @ptrCast(@alignCast(p_original)))[0..old_size]; break :blk_then @as([*]align(actual_alignment.toByteUnits()) u8, @ptrCast(@alignCast(p_original)))[0..old_size];
} else blk_else: { } else blk_else: {
break :blk_else @as([]align(actual_alignment.toByteUnits()) u8, &.{}); break :blk_else @as([]align(actual_alignment.toByteUnits()) u8, &.{});
}; };
const memory = self.allocator.realloc(old_memory, size) catch return null; const memory = allocator_general.realloc(old_memory, size) catch return null;
std.debug.assert(std.mem.isAligned(@intFromPtr(memory.ptr), alignment)); std.debug.assert(std.mem.isAligned(@intFromPtr(memory.ptr), alignment));
if (maybe_p_original) |p_original| { if (maybe_p_original) |p_original| {
const old_size = self.allocations.fetchRemove(p_original).?.value; const old_size = self.allocations.fetchRemove(p_original).?.value.len;
self.allocated_bytes -= old_size; self.allocated_bytes -= old_size;
} }
self.allocations.putAssumeCapacityNoClobber(memory.ptr, size); self.allocations.putAssumeCapacityNoClobber(memory.ptr, .capture(size));
self.allocated_bytes += size; self.allocated_bytes += size;
//log.debug("Reallocated into {d} bytes(s) at 0x{x} from 0x{x}", .{ size, @intFromPtr(memory.ptr), @intFromPtr(maybe_p_original) }); //log.debug("Reallocated into {d} bytes(s) at 0x{x} from 0x{x}", .{ size, @intFromPtr(memory.ptr), @intFromPtr(maybe_p_original) });
@@ -129,16 +183,19 @@ fn freeFunction(
p_user_data: ?*anyopaque, p_user_data: ?*anyopaque,
maybe_p_memory: ?*anyopaque, maybe_p_memory: ?*anyopaque,
) callconv(vk.vulkan_call_conv) void { ) callconv(vk.vulkan_call_conv) void {
const allocator_general = ctx.allocator_general;
const io = ctx.io;
const self: *VkAllocator = @ptrCast(@alignCast(p_user_data.?)); const self: *VkAllocator = @ptrCast(@alignCast(p_user_data.?));
if (maybe_p_memory) |p_memory| { if (maybe_p_memory) |p_memory| {
self.mutex.lockUncancelable(self.io); self.mutex.lockUncancelable(io);
defer self.mutex.unlock(self.io); defer self.mutex.unlock(io);
const size = self.allocations.fetchRemove(p_memory).?.value; const size = self.allocations.fetchRemove(p_memory).?.value.len;
self.allocated_bytes -= size; self.allocated_bytes -= size;
const memory = @as([*]align(actual_alignment.toByteUnits()) u8, @ptrCast(@alignCast(p_memory)))[0..size]; const memory = @as([*]align(actual_alignment.toByteUnits()) u8, @ptrCast(@alignCast(p_memory)))[0..size];
self.allocator.free(memory); allocator_general.free(memory);
} }
} }

View File

@@ -1,11 +1,11 @@
const std = @import("std"); const std = @import("std");
const c = @import("const.zig");
const ctx = @import("AppContext.zig");
const glfw = @import("zglfw"); const glfw = @import("zglfw");
const vk = @import("vulkan"); 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 Engine = @import("engine/Engine.zig");
const Swapchain = @import("engine/Swapchain.zig"); const Swapchain = @import("engine/Swapchain.zig");
const Game = @import("Game.zig"); const Game = @import("Game.zig");
@@ -25,11 +25,20 @@ pub const std_options: std.Options = .{
}; };
pub fn main(init: std.process.Init) !void { pub fn main(init: std.process.Init) !void {
const allocator = init.gpa; var arena_frame: std.heap.ArenaAllocator = .init(std.heap.page_allocator);
const io = init.io; defer arena_frame.deinit();
try atoms.init(allocator, io); ctx.allocator_general = init.gpa;
defer atoms.deinit(io); ctx.allocator_frame = arena_frame.allocator();
ctx.allocator_persistent = init.arena.allocator();
ctx.io = init.io;
ctx.vk_allocator = try .init();
defer {
ctx.vk_allocator.deinit();
ctx.vk_allocator = undefined;
}
// --- INITIALIZE APP CONTEXT ---
glfw.init() catch |err| { glfw.init() catch |err| {
std.log.err("Could not initialize GLFW", .{}); std.log.err("Could not initialize GLFW", .{});
@@ -43,46 +52,65 @@ pub fn main(init: std.process.Init) !void {
} }
glfw.windowHint(.client_api, .no_api); glfw.windowHint(.client_api, .no_api);
var window = glfw.Window.create(c.default_window_width, c.default_window_height, c.window_title, null, null) catch |err| { ctx.window = glfw.Window.create(c.default_window_width, c.default_window_height, c.window_title, null, null) catch |err| {
std.log.err("Could not create window", .{}); std.log.err("Could not create window", .{});
return err; return err;
}; };
defer window.destroy(); defer {
ctx.window.destroy();
window.setSizeLimits(c.min_window_width, c.min_window_height, -1, -1); ctx.window = undefined;
window.setInputMode(.cursor, .disabled) catch {};
if (glfw.rawMouseMotionSupported()) {
window.setInputMode(.raw_mouse_motion, true) catch {};
} }
_ = window.setKeyCallback(keyCallback); ctx.window.setSizeLimits(c.min_window_width, c.min_window_height, -1, -1);
_ = window.setCursorPosCallback(cursorPosCallback); ctx.window.setInputMode(.cursor, .disabled) catch {};
_ = window.setMouseButtonCallback(mouseButtonCallback); if (glfw.rawMouseMotionSupported()) {
ctx.window.setInputMode(.raw_mouse_motion, true) catch {};
}
var engine = try Engine.init(allocator, io, window); _ = ctx.window.setKeyCallback(keyCallback);
defer engine.deinit(); _ = ctx.window.setCursorPosCallback(cursorPosCallback);
_ = ctx.window.setMouseButtonCallback(mouseButtonCallback);
var swapchain = try Swapchain.init(&engine); ctx.atoms = try .init();
defer swapchain.deinit(&engine); defer {
ctx.atoms.deinit();
ctx.atoms = undefined;
}
var game = try Game.init(allocator, io, &engine, &swapchain); ctx.engine = try .init();
defer {
ctx.engine.deinit();
ctx.engine = undefined;
}
ctx.swapchain = try .init();
defer {
ctx.swapchain.deinit();
ctx.swapchain = undefined;
}
// --- INITIALIZE THE GAME ---
var game = try Game.init();
var callback_context: CallbackContext = blk: { var callback_context: CallbackContext = blk: {
const cursor_last_xpos, const cursor_last_ypos = window.getCursorPos(); const cursor_last_xpos, const cursor_last_ypos = ctx.window.getCursorPos();
break :blk .{ break :blk .{
.game = &game, .game = &game,
.cursor_last_xpos = cursor_last_xpos, .cursor_last_xpos = cursor_last_xpos,
.cursor_last_ypos = cursor_last_ypos, .cursor_last_ypos = cursor_last_ypos,
.focused = (window.getInputMode(.cursor) catch .normal) == .disabled, .focused = (ctx.window.getInputMode(.cursor) catch .normal) == .disabled,
}; };
}; };
window.setUserPointer(&callback_context); ctx.window.setUserPointer(&callback_context);
defer { defer {
window.setUserPointer(null); ctx.window.setUserPointer(null);
game.deinit(); game.deinit();
} }
_ = arena_frame.reset(.retain_capacity);
var t1 = glfw.getTime(); var t1 = glfw.getTime();
while (!window.shouldClose()) { while (!ctx.window.shouldClose()) {
glfw.pollEvents(); glfw.pollEvents();
const t2 = glfw.getTime(); const t2 = glfw.getTime();
@@ -90,12 +118,13 @@ pub fn main(init: std.process.Init) !void {
t1 = t2; t1 = t2;
game.update(dt); game.update(dt);
_ = arena_frame.reset(.retain_capacity);
} }
std.log.debug("Exitted the game loop", .{}); std.log.debug("Exitted the game loop", .{});
engine.waitForFence(swapchain.fence) catch {}; ctx.engine.waitForFence(ctx.swapchain.fence) catch {};
engine.deviceWaitIdle() catch {}; ctx.engine.deviceWaitIdle() catch {};
} }
const CallbackContext = struct { const CallbackContext = struct {
@@ -106,24 +135,24 @@ const CallbackContext = struct {
}; };
fn keyCallback(window: *glfw.Window, key_code: glfw.Key, _: c_int, action: glfw.Action, _: glfw.Mods) callconv(.c) void { fn keyCallback(window: *glfw.Window, key_code: glfw.Key, _: c_int, action: glfw.Action, _: glfw.Mods) callconv(.c) void {
const maybe_ctx = window.getUserPointer(CallbackContext); const maybe_callback_ctx = window.getUserPointer(CallbackContext);
if (key_code == .escape and action == .press and (window.getInputMode(.cursor) catch .normal) == .disabled) { if (key_code == .escape and action == .press and (window.getInputMode(.cursor) catch .normal) == .disabled) {
window.setInputMode(.cursor, .normal) catch {}; window.setInputMode(.cursor, .normal) catch {};
if (maybe_ctx) |ctx| { if (maybe_callback_ctx) |callback_ctx| {
const cursor_last_xpos, const cursor_last_ypos = window.getCursorPos(); const cursor_last_xpos, const cursor_last_ypos = window.getCursorPos();
ctx.cursor_last_xpos = cursor_last_xpos; callback_ctx.cursor_last_xpos = cursor_last_xpos;
ctx.cursor_last_ypos = cursor_last_ypos; callback_ctx.cursor_last_ypos = cursor_last_ypos;
ctx.focused = false; callback_ctx.focused = false;
} }
return; return;
} }
if (maybe_ctx) |ctx| { if (maybe_callback_ctx) |callback_ctx| {
if (ctx.focused) { if (callback_ctx.focused) {
switch (action) { switch (action) {
.press => ctx.game.onKeyDown(key_code), .press => callback_ctx.game.onKeyDown(key_code),
.release => ctx.game.onKeyUp(key_code), .release => callback_ctx.game.onKeyUp(key_code),
.repeat => {}, .repeat => {},
} }
} }
@@ -131,36 +160,36 @@ fn keyCallback(window: *glfw.Window, key_code: glfw.Key, _: c_int, action: glfw.
} }
fn cursorPosCallback(window: *glfw.Window, cursor_xpos: f64, cursor_ypos: f64) callconv(.c) void { fn cursorPosCallback(window: *glfw.Window, cursor_xpos: f64, cursor_ypos: f64) callconv(.c) void {
const maybe_ctx = window.getUserPointer(CallbackContext); const maybe_callback_ctx = window.getUserPointer(CallbackContext);
if (maybe_ctx) |ctx| { if (maybe_callback_ctx) |callback_ctx| {
if (ctx.focused) { if (callback_ctx.focused) {
const dx: f32 = @floatCast(cursor_xpos - ctx.cursor_last_xpos); const dx: f32 = @floatCast(cursor_xpos - callback_ctx.cursor_last_xpos);
const dy: f32 = @floatCast(cursor_ypos - ctx.cursor_last_ypos); const dy: f32 = @floatCast(cursor_ypos - callback_ctx.cursor_last_ypos);
ctx.game.onMouseMove(dx, dy); callback_ctx.game.onMouseMove(dx, dy);
ctx.cursor_last_xpos = cursor_xpos; callback_ctx.cursor_last_xpos = cursor_xpos;
ctx.cursor_last_ypos = cursor_ypos; callback_ctx.cursor_last_ypos = cursor_ypos;
} }
} }
} }
fn mouseButtonCallback(window: *glfw.Window, button: glfw.MouseButton, action: glfw.Action, _: glfw.Mods) callconv(.c) void { fn mouseButtonCallback(window: *glfw.Window, button: glfw.MouseButton, action: glfw.Action, _: glfw.Mods) callconv(.c) void {
const maybe_ctx = window.getUserPointer(CallbackContext); const maybe_callback_ctx = window.getUserPointer(CallbackContext);
if (button == .left and action == .press and (window.getInputMode(.cursor) catch .normal) == .normal) { if (button == .left and action == .press and (window.getInputMode(.cursor) catch .normal) == .normal) {
window.setInputMode(.cursor, .disabled) catch {}; window.setInputMode(.cursor, .disabled) catch {};
if (maybe_ctx) |ctx| { if (maybe_callback_ctx) |callback_ctx| {
const cursor_last_xpos, const cursor_last_ypos = window.getCursorPos(); const cursor_last_xpos, const cursor_last_ypos = window.getCursorPos();
ctx.cursor_last_xpos = cursor_last_xpos; callback_ctx.cursor_last_xpos = cursor_last_xpos;
ctx.cursor_last_ypos = cursor_last_ypos; callback_ctx.cursor_last_ypos = cursor_last_ypos;
ctx.focused = true; callback_ctx.focused = true;
} }
} }
if (maybe_ctx) |ctx| { if (maybe_callback_ctx) |callback_ctx| {
if (ctx.focused) { if (callback_ctx.focused) {
switch (action) { switch (action) {
.press => ctx.game.onMouseDown(button), .press => callback_ctx.game.onMouseDown(button),
.release => ctx.game.onMouseUp(button), .release => callback_ctx.game.onMouseUp(button),
.repeat => {}, .repeat => {},
} }
} }