Compare commits
4 Commits
36434f8107
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 40fcba9323 | |||
| c971443b02 | |||
| fd311b97e7 | |||
| ad80fb4fd9 |
34
src/AppContext.zig
Normal file
34
src/AppContext.zig
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
//! This module contains some common global variables, which mostly refer to
|
||||||
|
//! singleton modules that are active throughout entire program's runtime.
|
||||||
|
//!
|
||||||
|
//! These globals can be safely referenced during and after game initialization.
|
||||||
|
|
||||||
|
const AppContext = @This();
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const media = @import("media");
|
||||||
|
const glfw = @import("zglfw");
|
||||||
|
|
||||||
|
const Atoms = @import("engine/Atoms.zig");
|
||||||
|
const Engine = @import("engine/Engine.zig");
|
||||||
|
const Gui = @import("engine/Gui.zig");
|
||||||
|
const Materials = @import("engine/Materials.zig");
|
||||||
|
const Swapchain = @import("engine/Swapchain.zig");
|
||||||
|
const Textures = @import("engine/Textures.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 stbi: *media.stbi = undefined;
|
||||||
|
|
||||||
|
pub var window: *glfw.Window = undefined;
|
||||||
|
pub var atoms: *Atoms = undefined;
|
||||||
|
pub var engine: *Engine = undefined;
|
||||||
|
pub var swapchain: *Swapchain = undefined;
|
||||||
|
|
||||||
|
pub var textures: *Textures = undefined;
|
||||||
|
pub var materials: *Materials = undefined;
|
||||||
|
pub var gui: *Gui = undefined;
|
||||||
@@ -1,18 +1,19 @@
|
|||||||
const Chunk = @This();
|
const Chunk = @This();
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const math = @import("../math.zig");
|
const ctx = @import("AppContext.zig");
|
||||||
const shaders = @import("../shaders.zig");
|
const math = @import("math.zig");
|
||||||
|
const shaders = @import("shaders.zig");
|
||||||
const vk = @import("vulkan");
|
const vk = @import("vulkan");
|
||||||
const vm = @import("vecmath");
|
const vm = @import("vecmath");
|
||||||
const voxels = @import("../voxels.zig");
|
const voxels = @import("voxels.zig");
|
||||||
|
|
||||||
const Blocks = @import("Blocks.zig");
|
const Blocks = @import("assets/Blocks.zig");
|
||||||
const CommandBuffer = @import("../engine/CommandBuffer.zig");
|
const CommandBuffer = @import("engine/CommandBuffer.zig");
|
||||||
const Engine = @import("../engine/Engine.zig");
|
const Engine = @import("engine/Engine.zig");
|
||||||
const Game = @import("../Game.zig");
|
const Game = @import("Game.zig");
|
||||||
const GenericBuffer = @import("../engine/GenericBuffer.zig").GenericBuffer;
|
const GenericBuffer = @import("engine/GenericBuffer.zig").GenericBuffer;
|
||||||
const Materials = @import("Materials.zig");
|
const Materials = @import("engine/Materials.zig");
|
||||||
|
|
||||||
const ObjectUniformsBuffer = GenericBuffer(void, shaders.ObjectUniforms);
|
const ObjectUniformsBuffer = GenericBuffer(void, shaders.ObjectUniforms);
|
||||||
|
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2,13 +2,14 @@ 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");
|
||||||
const voxels = @import("voxels.zig");
|
const voxels = @import("voxels.zig");
|
||||||
|
|
||||||
const Blocks = @import("assets/Blocks.zig");
|
const Blocks = @import("assets/Blocks.zig");
|
||||||
const Chunk = @import("assets/Chunk.zig");
|
const Chunk = @import("Chunk.zig");
|
||||||
const Engine = @import("engine/Engine.zig");
|
const Engine = @import("engine/Engine.zig");
|
||||||
const Iterator2 = math.Iterator2;
|
const Iterator2 = math.Iterator2;
|
||||||
|
|
||||||
@@ -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,
|
||||||
|
|||||||
282
src/Game.zig
282
src/Game.zig
@@ -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");
|
||||||
@@ -11,24 +12,19 @@ const vm = @import("vecmath");
|
|||||||
const worldgen = @import("worldgen.zig");
|
const worldgen = @import("worldgen.zig");
|
||||||
|
|
||||||
const Blocks = @import("assets/Blocks.zig");
|
const Blocks = @import("assets/Blocks.zig");
|
||||||
const Chunk = @import("assets/Chunk.zig");
|
const Chunk = @import("Chunk.zig");
|
||||||
const Chunks = @import("Chunks.zig");
|
const Chunks = @import("Chunks.zig");
|
||||||
const CommandBuffer = @import("engine/CommandBuffer.zig");
|
const CommandBuffer = @import("engine/CommandBuffer.zig");
|
||||||
const Engine = @import("engine/Engine.zig");
|
const Engine = @import("engine/Engine.zig");
|
||||||
const Gui = @import("Gui.zig");
|
|
||||||
const Iterator2 = math.Iterator2;
|
const Iterator2 = math.Iterator2;
|
||||||
const Materials = @import("assets/Materials.zig");
|
const Materials = @import("engine/Materials.zig");
|
||||||
const Player = @import("Player.zig");
|
const Player = @import("Player.zig");
|
||||||
const Skybox = @import("engine/Skybox.zig");
|
const Skybox = @import("engine/Skybox.zig");
|
||||||
const StagingBuffer = @import("engine/StagingBuffer.zig");
|
const StagingBuffer = @import("engine/StagingBuffer.zig");
|
||||||
const Swapchain = @import("engine/Swapchain.zig");
|
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("engine/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,
|
||||||
@@ -47,14 +43,9 @@ directional_lights: shaders.DirectionalLightBuffer,
|
|||||||
sampler: vk.Sampler,
|
sampler: vk.Sampler,
|
||||||
deferred_command_buffers: std.ArrayList(CommandBuffer),
|
deferred_command_buffers: std.ArrayList(CommandBuffer),
|
||||||
|
|
||||||
stbi: media.stbi,
|
|
||||||
blocks: Blocks,
|
blocks: Blocks,
|
||||||
materials: Materials,
|
|
||||||
textures: Textures,
|
|
||||||
chunks: Chunks,
|
chunks: Chunks,
|
||||||
skybox: Skybox,
|
skybox: Skybox,
|
||||||
gui: Gui,
|
|
||||||
|
|
||||||
player: Player,
|
player: Player,
|
||||||
|
|
||||||
const max_textures = 1024;
|
const max_textures = 1024;
|
||||||
@@ -64,24 +55,22 @@ 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;
|
||||||
errdefer stbi.deinit();
|
const allocator_frame = ctx.allocator_frame;
|
||||||
|
const engine = ctx.engine;
|
||||||
|
const swapchain = ctx.swapchain;
|
||||||
|
const textures = ctx.textures;
|
||||||
|
const materials = ctx.materials;
|
||||||
|
const gui = ctx.gui;
|
||||||
|
|
||||||
var materials = try Materials.init(engine, allocator);
|
var blocks = try Blocks.init();
|
||||||
errdefer materials.deinit(engine, allocator);
|
errdefer blocks.deinit();
|
||||||
|
|
||||||
var textures = try Textures.init(engine, allocator);
|
|
||||||
errdefer textures.deinit(engine, allocator);
|
|
||||||
|
|
||||||
var blocks = try Blocks.init(allocator);
|
|
||||||
errdefer blocks.deinit(allocator);
|
|
||||||
|
|
||||||
// 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();
|
||||||
blocks.loadAll(engine, &materials, &textures, &stbi, allocator, io);
|
|
||||||
|
|
||||||
const sampler = try engine.createSampler(.{
|
const sampler = try engine.createSampler(.{
|
||||||
.mag_filter = .linear,
|
.mag_filter = .linear,
|
||||||
@@ -177,14 +166,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 +202,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 +238,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 +433,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("Grass.json");
|
||||||
const block_dirt = try blocks.getOrLoad(engine, &materials, &textures, &stbi, "Dirt.json", allocator, io);
|
const block_dirt = try blocks.getOrLoad("Dirt.json");
|
||||||
const block_stone = try blocks.getOrLoad(engine, &materials, &textures, &stbi, "Stone.json", allocator, io);
|
const block_stone = try blocks.getOrLoad("Stone.json");
|
||||||
const block_bedrock = try blocks.getOrLoad(engine, &materials, &textures, &stbi, "Bedrock.json", allocator, io);
|
const block_bedrock = try blocks.getOrLoad("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 +448,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 +525,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 +545,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 +599,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 +621,40 @@ 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", 512, global_uniforms.buffer);
|
||||||
"skybox.hdr",
|
errdefer skybox.deinit();
|
||||||
engine,
|
|
||||||
swapchain,
|
|
||||||
&stbi,
|
|
||||||
512,
|
|
||||||
global_uniforms.buffer,
|
|
||||||
allocator,
|
|
||||||
io,
|
|
||||||
);
|
|
||||||
errdefer skybox.deinit(engine);
|
|
||||||
|
|
||||||
var gui = try Gui.init(
|
// TODO This coupling is not elegant - the game must provide the GUI with
|
||||||
allocator,
|
// uniform buffer for globals. Maybe GUI should just use their own global
|
||||||
engine,
|
// uniform buffer not for any other reason than decoupling. After all, GUI
|
||||||
swapchain,
|
// only needs a single 3×2 matrix and does not need to depend on game's
|
||||||
global_uniforms.buffer,
|
// rendering pipeline.
|
||||||
);
|
try engine.updateDescriptorSets(.{
|
||||||
errdefer gui.deinit(engine, allocator);
|
.writes = &.{
|
||||||
|
.{
|
||||||
|
.dst_set = gui.box_descriptor_set,
|
||||||
|
.dst_binding = 0,
|
||||||
|
.dst_array_element = 0,
|
||||||
|
.descriptor_type = .uniform_buffer,
|
||||||
|
.descriptor_infos = .{
|
||||||
|
.buffer = &.{
|
||||||
|
.{
|
||||||
|
.buffer = global_uniforms.buffer,
|
||||||
|
.offset = 0,
|
||||||
|
.range = vk.WHOLE_SIZE,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
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,64 +671,60 @@ 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,
|
|
||||||
.blocks = blocks,
|
.blocks = blocks,
|
||||||
.materials = materials,
|
|
||||||
.textures = textures,
|
|
||||||
.chunks = .{ .chunks = chunks },
|
.chunks = .{ .chunks = chunks },
|
||||||
.skybox = skybox,
|
.skybox = skybox,
|
||||||
.gui = gui,
|
|
||||||
|
|
||||||
.player = .init(player_position_sv, 0, 0),
|
.player = .init(player_position_sv, 0, 0),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
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.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.blocks.deinit();
|
||||||
self.materials.deinit(self.engine, self.allocator);
|
|
||||||
self.blocks.deinit(self.allocator);
|
|
||||||
self.stbi.deinit();
|
|
||||||
self.* = undefined;
|
self.* = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(self: *Game, dt: f32) void {
|
pub fn update(self: *Game, dt: f32) void {
|
||||||
self.gui.beginFrame();
|
const swapchain = ctx.swapchain;
|
||||||
|
const gui = ctx.gui;
|
||||||
|
|
||||||
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),
|
||||||
@@ -745,7 +733,7 @@ pub fn update(self: *Game, dt: f32) void {
|
|||||||
const crosshair_half_extent_px = 8;
|
const crosshair_half_extent_px = 8;
|
||||||
const crosshair_half_width_px = 1;
|
const crosshair_half_width_px = 1;
|
||||||
|
|
||||||
self.gui.pushBox(.{
|
gui.pushBox(.{
|
||||||
.background_color = .init(1, 1, 1, 0.5),
|
.background_color = .init(1, 1, 1, 0.5),
|
||||||
.border_color = .init(0, 0, 0, 1),
|
.border_color = .init(0, 0, 0, 1),
|
||||||
.position_sspx = framebuffer_size.mulScalar(0.5).add(.init(-crosshair_half_extent_px, -crosshair_half_width_px)),
|
.position_sspx = framebuffer_size.mulScalar(0.5).add(.init(-crosshair_half_extent_px, -crosshair_half_width_px)),
|
||||||
@@ -754,7 +742,7 @@ pub fn update(self: *Game, dt: f32) void {
|
|||||||
.border_radius_px = 0,
|
.border_radius_px = 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
self.gui.pushBox(.{
|
gui.pushBox(.{
|
||||||
.background_color = .init(1, 1, 1, 0.5),
|
.background_color = .init(1, 1, 1, 0.5),
|
||||||
.border_color = .init(0, 0, 0, 1),
|
.border_color = .init(0, 0, 0, 1),
|
||||||
.position_sspx = framebuffer_size.mulScalar(0.5).add(.init(-crosshair_half_width_px, -crosshair_half_extent_px)),
|
.position_sspx = framebuffer_size.mulScalar(0.5).add(.init(-crosshair_half_width_px, -crosshair_half_extent_px)),
|
||||||
@@ -791,8 +779,13 @@ 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 gui = ctx.gui;
|
||||||
|
|
||||||
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 +793,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 +860,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 +882,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.getCurrentSwapchainImage();
|
||||||
|
|
||||||
command_buffer.pipelineBarrier(.{
|
command_buffer.pipelineBarrier(.{
|
||||||
.src_stage_mask = .{ .color_attachment_output_bit = true },
|
.src_stage_mask = .{ .color_attachment_output_bit = true },
|
||||||
@@ -942,7 +935,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 +968,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,
|
||||||
@@ -988,31 +981,25 @@ fn render(self: *Game) !void {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
command_buffer.setViewport(0, &.{
|
command_buffer.setViewport(0, .{
|
||||||
.{
|
|
||||||
.x = 0,
|
.x = 0,
|
||||||
.y = 0,
|
.y = 0,
|
||||||
.width = @floatFromInt(extent.width),
|
.width = @floatFromInt(extent.width),
|
||||||
.height = @floatFromInt(extent.height),
|
.height = @floatFromInt(extent.height),
|
||||||
.min_depth = 0,
|
.min_depth = 0,
|
||||||
.max_depth = 1,
|
.max_depth = 1,
|
||||||
},
|
|
||||||
});
|
});
|
||||||
command_buffer.setScissor(0, &.{
|
command_buffer.setScissor(0, .{
|
||||||
.{
|
|
||||||
.offset = .{ .x = 0, .y = 0 },
|
.offset = .{ .x = 0, .y = 0 },
|
||||||
.extent = extent,
|
.extent = extent,
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// --- RENDER 3D SCENE ---
|
// --- RENDER 3D SCENE ---
|
||||||
|
|
||||||
command_buffer.bindPipeline(.graphics, self.pipeline);
|
command_buffer.bindPipeline(.graphics, self.pipeline);
|
||||||
try command_buffer.bindVertexBuffers(0, &.{
|
try command_buffer.bindVertexBuffer(0, .{
|
||||||
.{
|
|
||||||
.buffer = self.vertex_buffer.buffer,
|
.buffer = self.vertex_buffer.buffer,
|
||||||
.offset = 0,
|
.offset = 0,
|
||||||
},
|
|
||||||
});
|
});
|
||||||
command_buffer.bindIndexBuffer(self.index_buffer.buffer, 0, .uint16);
|
command_buffer.bindIndexBuffer(self.index_buffer.buffer, 0, .uint16);
|
||||||
command_buffer.bindDescriptorSet(.graphics, self.pipeline_layout, 0, self.global_descriptor_set, null);
|
command_buffer.bindDescriptorSet(.graphics, self.pipeline_layout, 0, self.global_descriptor_set, null);
|
||||||
@@ -1026,7 +1013,7 @@ fn render(self: *Game) !void {
|
|||||||
|
|
||||||
// --- RENDER GUI ---
|
// --- RENDER GUI ---
|
||||||
|
|
||||||
try self.gui.draw(self.engine, command_buffer);
|
try gui.draw(command_buffer);
|
||||||
|
|
||||||
// --- FINALIZE ---
|
// --- FINALIZE ---
|
||||||
|
|
||||||
@@ -1056,11 +1043,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 +1058,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});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -253,37 +253,25 @@ 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 });
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
.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(blocks[self.block_index]) catch |err| {
|
||||||
game.engine,
|
|
||||||
&game.materials,
|
|
||||||
&game.textures,
|
|
||||||
&game.stbi,
|
|
||||||
blocks[self.block_index],
|
|
||||||
game.allocator,
|
|
||||||
game.io,
|
|
||||||
) 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;
|
||||||
};
|
};
|
||||||
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 });
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,13 +3,14 @@
|
|||||||
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");
|
||||||
|
|
||||||
const Atom = @import("../engine/Atom.zig").Atom;
|
const Atom = @import("../engine/Atom.zig").Atom;
|
||||||
const Engine = @import("../engine/Engine.zig");
|
const Engine = @import("../engine/Engine.zig");
|
||||||
const Materials = @import("Materials.zig");
|
const Materials = @import("../engine/Materials.zig");
|
||||||
const Textures = @import("Textures.zig");
|
const Textures = @import("../engine/Textures.zig");
|
||||||
|
|
||||||
pub const Id = enum(u16) {
|
pub const Id = enum(u16) {
|
||||||
// VOLATILE Synchronize explicit values with `init` implementation.
|
// VOLATILE Synchronize explicit values with `init` implementation.
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,20 +136,11 @@ pub fn getAtom(self: *const Blocks, filename: Atom) ?Id {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getOrLoad(
|
pub fn getOrLoad(self: *Blocks, filename: []const u8) !Id {
|
||||||
self: *Blocks,
|
|
||||||
engine: *Engine,
|
|
||||||
materials: *Materials,
|
|
||||||
textures: *Textures,
|
|
||||||
stbi: *media.stbi,
|
|
||||||
filename: []const u8,
|
|
||||||
temp_allocator: std.mem.Allocator,
|
|
||||||
io: std.Io,
|
|
||||||
) !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 +152,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(filename);
|
||||||
|
|
||||||
self.map.putAssumeCapacityNoClobber(key, id);
|
self.map.putAssumeCapacityNoClobber(key, id);
|
||||||
self.array.appendAssumeCapacity(def);
|
self.array.appendAssumeCapacity(def);
|
||||||
@@ -164,16 +161,7 @@ pub fn getOrLoad(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getOrLoadAtom(
|
pub fn getOrLoadAtom(self: *Blocks, filename: Atom) !Id {
|
||||||
self: *Blocks,
|
|
||||||
engine: *Engine,
|
|
||||||
materials: *Materials,
|
|
||||||
textures: *Textures,
|
|
||||||
stbi: *media.stbi,
|
|
||||||
filename: Atom,
|
|
||||||
temp_allocator: std.mem.Allocator,
|
|
||||||
io: std.Io,
|
|
||||||
) !Id {
|
|
||||||
const key: Key = .{
|
const key: Key = .{
|
||||||
.filename = filename,
|
.filename = filename,
|
||||||
};
|
};
|
||||||
@@ -187,7 +175,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(filename.toString());
|
||||||
|
|
||||||
self.map.putAssumeCapacityNoClobber(key, id);
|
self.map.putAssumeCapacityNoClobber(key, id);
|
||||||
self.array.appendAssumeCapacity(def);
|
self.array.appendAssumeCapacity(def);
|
||||||
@@ -196,15 +184,9 @@ pub fn getOrLoadAtom(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn loadAll(
|
pub fn loadAll(self: *Blocks) void {
|
||||||
self: *Blocks,
|
const io = ctx.io;
|
||||||
engine: *Engine,
|
|
||||||
materials: *Materials,
|
|
||||||
textures: *Textures,
|
|
||||||
stbi: *media.stbi,
|
|
||||||
temp_allocator: std.mem.Allocator,
|
|
||||||
io: std.Io,
|
|
||||||
) void {
|
|
||||||
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 +205,17 @@ pub fn loadAll(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = self.getOrLoad(engine, materials, textures, stbi, entry.name, temp_allocator, io) catch |err| {
|
_ = self.getOrLoad(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(filename: []const u8) !Definition {
|
||||||
engine: *Engine,
|
const allocator_frame = ctx.allocator_frame;
|
||||||
materials: *Materials,
|
const io = ctx.io;
|
||||||
textures: *Textures,
|
const materials = ctx.materials;
|
||||||
stbi: *media.stbi,
|
|
||||||
filename: []const u8,
|
|
||||||
temp_allocator: std.mem.Allocator,
|
|
||||||
io: std.Io,
|
|
||||||
) !Definition {
|
|
||||||
const DefinitionJson = struct {
|
const DefinitionJson = struct {
|
||||||
pub const Wall = struct {
|
pub const Wall = struct {
|
||||||
material: ?[]const u8 = null,
|
material: ?[]const u8 = null,
|
||||||
@@ -264,17 +242,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 +257,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(name);
|
||||||
break :blk .initUniform(material);
|
break :blk .initUniform(material);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,7 +267,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(@field(walls, field.name).material),
|
||||||
.transform = @field(walls, field.name).transform,
|
.transform = @field(walls, field.name).transform,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,14 @@
|
|||||||
//! 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");
|
||||||
|
|
||||||
|
/// Interned string ID. 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.
|
||||||
pub const Atom = enum(u16) {
|
pub const Atom = enum(u16) {
|
||||||
// VOLATILE Synchronize explicit values with `init` implementation.
|
// VOLATILE When modifying the list of explicitly defined atoms (i.e. any
|
||||||
|
// explicit enum value), we need to update `Atoms.init` implementation.
|
||||||
|
|
||||||
/// Atom representing an empty string, i.e. `""`.
|
/// Atom representing an empty string, i.e. `""`.
|
||||||
empty,
|
empty,
|
||||||
@@ -36,29 +34,29 @@ 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_persistent = ctx.allocator_persistent;
|
||||||
defer mutex.unlock(io);
|
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);
|
// We don't use `getOrPutAssumeCapacity` method, because we might
|
||||||
|
// already be at full capacity, in which case we should return
|
||||||
|
// `error.OutOfAtoms`.
|
||||||
|
|
||||||
if (entry.found_existing) {
|
if (atoms.map.get(string)) |atom| {
|
||||||
return entry.value_ptr.*;
|
return atom;
|
||||||
} else {
|
} else {
|
||||||
errdefer _ = map.remove(string);
|
const atom = Atom.fromIndexSafe(atoms.array.items.len) catch |err| switch (err) {
|
||||||
const atom = Atom.fromIndexSafe(array.items.len) catch |err| switch (err) {
|
|
||||||
error.Overflow => return error.OutOfAtoms,
|
error.Overflow => return error.OutOfAtoms,
|
||||||
};
|
};
|
||||||
|
const owned_string = try allocator_persistent.dupeZ(u8, string);
|
||||||
|
|
||||||
try array.ensureUnusedCapacity(allocator, 1);
|
atoms.map.putAssumeCapacityNoClobber(owned_string, atom);
|
||||||
const owned_string = try toOwnedString(string);
|
atoms.array.appendAssumeCapacity(owned_string);
|
||||||
|
|
||||||
entry.key_ptr.* = owned_string;
|
|
||||||
entry.value_ptr.* = atom;
|
|
||||||
|
|
||||||
array.appendAssumeCapacity(owned_string);
|
|
||||||
return atom;
|
return atom;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,13 +64,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 +80,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;
|
|
||||||
}
|
|
||||||
|
|||||||
75
src/engine/Atoms.zig
Normal file
75
src/engine/Atoms.zig
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
//! 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.
|
||||||
|
//!
|
||||||
|
//! The users should instead import `@import("Atom.zig").Atom` and use the
|
||||||
|
//! methods available in the `Atom` type instead of accessing this module
|
||||||
|
//! directly.
|
||||||
|
//!
|
||||||
|
//! This module is intended to be initialized once and to persist until the end
|
||||||
|
//! of the whole program's runtime. 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. Preallocated with
|
||||||
|
/// `allocator_persistent`.
|
||||||
|
map: std.StringHashMapUnmanaged(Atom),
|
||||||
|
/// Maps an atom value to a string. Preallocated with `allocator_persistent`.
|
||||||
|
array: std.ArrayList([:0]const u8),
|
||||||
|
/// Protects all reads and writes to `map` and `array`.
|
||||||
|
mutex: std.Io.Mutex,
|
||||||
|
|
||||||
|
pub const max_atoms = 1 << @typeInfo(std.meta.Tag(Atom)).int.bits;
|
||||||
|
|
||||||
|
pub fn init() !*Atoms {
|
||||||
|
const allocator_persistent = ctx.allocator_persistent;
|
||||||
|
|
||||||
|
const atoms = try allocator_persistent.create(Atoms);
|
||||||
|
|
||||||
|
atoms.* = .{
|
||||||
|
.map = .empty,
|
||||||
|
.array = .empty,
|
||||||
|
.mutex = .init,
|
||||||
|
};
|
||||||
|
|
||||||
|
try atoms.map.ensureTotalCapacity(allocator_persistent, max_atoms);
|
||||||
|
try atoms.array.ensureTotalCapacityPrecise(allocator_persistent, max_atoms);
|
||||||
|
|
||||||
|
// VOLATILE The initial contents of `atoms.map` and `atoms.array` must
|
||||||
|
// correspond to explicitly defined values at the top of the `Atom` type.
|
||||||
|
|
||||||
|
atoms.map.putAssumeCapacityNoClobber("", .empty);
|
||||||
|
atoms.array.appendAssumeCapacity("");
|
||||||
|
|
||||||
|
return atoms;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *Atoms) void {
|
||||||
|
std.log.scoped(.deinit).debug("Deinitializing {*}", .{self});
|
||||||
|
|
||||||
|
// No waiting; if atoms are in use while deinitializing, something is wrong.
|
||||||
|
std.debug.assert(self.mutex.tryLock());
|
||||||
|
|
||||||
|
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 });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,28 +9,28 @@
|
|||||||
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 handle = try engine.allocateCommandBuffer(.{
|
const engine = ctx.engine;
|
||||||
.queue_type = queue_type,
|
|
||||||
.level = .primary,
|
const handle = try engine.allocateCommandBuffer(.{ .queue_type = queue_type });
|
||||||
});
|
|
||||||
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,12 +38,48 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- VULKAN WRAPPERS ---------------------------------------------------------
|
// --- VULKAN WRAPPERS ---------------------------------------------------------
|
||||||
|
|
||||||
|
fn ToVkType(comptime T: type) type {
|
||||||
|
if (!std.meta.hasFn(T, "toVk")) {
|
||||||
|
@compileError("Type " ++ T ++ " has no toVk method.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const to_vk_type_info = @typeInfo(@TypeOf(@field(T, "toVk")));
|
||||||
|
return to_vk_type_info.@"fn".return_type.?;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn toVkOptional(comptime T: type, maybe_value: ?T) !?*const ToVkType(T) {
|
||||||
|
const allocator_frame = ctx.allocator_frame;
|
||||||
|
|
||||||
|
if (maybe_value) |value| {
|
||||||
|
const ptr = try allocator_frame.create(ToVkType(T));
|
||||||
|
if (std.meta.hasFn(T, "toVk")) {
|
||||||
|
ptr.* = value.toVk();
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn toVkMulti(comptime T: type, values: []const T) ![*]const ToVkType(T) {
|
||||||
|
const allocator_frame = ctx.allocator_frame;
|
||||||
|
|
||||||
|
const slice = try allocator_frame.alloc(ToVkType(T), values.len);
|
||||||
|
for (slice, values) |*y, x| {
|
||||||
|
y.* = x.toVk();
|
||||||
|
}
|
||||||
|
|
||||||
|
return slice.ptr;
|
||||||
|
}
|
||||||
|
|
||||||
pub const DrawIndexed = struct {
|
pub const DrawIndexed = struct {
|
||||||
index_count: u32,
|
index_count: u32,
|
||||||
instance_count: u32 = 1,
|
instance_count: u32 = 1,
|
||||||
@@ -70,6 +106,19 @@ pub const RenderingAttachmentInfo = struct {
|
|||||||
load_op: vk.AttachmentLoadOp,
|
load_op: vk.AttachmentLoadOp,
|
||||||
store_op: vk.AttachmentStoreOp,
|
store_op: vk.AttachmentStoreOp,
|
||||||
clear_value: vk.ClearValue,
|
clear_value: vk.ClearValue,
|
||||||
|
|
||||||
|
pub fn toVk(self: RenderingAttachmentInfo) vk.RenderingAttachmentInfo {
|
||||||
|
return .{
|
||||||
|
.image_view = self.image_view,
|
||||||
|
.image_layout = self.image_layout,
|
||||||
|
.resolve_mode = self.resolve_mode,
|
||||||
|
.resolve_image_view = self.resolve_image_view,
|
||||||
|
.resolve_image_layout = self.resolve_image_layout,
|
||||||
|
.load_op = self.load_op,
|
||||||
|
.store_op = self.store_op,
|
||||||
|
.clear_value = self.clear_value,
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const RenderingInfo = struct {
|
pub const RenderingInfo = struct {
|
||||||
@@ -80,11 +129,40 @@ pub const RenderingInfo = struct {
|
|||||||
color_attachments: []const RenderingAttachmentInfo = &.{},
|
color_attachments: []const RenderingAttachmentInfo = &.{},
|
||||||
depth_attachment: ?RenderingAttachmentInfo = null,
|
depth_attachment: ?RenderingAttachmentInfo = null,
|
||||||
stencil_attachment: ?RenderingAttachmentInfo = null,
|
stencil_attachment: ?RenderingAttachmentInfo = null,
|
||||||
|
|
||||||
|
pub fn toVk(self: RenderingInfo) !vk.RenderingInfo {
|
||||||
|
return .{
|
||||||
|
.flags = self.flags,
|
||||||
|
.render_area = self.render_area,
|
||||||
|
.layer_count = self.layer_count,
|
||||||
|
.view_mask = self.view_mask,
|
||||||
|
.color_attachment_count = @intCast(self.color_attachments.len),
|
||||||
|
.p_color_attachments = try toVkMulti(RenderingAttachmentInfo, self.color_attachments),
|
||||||
|
.p_depth_attachment = try toVkOptional(RenderingAttachmentInfo, self.depth_attachment),
|
||||||
|
.p_stencil_attachment = try toVkOptional(RenderingAttachmentInfo, self.stencil_attachment),
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const VertexBufferBinding = struct {
|
pub const VertexBufferBinding = struct {
|
||||||
buffer: vk.Buffer,
|
buffer: vk.Buffer,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
|
|
||||||
|
pub fn transpose(vertex_buffer_bindings: []const VertexBufferBinding) !struct { []const vk.Buffer, []const usize } {
|
||||||
|
const allocator_frame = ctx.allocator_frame;
|
||||||
|
|
||||||
|
var t: std.MultiArrayList(VertexBufferBinding) = .empty;
|
||||||
|
try t.ensureTotalCapacity(allocator_frame, vertex_buffer_bindings.len);
|
||||||
|
|
||||||
|
for (vertex_buffer_bindings) |binding| {
|
||||||
|
t.appendAssumeCapacity(binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{
|
||||||
|
t.items(.buffer),
|
||||||
|
t.items(.offset),
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn beginCommandBuffer(self: CommandBuffer) !void {
|
pub fn beginCommandBuffer(self: CommandBuffer) !void {
|
||||||
@@ -96,52 +174,8 @@ 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 rendering_info_vk = try rendering_info.toVk();
|
||||||
defer arena.deinit();
|
self.proxy.beginRendering(&rendering_info_vk);
|
||||||
const allocator = arena.allocator();
|
|
||||||
|
|
||||||
const color_attachments = try allocator.alloc(vk.RenderingAttachmentInfo, rendering_info.color_attachments.len);
|
|
||||||
for (color_attachments, rendering_info.color_attachments) |*x, color_attachment| {
|
|
||||||
x.* = .{
|
|
||||||
.image_view = color_attachment.image_view,
|
|
||||||
.image_layout = color_attachment.image_layout,
|
|
||||||
.resolve_mode = color_attachment.resolve_mode,
|
|
||||||
.resolve_image_view = color_attachment.resolve_image_view,
|
|
||||||
.resolve_image_layout = color_attachment.resolve_image_layout,
|
|
||||||
.load_op = color_attachment.load_op,
|
|
||||||
.store_op = color_attachment.store_op,
|
|
||||||
.clear_value = color_attachment.clear_value,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
self.proxy.beginRendering(&.{
|
|
||||||
.flags = rendering_info.flags,
|
|
||||||
.render_area = rendering_info.render_area,
|
|
||||||
.layer_count = rendering_info.layer_count,
|
|
||||||
.view_mask = rendering_info.view_mask,
|
|
||||||
.color_attachment_count = @intCast(color_attachments.len),
|
|
||||||
.p_color_attachments = color_attachments.ptr,
|
|
||||||
.p_depth_attachment = if (rendering_info.depth_attachment) |x| &.{
|
|
||||||
.image_view = x.image_view,
|
|
||||||
.image_layout = x.image_layout,
|
|
||||||
.resolve_mode = x.resolve_mode,
|
|
||||||
.resolve_image_view = x.resolve_image_view,
|
|
||||||
.resolve_image_layout = x.resolve_image_layout,
|
|
||||||
.load_op = x.load_op,
|
|
||||||
.store_op = x.store_op,
|
|
||||||
.clear_value = x.clear_value,
|
|
||||||
} else null,
|
|
||||||
.p_stencil_attachment = if (rendering_info.stencil_attachment) |x| &.{
|
|
||||||
.image_view = x.image_view,
|
|
||||||
.image_layout = x.image_layout,
|
|
||||||
.resolve_mode = x.resolve_mode,
|
|
||||||
.resolve_image_view = x.resolve_image_view,
|
|
||||||
.resolve_image_layout = x.resolve_image_layout,
|
|
||||||
.load_op = x.load_op,
|
|
||||||
.store_op = x.store_op,
|
|
||||||
.clear_value = x.clear_value,
|
|
||||||
} else null,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bindDescriptorSet(
|
pub fn bindDescriptorSet(
|
||||||
@@ -178,46 +212,21 @@ pub fn bindDescriptorSets(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bindPipeline(self: CommandBuffer, pipeline_bind_point: vk.PipelineBindPoint, pipeline: vk.Pipeline) void {
|
|
||||||
self.proxy.bindPipeline(pipeline_bind_point, pipeline);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn bindIndexBuffer(self: CommandBuffer, buffer: vk.Buffer, offset: u64, index_type: vk.IndexType) void {
|
pub fn bindIndexBuffer(self: CommandBuffer, buffer: vk.Buffer, offset: u64, index_type: vk.IndexType) void {
|
||||||
self.proxy.bindIndexBuffer(buffer, offset, index_type);
|
self.proxy.bindIndexBuffer(buffer, offset, index_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn bindPipeline(self: CommandBuffer, pipeline_bind_point: vk.PipelineBindPoint, pipeline: vk.Pipeline) void {
|
||||||
|
self.proxy.bindPipeline(pipeline_bind_point, pipeline);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bindVertexBuffer(self: CommandBuffer, index: u32, binding: VertexBufferBinding) !void {
|
||||||
|
self.proxy.bindVertexBuffers(index, &.{binding.buffer}, &.{binding.offset});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn bindVertexBuffers(self: CommandBuffer, first_binding: u32, bindings: []const VertexBufferBinding) !void {
|
pub fn bindVertexBuffers(self: CommandBuffer, first_binding: u32, bindings: []const VertexBufferBinding) !void {
|
||||||
var vertex_buffer_bindings = std.MultiArrayList(VertexBufferBinding){};
|
const buffers, const offsets = try VertexBufferBinding.transpose(bindings);
|
||||||
defer vertex_buffer_bindings.deinit(self.allocator);
|
self.proxy.bindVertexBuffers(first_binding, buffers, offsets);
|
||||||
try vertex_buffer_bindings.ensureTotalCapacity(self.allocator, bindings.len);
|
|
||||||
|
|
||||||
for (bindings) |binding| {
|
|
||||||
vertex_buffer_bindings.appendAssumeCapacity(binding);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.proxy.bindVertexBuffers(
|
|
||||||
first_binding,
|
|
||||||
vertex_buffer_bindings.items(.buffer),
|
|
||||||
vertex_buffer_bindings.items(.offset),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn drawIndexed(self: CommandBuffer, draw_indexed: DrawIndexed) void {
|
|
||||||
self.proxy.drawIndexed(
|
|
||||||
draw_indexed.index_count,
|
|
||||||
draw_indexed.instance_count,
|
|
||||||
draw_indexed.first_index,
|
|
||||||
draw_indexed.vertex_offset,
|
|
||||||
draw_indexed.first_instance,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn endCommandBuffer(self: CommandBuffer) !void {
|
|
||||||
try self.proxy.endCommandBuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn endRendering(self: CommandBuffer) void {
|
|
||||||
self.proxy.endRendering();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn copyBuffer(
|
pub fn copyBuffer(
|
||||||
@@ -239,8 +248,22 @@ pub fn copyBufferToImage(
|
|||||||
self.proxy.copyBufferToImage(src_buffer, dst_image, dst_image_layout, regions);
|
self.proxy.copyBufferToImage(src_buffer, dst_image, dst_image_layout, regions);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn nextSubpass(self: CommandBuffer, contents: vk.SubpassContents) void {
|
pub fn drawIndexed(self: CommandBuffer, draw_indexed: DrawIndexed) void {
|
||||||
self.proxy.nextSubpass(contents);
|
self.proxy.drawIndexed(
|
||||||
|
draw_indexed.index_count,
|
||||||
|
draw_indexed.instance_count,
|
||||||
|
draw_indexed.first_index,
|
||||||
|
draw_indexed.vertex_offset,
|
||||||
|
draw_indexed.first_instance,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn endCommandBuffer(self: CommandBuffer) !void {
|
||||||
|
try self.proxy.endCommandBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn endRendering(self: CommandBuffer) void {
|
||||||
|
self.proxy.endRendering();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pipelineBarrier(self: CommandBuffer, barrier: PipelineBarrier) void {
|
pub fn pipelineBarrier(self: CommandBuffer, barrier: PipelineBarrier) void {
|
||||||
@@ -254,10 +277,18 @@ pub fn pipelineBarrier(self: CommandBuffer, barrier: PipelineBarrier) void {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setScissor(self: CommandBuffer, first_scissor: u32, scissors: []const vk.Rect2D) void {
|
pub fn setScissor(self: CommandBuffer, index: u32, scissor: vk.Rect2D) void {
|
||||||
|
self.proxy.setScissor(index, &.{scissor});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setScissors(self: CommandBuffer, first_scissor: u32, scissors: []const vk.Rect2D) void {
|
||||||
self.proxy.setScissor(first_scissor, scissors);
|
self.proxy.setScissor(first_scissor, scissors);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setViewport(self: CommandBuffer, first_viewport: u32, viewports: []const vk.Viewport) void {
|
pub fn setViewport(self: CommandBuffer, index: u32, viewports: vk.Viewport) void {
|
||||||
|
self.proxy.setViewport(index, &.{viewports});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setViewports(self: CommandBuffer, first_viewport: u32, viewports: []const vk.Viewport) void {
|
||||||
self.proxy.setViewport(first_viewport, viewports);
|
self.proxy.setViewport(first_viewport, viewports);
|
||||||
}
|
}
|
||||||
|
|||||||
62
src/engine/DeviceAllocation.zig
Normal file
62
src/engine/DeviceAllocation.zig
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const DeviceAllocation = @This();
|
||||||
|
|
||||||
|
const ctx = @import("../AppContext.zig");
|
||||||
|
const vk = @import("vulkan");
|
||||||
|
|
||||||
|
device_memory: vk.DeviceMemory,
|
||||||
|
memory_type_index: u32,
|
||||||
|
allocated: usize,
|
||||||
|
capacity: usize,
|
||||||
|
|
||||||
|
/// Dummy `DeviceAllocation`, which isn't safe to use, but it is safe to call
|
||||||
|
/// `deinit` on it (once).
|
||||||
|
pub const empty: DeviceAllocation = .{
|
||||||
|
.device_memory = .null_handle,
|
||||||
|
.memory_type_index = vk.MAX_MEMORY_TYPES,
|
||||||
|
.allocated = 0,
|
||||||
|
.capacity = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn deinit(self: *DeviceAllocation) void {
|
||||||
|
const engine = ctx.engine;
|
||||||
|
|
||||||
|
engine.freeMemory(self.device_memory);
|
||||||
|
self.* = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bindBuffer(self: *DeviceAllocation, buffer: vk.Buffer) !void {
|
||||||
|
const engine = ctx.engine;
|
||||||
|
|
||||||
|
const memory_requirements = engine.getBufferMemoryRequirements(buffer);
|
||||||
|
const is_type = memory_requirements.memory_type_bits & (@as(u32, 1) << @truncate(self.memory_type_index)) != 0;
|
||||||
|
std.debug.assert(is_type);
|
||||||
|
|
||||||
|
const offset = std.mem.alignForward(usize, self.allocated, memory_requirements.alignment);
|
||||||
|
const next_allocated = offset + memory_requirements.size;
|
||||||
|
|
||||||
|
if (next_allocated > self.capacity) {
|
||||||
|
return error.OutOfMemory;
|
||||||
|
}
|
||||||
|
|
||||||
|
try engine.bindBufferMemory(buffer, self.device_memory, offset);
|
||||||
|
self.allocated = next_allocated;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bindImage(self: *DeviceAllocation, image: vk.Image) !void {
|
||||||
|
const engine = ctx.engine;
|
||||||
|
|
||||||
|
const memory_requirements = engine.getImageMemoryRequirements(image);
|
||||||
|
const is_type = memory_requirements.memory_type_bits & (@as(u32, 1) << @truncate(self.memory_type_index)) != 0;
|
||||||
|
std.debug.assert(is_type);
|
||||||
|
|
||||||
|
const offset = std.mem.alignForward(usize, self.allocated, memory_requirements.alignment);
|
||||||
|
const next_allocated = offset + memory_requirements.size;
|
||||||
|
|
||||||
|
if (next_allocated > self.capacity) {
|
||||||
|
return error.OutOfMemory;
|
||||||
|
}
|
||||||
|
|
||||||
|
try engine.bindImageMemory(image, self.device_memory, offset);
|
||||||
|
self.allocated = next_allocated;
|
||||||
|
}
|
||||||
@@ -2,25 +2,16 @@ 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");
|
||||||
|
|
||||||
|
const DeviceAllocation = @import("DeviceAllocation.zig");
|
||||||
const Queue = @import("Queue.zig");
|
const Queue = @import("Queue.zig");
|
||||||
const QueueType = @import("QueueType.zig").QueueType;
|
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 +60,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 +78,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 +88,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 +110,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 +132,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 +195,18 @@ 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);
|
|
||||||
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);
|
|
||||||
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 +223,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 +246,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 +256,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 +302,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 +353,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 +372,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 +386,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,52 +395,68 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn allocate(self: *const Engine, memory_requirements: vk.MemoryRequirements, memory_property_flags: vk.MemoryPropertyFlags) !vk.DeviceMemory {
|
pub fn allocate(self: *const Engine, memory_requirements: vk.MemoryRequirements, memory_property_flags: vk.MemoryPropertyFlags) !vk.DeviceMemory {
|
||||||
for (self.memory_types.items, 0..) |memory_type, i| {
|
const memory_type_index = try self.findMemoryTypeIndex(memory_requirements.memory_type_bits, memory_property_flags);
|
||||||
const is_type = memory_requirements.memory_type_bits & (@as(u32, 1) << @truncate(i)) != 0;
|
|
||||||
const has_flags = memory_type.property_flags.contains(memory_property_flags);
|
|
||||||
if (is_type and has_flags) {
|
|
||||||
return try self.device.allocateMemory(&.{
|
|
||||||
.allocation_size = memory_requirements.size,
|
|
||||||
.memory_type_index = @truncate(i),
|
|
||||||
}, &self.vk_allocator.interface);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return error.NoSuitableMemoryType;
|
return self.device.allocateMemory(&.{
|
||||||
|
.allocation_size = memory_requirements.size,
|
||||||
|
.memory_type_index = memory_type_index,
|
||||||
|
}, ctx.vk_allocator.capture());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocate `vk.DeviceMemory` suitable for image created by given
|
||||||
|
/// `create_info`, except the allocation is `size` bytes large.
|
||||||
|
pub fn allocateForImage(
|
||||||
|
self: *const Engine,
|
||||||
|
create_info: ImageCreateInfo,
|
||||||
|
plane_aspect: vk.ImageAspectFlags,
|
||||||
|
size: usize,
|
||||||
|
memory_property_flags: vk.MemoryPropertyFlags,
|
||||||
|
) !DeviceAllocation {
|
||||||
|
const create_info_vk = try create_info.toVk();
|
||||||
|
|
||||||
|
var memory_requirements2: vk.MemoryRequirements2 = .{
|
||||||
|
.memory_requirements = undefined,
|
||||||
|
};
|
||||||
|
self.device.getDeviceImageMemoryRequirements(&.{
|
||||||
|
.p_create_info = &create_info_vk,
|
||||||
|
.plane_aspect = plane_aspect,
|
||||||
|
}, &memory_requirements2);
|
||||||
|
|
||||||
|
const memory_requirements = memory_requirements2.memory_requirements;
|
||||||
|
const memory_type_index = try self.findMemoryTypeIndex(memory_requirements.memory_type_bits, memory_property_flags);
|
||||||
|
const device_memory = try self.device.allocateMemory(&.{
|
||||||
|
.allocation_size = size,
|
||||||
|
.memory_type_index = memory_type_index,
|
||||||
|
}, ctx.vk_allocator.capture());
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.device_memory = device_memory,
|
||||||
|
.allocated = 0,
|
||||||
|
.capacity = size,
|
||||||
|
.memory_type_index = memory_type_index,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setObjectName(self: *const Engine, handle: anytype, comptime fmt: []const u8, args: anytype) void {
|
pub fn setObjectName(self: *const Engine, handle: anytype, comptime fmt: []const u8, args: anytype) void {
|
||||||
@@ -536,6 +530,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 +541,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 +573,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,
|
||||||
@@ -694,8 +690,16 @@ fn findPresentationQueueFamily(queue_families_properties: []const vk.QueueFamily
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn makeArena(self: *const Engine) std.heap.ArenaAllocator {
|
fn findMemoryTypeIndex(self: *const Engine, memory_type_bits: u32, memory_property_flags: vk.MemoryPropertyFlags) !u32 {
|
||||||
return .init(self.vk_allocator.allocator);
|
for (self.memory_types.items, 0..) |memory_type, i| {
|
||||||
|
const is_type = memory_type_bits & (@as(u32, 1) << @truncate(i)) != 0;
|
||||||
|
const has_flags = memory_type.property_flags.contains(memory_property_flags);
|
||||||
|
if (is_type and has_flags) {
|
||||||
|
return @truncate(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return error.NoSuitableMemoryType;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolveCommandPool(self: *const Engine, queue_type: QueueType) vk.CommandPool {
|
fn resolveCommandPool(self: *const Engine, queue_type: QueueType) vk.CommandPool {
|
||||||
@@ -723,7 +727,7 @@ pub const BufferCreateInfo = struct {
|
|||||||
|
|
||||||
pub const CommandBufferAllocateInfo = struct {
|
pub const CommandBufferAllocateInfo = struct {
|
||||||
queue_type: QueueType,
|
queue_type: QueueType,
|
||||||
level: vk.CommandBufferLevel,
|
level: vk.CommandBufferLevel = .primary,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const CommandBufferFreeInfo = struct {
|
pub const CommandBufferFreeInfo = struct {
|
||||||
@@ -800,6 +804,33 @@ pub const ImageCreateInfo = struct {
|
|||||||
usage: vk.ImageUsageFlags,
|
usage: vk.ImageUsageFlags,
|
||||||
queue_family_indices: []const u32 = &.{},
|
queue_family_indices: []const u32 = &.{},
|
||||||
initial_layout: vk.ImageLayout,
|
initial_layout: vk.ImageLayout,
|
||||||
|
|
||||||
|
pub fn toVk(self: ImageCreateInfo) !vk.ImageCreateInfo {
|
||||||
|
const allocator_frame = ctx.allocator_frame;
|
||||||
|
|
||||||
|
var queue_family_indices_set: std.AutoArrayHashMapUnmanaged(u32, void) = .{};
|
||||||
|
for (self.queue_family_indices) |queue_family_index| {
|
||||||
|
try queue_family_indices_set.put(allocator_frame, queue_family_index, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
const queue_family_indices = queue_family_indices_set.keys();
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.flags = self.flags,
|
||||||
|
.image_type = self.image_type,
|
||||||
|
.format = self.format,
|
||||||
|
.extent = self.extent,
|
||||||
|
.mip_levels = self.mip_levels,
|
||||||
|
.array_layers = self.array_layers,
|
||||||
|
.samples = self.samples,
|
||||||
|
.tiling = self.tiling,
|
||||||
|
.usage = self.usage,
|
||||||
|
.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,
|
||||||
|
.p_queue_family_indices = if (queue_family_indices.len > 1) queue_family_indices.ptr else null,
|
||||||
|
.initial_layout = self.initial_layout,
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const ImageViewCreateInfo = struct {
|
pub const ImageViewCreateInfo = struct {
|
||||||
@@ -974,13 +1005,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 +1021,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 +1040,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 +1051,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 +1067,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 +1077,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 +1098,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,38 +1167,13 @@ 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 create_info_vk = try create_info.toVk();
|
||||||
defer arena.deinit();
|
return self.device.createImage(&create_info_vk, ctx.vk_allocator.capture());
|
||||||
const allocator = arena.allocator();
|
|
||||||
|
|
||||||
var queue_family_indices_set: std.AutoArrayHashMapUnmanaged(u32, void) = .{};
|
|
||||||
for (create_info.queue_family_indices) |queue_family_index| {
|
|
||||||
try queue_family_indices_set.put(allocator, queue_family_index, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
const queue_family_indices = queue_family_indices_set.keys();
|
|
||||||
|
|
||||||
const image = self.device.createImage(&.{
|
|
||||||
.flags = create_info.flags,
|
|
||||||
.image_type = create_info.image_type,
|
|
||||||
.format = create_info.format,
|
|
||||||
.extent = create_info.extent,
|
|
||||||
.mip_levels = create_info.mip_levels,
|
|
||||||
.array_layers = create_info.array_layers,
|
|
||||||
.samples = create_info.samples,
|
|
||||||
.tiling = create_info.tiling,
|
|
||||||
.usage = create_info.usage,
|
|
||||||
.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,
|
|
||||||
.p_queue_family_indices = if (queue_family_indices.len > 1) queue_family_indices.ptr else null,
|
|
||||||
.initial_layout = create_info.initial_layout,
|
|
||||||
}, &self.vk_allocator.interface);
|
|
||||||
return image;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn createImageView(self: *Engine, create_info: ImageViewCreateInfo) !vk.ImageView {
|
pub fn createImageView(self: *Engine, create_info: ImageViewCreateInfo) !vk.ImageView {
|
||||||
@@ -1184,7 +1184,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 +1195,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 +1214,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 +1245,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 +1313,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 +1325,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 +1354,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| .{
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
const Gui = @This();
|
const Gui = @This();
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const shaders = @import("shaders.zig");
|
const ctx = @import("../AppContext.zig");
|
||||||
|
const shaders = @import("../shaders.zig");
|
||||||
const vk = @import("vulkan");
|
const vk = @import("vulkan");
|
||||||
const vm = @import("vecmath");
|
const vm = @import("vecmath");
|
||||||
|
|
||||||
const CommandBuffer = @import("engine/CommandBuffer.zig");
|
const CommandBuffer = @import("CommandBuffer.zig");
|
||||||
const Engine = @import("engine/Engine.zig");
|
const Engine = @import("Engine.zig");
|
||||||
const GenericBuffer = @import("engine/GenericBuffer.zig").GenericBuffer;
|
const GenericBuffer = @import("GenericBuffer.zig").GenericBuffer;
|
||||||
const Swapchain = @import("engine/Swapchain.zig");
|
const Swapchain = @import("Swapchain.zig");
|
||||||
const Texture = @import("engine/Texture.zig");
|
const Texture = @import("Texture.zig");
|
||||||
const Textures = @import("assets/Textures.zig");
|
const Textures = @import("Textures.zig");
|
||||||
|
|
||||||
pub const Draw = struct {
|
pub const Draw = struct {
|
||||||
pub const Box = extern struct {
|
pub const Box = extern struct {
|
||||||
@@ -73,47 +74,41 @@ pub const max_text_draws = 4096;
|
|||||||
|
|
||||||
pub const max_batches = 1024;
|
pub const max_batches = 1024;
|
||||||
|
|
||||||
pub fn init(
|
pub fn init() !*Gui {
|
||||||
allocator: std.mem.Allocator,
|
const allocator_persistent = ctx.allocator_persistent;
|
||||||
engine: *Engine,
|
const engine = ctx.engine;
|
||||||
swapchain: *Swapchain,
|
const swapchain = ctx.swapchain;
|
||||||
global_uniforms_buffer: vk.Buffer,
|
|
||||||
) !Gui {
|
const gui = try allocator_persistent.create(Gui);
|
||||||
var box_gpu_buffer: GenericBuffer(void, Draw.Box) = try .init(engine, .{
|
|
||||||
|
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);
|
const box_cpu_buffer: std.ArrayList(Draw.Box) = try .initCapacity(allocator_persistent, max_box_draws);
|
||||||
errdefer box_cpu_buffer.deinit(allocator);
|
const text_cpu_buffer: std.ArrayList(Draw.Text) = try .initCapacity(allocator_persistent, max_text_draws);
|
||||||
|
const image_cpu_buffer: std.ArrayList(Draw.Image) = try .initCapacity(allocator_persistent, max_image_draws);
|
||||||
var text_cpu_buffer: std.ArrayList(Draw.Text) = try .initCapacity(allocator, max_text_draws);
|
const batches: std.ArrayList(Batch) = try .initCapacity(allocator_persistent, max_batches);
|
||||||
errdefer text_cpu_buffer.deinit(allocator);
|
|
||||||
|
|
||||||
var image_cpu_buffer: std.ArrayList(Draw.Image) = try .initCapacity(allocator, max_image_draws);
|
|
||||||
errdefer image_cpu_buffer.deinit(allocator);
|
|
||||||
|
|
||||||
var batches: std.ArrayList(Batch) = try .initCapacity(allocator, max_batches);
|
|
||||||
errdefer batches.deinit(allocator);
|
|
||||||
|
|
||||||
const sampler = try engine.createSampler(.{
|
const sampler = try engine.createSampler(.{
|
||||||
.mag_filter = .linear,
|
.mag_filter = .linear,
|
||||||
@@ -169,14 +164,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 },
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -306,21 +301,6 @@ pub fn init(
|
|||||||
|
|
||||||
try engine.updateDescriptorSets(.{
|
try engine.updateDescriptorSets(.{
|
||||||
.writes = &.{
|
.writes = &.{
|
||||||
.{
|
|
||||||
.dst_set = box_descriptor_set,
|
|
||||||
.dst_binding = 0,
|
|
||||||
.dst_array_element = 0,
|
|
||||||
.descriptor_type = .uniform_buffer,
|
|
||||||
.descriptor_infos = .{
|
|
||||||
.buffer = &.{
|
|
||||||
.{
|
|
||||||
.buffer = global_uniforms_buffer,
|
|
||||||
.offset = 0,
|
|
||||||
.range = vk.WHOLE_SIZE,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
.{
|
.{
|
||||||
.dst_set = box_descriptor_set,
|
.dst_set = box_descriptor_set,
|
||||||
.dst_binding = 1,
|
.dst_binding = 1,
|
||||||
@@ -339,7 +319,7 @@ pub fn init(
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return .{
|
gui.* = .{
|
||||||
.box_gpu_buffer = box_gpu_buffer,
|
.box_gpu_buffer = box_gpu_buffer,
|
||||||
.text_gpu_buffer = text_gpu_buffer,
|
.text_gpu_buffer = text_gpu_buffer,
|
||||||
.image_gpu_buffer = image_gpu_buffer,
|
.image_gpu_buffer = image_gpu_buffer,
|
||||||
@@ -360,27 +340,25 @@ pub fn init(
|
|||||||
.descriptor_pool = descriptor_pool,
|
.descriptor_pool = descriptor_pool,
|
||||||
.box_descriptor_set = box_descriptor_set,
|
.box_descriptor_set = box_descriptor_set,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return gui;
|
||||||
}
|
}
|
||||||
|
|
||||||
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 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.image_gpu_buffer.deinit();
|
||||||
|
self.text_gpu_buffer.deinit();
|
||||||
self.image_cpu_buffer.deinit(allocator);
|
self.box_gpu_buffer.deinit();
|
||||||
self.text_cpu_buffer.deinit(allocator);
|
|
||||||
self.box_cpu_buffer.deinit(allocator);
|
|
||||||
|
|
||||||
self.image_gpu_buffer.deinit(engine);
|
|
||||||
self.text_gpu_buffer.deinit(engine);
|
|
||||||
self.box_gpu_buffer.deinit(engine);
|
|
||||||
|
|
||||||
self.* = undefined;
|
self.* = undefined;
|
||||||
}
|
}
|
||||||
@@ -425,12 +403,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) {
|
||||||
@@ -1,8 +1,14 @@
|
|||||||
//! Module for loading persistent materials.
|
//! Module for loading persistent materials, which are all stored in a single
|
||||||
|
//! storage buffer in VRAM.
|
||||||
|
//!
|
||||||
|
//! This module is intended to be initialized once and to persist until the end
|
||||||
|
//! of the whole program's runtime. Trying to use it in any other way will
|
||||||
|
//! result in weird behavior.
|
||||||
|
|
||||||
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");
|
||||||
@@ -12,11 +18,13 @@ const Engine = @import("../engine/Engine.zig");
|
|||||||
const Textures = @import("Textures.zig");
|
const Textures = @import("Textures.zig");
|
||||||
|
|
||||||
pub const Id = enum(u16) {
|
pub const Id = enum(u16) {
|
||||||
// VOLATILE Synchronize explicit values with `init` implementation.
|
// VOLATILE When modifying the list of explicitly defined material IDs (i.e.
|
||||||
|
// any explicit enum value), we need to update `Materials.init`
|
||||||
|
// implementation.
|
||||||
|
|
||||||
/// A material ID that can be used as a "null" material. An object with this
|
/// A material ID that can be used as a "null" material. An object with this
|
||||||
/// material ID must not be rendered. If this ID is rendered, an "error"
|
/// material ID must not be rendered. If this material is rendered anyway,
|
||||||
/// replacement material will be used.
|
/// an appropriate "error"-looking material will be used.
|
||||||
empty,
|
empty,
|
||||||
_,
|
_,
|
||||||
|
|
||||||
@@ -50,7 +58,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_persistent`.
|
||||||
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 +70,30 @@ 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_persistent = ctx.allocator_persistent;
|
||||||
errdefer map.deinit(allocator);
|
|
||||||
try map.ensureTotalCapacity(allocator, max_materials);
|
|
||||||
|
|
||||||
var material_buffer = try shaders.MaterialBuffer.init(engine, .{
|
const materials = try allocator_persistent.create(Materials);
|
||||||
|
|
||||||
|
materials.* = .{
|
||||||
|
.map = .empty,
|
||||||
|
.material_buffer = undefined,
|
||||||
|
.material_count = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
try materials.map.ensureTotalCapacity(allocator_persistent, max_materials);
|
||||||
|
|
||||||
|
materials.material_buffer = try .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 materials.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 materials.material_buffer.write(.{
|
||||||
.element_offset = Id.empty.toInt(),
|
.element_offset = Id.empty.toInt(),
|
||||||
.elements = &.{
|
.elements = &.{
|
||||||
.{
|
.{
|
||||||
@@ -95,19 +111,16 @@ pub fn init(engine: *Engine, allocator: std.mem.Allocator) !Materials {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
materials.material_count += 1;
|
||||||
|
|
||||||
return .{
|
std.debug.assert(materials.material_count == @typeInfo(Id).@"enum".fields.len);
|
||||||
.map = map,
|
return materials;
|
||||||
.material_buffer = material_buffer,
|
|
||||||
.material_count = @typeInfo(Id).@"enum".fields.len,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 });
|
std.log.scoped(.deinit).debug("Deinitializing {*}", .{self});
|
||||||
|
|
||||||
self.material_buffer.deinit(engine);
|
self.material_buffer.deinit();
|
||||||
self.map.deinit(allocator);
|
|
||||||
self.* = undefined;
|
self.* = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,20 +162,12 @@ pub fn getAtom(self: *const Materials, filename: Atom) ?Id {
|
|||||||
/// 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 getOrLoad(
|
pub fn getOrLoad(self: *Materials, maybe_filename: ?[]const u8) !Id {
|
||||||
self: *Materials,
|
|
||||||
engine: *Engine,
|
|
||||||
textures: *Textures,
|
|
||||||
stbi: *media.stbi,
|
|
||||||
maybe_filename: ?[]const u8,
|
|
||||||
temp_allocator: std.mem.Allocator,
|
|
||||||
io: std.Io,
|
|
||||||
) !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 +179,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(filename, id.toInt());
|
||||||
|
|
||||||
self.map.putAssumeCapacityNoClobber(key, id);
|
self.map.putAssumeCapacityNoClobber(key, id);
|
||||||
self.material_count += 1;
|
self.material_count += 1;
|
||||||
@@ -197,15 +202,7 @@ pub fn getOrLoad(
|
|||||||
/// 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 getOrLoadAtom(
|
pub fn getOrLoadAtom(self: *Materials, filename: Atom) !Id {
|
||||||
self: *Materials,
|
|
||||||
engine: *Engine,
|
|
||||||
textures: *Textures,
|
|
||||||
stbi: *media.stbi,
|
|
||||||
filename: Atom,
|
|
||||||
temp_allocator: std.mem.Allocator,
|
|
||||||
io: std.Io,
|
|
||||||
) !Id {
|
|
||||||
if (filename != .empty) {
|
if (filename != .empty) {
|
||||||
const key: Key = .{
|
const key: Key = .{
|
||||||
.filename = filename,
|
.filename = filename,
|
||||||
@@ -220,7 +217,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(filename.toString(), id.toInt());
|
||||||
|
|
||||||
self.map.putAssumeCapacityNoClobber(key, id);
|
self.map.putAssumeCapacityNoClobber(key, id);
|
||||||
self.material_count += 1;
|
self.material_count += 1;
|
||||||
@@ -242,14 +239,16 @@ 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(self: *Materials) void {
|
||||||
const cwd = std.fs.cwd();
|
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 +260,17 @@ 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(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, filename: []const u8, index: u32) !void {
|
||||||
|
const allocator_frame = ctx.allocator_frame;
|
||||||
|
const io = ctx.io;
|
||||||
|
const textures = ctx.textures;
|
||||||
|
|
||||||
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 +299,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 +318,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(material_json.baseColorTexture, .base_color),
|
||||||
.emissive_texture = try textures.getOrLoad(engine, stbi, material_json.emissiveTexture, .emissive, io),
|
.emissive_texture = try textures.getOrLoad(material_json.emissiveTexture, .emissive),
|
||||||
.normal_texture = try textures.getOrLoad(engine, stbi, material_json.normalTexture, .normal, io),
|
.normal_texture = try textures.getOrLoad(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(material_json.occlusionRoughnessMetallicTexture, .occlusion_roughness_metallic),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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,
|
|
||||||
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 stbi = ctx.stbi;
|
||||||
|
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);
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
@@ -207,19 +218,24 @@ pub fn acquire(self: *Swapchain, engine: *Engine) !void {
|
|||||||
self.image_index = res.image_index;
|
self.image_index = res.image_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn getCurrentSwapchainImage(self: *Swapchain) *const SwapchainImage {
|
||||||
|
return &self.swapchain_images[self.image_index.?];
|
||||||
|
}
|
||||||
|
|
||||||
pub const PresentInfo = struct {
|
pub const PresentInfo = struct {
|
||||||
command_buffer: CommandBuffer,
|
command_buffer: CommandBuffer,
|
||||||
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 current_swapchain_image = &self.swapchain_images[self.image_index.?];
|
const engine = ctx.engine;
|
||||||
|
|
||||||
|
const current_swapchain_image = self.getCurrentSwapchainImage();
|
||||||
|
|
||||||
// --- 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 +243,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 +258,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 +288,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 +341,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 +375,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);
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
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");
|
||||||
|
const DeviceAllocation = @import("DeviceAllocation.zig");
|
||||||
const Engine = @import("Engine.zig");
|
const Engine = @import("Engine.zig");
|
||||||
const StagingBuffer = @import("StagingBuffer.zig");
|
const StagingBuffer = @import("StagingBuffer.zig");
|
||||||
const TargetQueue = @import("TargetQueue.zig").TargetQueue;
|
const TargetQueue = @import("TargetQueue.zig").TargetQueue;
|
||||||
@@ -67,11 +69,14 @@ pub const InitInfo = struct {
|
|||||||
height: u32,
|
height: u32,
|
||||||
usage: Usage,
|
usage: Usage,
|
||||||
target_queue: TargetQueue,
|
target_queue: TargetQueue,
|
||||||
|
device_allocation: ?*DeviceAllocation = null,
|
||||||
name: ?[]const u8 = null,
|
name: ?[]const u8 = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
image: vk.Image,
|
image: vk.Image,
|
||||||
image_view: vk.ImageView,
|
image_view: vk.ImageView,
|
||||||
|
/// Is `.null_handle` when external device memory is provided via
|
||||||
|
/// `device_allocation` in init info.
|
||||||
device_memory: vk.DeviceMemory,
|
device_memory: vk.DeviceMemory,
|
||||||
target_queue: TargetQueue,
|
target_queue: TargetQueue,
|
||||||
|
|
||||||
@@ -79,18 +84,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 +110,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);
|
||||||
@@ -116,14 +118,20 @@ pub fn init(engine: *Engine, init_info: InitInfo) !Texture {
|
|||||||
engine.setObjectName(image, "I {s} [{s}]", .{ name, @tagName(init_info.usage) });
|
engine.setObjectName(image, "I {s} [{s}]", .{ name, @tagName(init_info.usage) });
|
||||||
}
|
}
|
||||||
|
|
||||||
const memory_requirements = engine.getImageMemoryRequirements(image);
|
var device_memory: vk.DeviceMemory = .null_handle;
|
||||||
const device_memory = try engine.allocate(memory_requirements, .{ .device_local_bit = true });
|
|
||||||
errdefer engine.freeMemory(device_memory);
|
errdefer engine.freeMemory(device_memory);
|
||||||
|
|
||||||
|
if (init_info.device_allocation) |device_allocation| {
|
||||||
|
try device_allocation.bindImage(image);
|
||||||
|
} else {
|
||||||
|
const memory_requirements = engine.getImageMemoryRequirements(image);
|
||||||
|
device_memory = try engine.allocate(memory_requirements, .{ .device_local_bit = true });
|
||||||
if (init_info.name) |name| {
|
if (init_info.name) |name| {
|
||||||
engine.setObjectName(device_memory, "DM {s} [{s}]", .{ name, @tagName(init_info.usage) });
|
engine.setObjectName(device_memory, "DM {s} [{s}]", .{ name, @tagName(init_info.usage) });
|
||||||
}
|
}
|
||||||
|
|
||||||
try engine.bindImageMemory(image, device_memory, 0);
|
try engine.bindImageMemory(image, device_memory, 0);
|
||||||
|
}
|
||||||
|
|
||||||
const image_view = try engine.createImageView(.{
|
const image_view = try engine.createImageView(.{
|
||||||
.image = image,
|
.image = image,
|
||||||
@@ -156,8 +164,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 +176,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 +188,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 +196,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 +273,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 +312,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,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,11 +3,14 @@
|
|||||||
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 vk = @import("vulkan");
|
||||||
|
|
||||||
const Atom = @import("../engine/Atom.zig").Atom;
|
const Atom = @import("Atom.zig").Atom;
|
||||||
const Engine = @import("../engine/Engine.zig");
|
const DeviceAllocation = @import("DeviceAllocation.zig");
|
||||||
const Texture = @import("../engine/Texture.zig");
|
const Engine = @import("Engine.zig");
|
||||||
|
const Texture = @import("Texture.zig");
|
||||||
|
|
||||||
pub const Id = enum(u16) {
|
pub const Id = enum(u16) {
|
||||||
// VOLATILE Synchronize explicit values with `init` implementation.
|
// VOLATILE Synchronize explicit values with `init` implementation.
|
||||||
@@ -56,85 +59,123 @@ 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_persistent`.
|
||||||
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_persistent`.
|
||||||
array: std.ArrayList(Texture),
|
array: std.ArrayList(Texture),
|
||||||
|
device_allocation: DeviceAllocation,
|
||||||
|
|
||||||
/// 4096 textures of usage `.base_color` and 16×16 dimensions should take 4 MiB
|
|
||||||
/// in VRAM.
|
|
||||||
pub const max_textures = 4096;
|
pub const max_textures = 4096;
|
||||||
|
/// Enough for 4096 textures of usage `.base_color` and 64×64 dimensions.
|
||||||
|
pub const max_memory = 64 * 1024 * 1024;
|
||||||
|
|
||||||
pub fn init(engine: *Engine, allocator: std.mem.Allocator) !Textures {
|
pub fn init() !*Textures {
|
||||||
var map: std.AutoHashMapUnmanaged(Key, Id) = .empty;
|
const allocator_persistent = ctx.allocator_persistent;
|
||||||
errdefer map.deinit(allocator);
|
const engine = ctx.engine;
|
||||||
try map.ensureTotalCapacity(allocator, max_textures);
|
|
||||||
|
const textures = try allocator_persistent.create(Textures);
|
||||||
|
|
||||||
|
textures.* = .{
|
||||||
|
.map = .empty,
|
||||||
|
.array = .empty,
|
||||||
|
.device_allocation = .empty,
|
||||||
|
};
|
||||||
|
|
||||||
var array: std.ArrayList(Texture) = try .initCapacity(allocator, max_textures);
|
|
||||||
errdefer {
|
errdefer {
|
||||||
for (array.items) |*texture| {
|
for (textures.array.items) |*texture| {
|
||||||
texture.deinit(engine);
|
texture.deinit();
|
||||||
}
|
}
|
||||||
array.deinit(allocator);
|
textures.device_allocation.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try textures.map.ensureTotalCapacity(allocator_persistent, max_textures);
|
||||||
|
try textures.array.ensureTotalCapacityPrecise(allocator_persistent, max_textures);
|
||||||
|
textures.device_allocation = try engine.allocateForImage(
|
||||||
|
.{
|
||||||
|
.image_type = .@"2d",
|
||||||
|
.format = .r8g8b8a8_unorm,
|
||||||
|
.extent = .{
|
||||||
|
.width = 1,
|
||||||
|
.height = 1,
|
||||||
|
.depth = 1,
|
||||||
|
},
|
||||||
|
.mip_levels = 1,
|
||||||
|
.array_layers = 1,
|
||||||
|
.samples = .{ .@"1_bit" = true },
|
||||||
|
.tiling = .optimal,
|
||||||
|
.usage = .{
|
||||||
|
.transfer_dst_bit = true,
|
||||||
|
.sampled_bit = true,
|
||||||
|
},
|
||||||
|
.queue_family_indices = &.{
|
||||||
|
engine.graphics_queue.allocation.family,
|
||||||
|
engine.transfer_queue.allocation.family,
|
||||||
|
},
|
||||||
|
.initial_layout = .undefined,
|
||||||
|
},
|
||||||
|
.{ .color_bit = true },
|
||||||
|
max_memory,
|
||||||
|
.{ .device_local_bit = true },
|
||||||
|
);
|
||||||
|
|
||||||
// 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,
|
||||||
.target_queue = .graphics,
|
.target_queue = .graphics,
|
||||||
|
.device_allocation = &textures.device_allocation,
|
||||||
.name = "@Empty",
|
.name = "@Empty",
|
||||||
});
|
});
|
||||||
array.appendAssumeCapacity(empty_base_color_texture);
|
textures.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,
|
||||||
.target_queue = .graphics,
|
.target_queue = .graphics,
|
||||||
|
.device_allocation = &textures.device_allocation,
|
||||||
.name = "@Empty",
|
.name = "@Empty",
|
||||||
});
|
});
|
||||||
array.appendAssumeCapacity(empty_emissive_texture);
|
textures.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,
|
||||||
.target_queue = .graphics,
|
.target_queue = .graphics,
|
||||||
|
.device_allocation = &textures.device_allocation,
|
||||||
.name = "@Empty",
|
.name = "@Empty",
|
||||||
});
|
});
|
||||||
array.appendAssumeCapacity(empty_normal_texture);
|
textures.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,
|
||||||
.target_queue = .graphics,
|
.target_queue = .graphics,
|
||||||
|
.device_allocation = &textures.device_allocation,
|
||||||
.name = "@Empty",
|
.name = "@Empty",
|
||||||
});
|
});
|
||||||
array.appendAssumeCapacity(empty_occlusuion_roughness_metallic_texture);
|
textures.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 textures;
|
||||||
.map = map,
|
|
||||||
.array = array,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 });
|
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.device_allocation.deinit();
|
||||||
self.map.deinit(allocator);
|
|
||||||
self.* = undefined;
|
self.* = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,19 +213,12 @@ pub fn getAtom(self: *const Textures, filename: Atom, usage: Texture.Usage) ?Id
|
|||||||
/// if necessary. Will not return any error if the texture already exists. When
|
/// if necessary. Will not return any error if the texture already exists. When
|
||||||
/// the filename is `null`, returns an empty texture ID appropriate for given
|
/// the filename is `null`, returns an empty texture ID appropriate for given
|
||||||
/// usage.
|
/// usage.
|
||||||
pub fn getOrLoad(
|
pub fn getOrLoad(self: *Textures, maybe_filename: ?[]const u8, usage: Texture.Usage) !Id {
|
||||||
self: *Textures,
|
|
||||||
engine: *Engine,
|
|
||||||
stbi: *media.stbi,
|
|
||||||
maybe_filename: ?[]const u8,
|
|
||||||
usage: Texture.Usage,
|
|
||||||
io: std.Io,
|
|
||||||
) !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 +231,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(filename, usage, &self.device_allocation);
|
||||||
|
|
||||||
self.map.putAssumeCapacityNoClobber(key, id);
|
self.map.putAssumeCapacityNoClobber(key, id);
|
||||||
self.array.appendAssumeCapacity(texture);
|
self.array.appendAssumeCapacity(texture);
|
||||||
@@ -214,22 +248,16 @@ pub fn getOrLoad(
|
|||||||
/// if necessary. Will not return any error if the texture already exists. When
|
/// if necessary. Will not return any error if the texture already exists. When
|
||||||
/// the filename is `.empty`, returns an empty texture ID appropriate for given
|
/// the filename is `.empty`, returns an empty texture ID appropriate for given
|
||||||
/// usage.
|
/// usage.
|
||||||
pub fn getOrLoadAtom(
|
pub fn getOrLoadAtom(self: *Textures, filename: Atom, usage: Texture.Usage) !Id {
|
||||||
self: *Textures,
|
|
||||||
engine: *Engine,
|
|
||||||
stbi: *media.stbi,
|
|
||||||
filename: Atom,
|
|
||||||
usage: Texture.Usage,
|
|
||||||
io: std.Io,
|
|
||||||
) !Id {
|
|
||||||
if (filename != .empty) {
|
if (filename != .empty) {
|
||||||
const key: Key = .{
|
const key: Key = .{
|
||||||
.filename = filename,
|
.filename = filename,
|
||||||
.usage = usage,
|
.usage = usage,
|
||||||
};
|
};
|
||||||
|
|
||||||
// We don't use `getOrPutAssumeCapacity` method, because we might already be
|
// We don't use `getOrPutAssumeCapacity` method, because we might
|
||||||
// at full capacity, in which case we should return `error.OutOfTextures`.
|
// already be at full capacity, in which case we should return
|
||||||
|
// `error.OutOfTextures`.
|
||||||
|
|
||||||
if (self.map.get(key)) |id| {
|
if (self.map.get(key)) |id| {
|
||||||
return id;
|
return id;
|
||||||
@@ -237,7 +265,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(filename.toString(), usage, &self.device_allocation);
|
||||||
|
|
||||||
self.map.putAssumeCapacityNoClobber(key, id);
|
self.map.putAssumeCapacityNoClobber(key, id);
|
||||||
self.array.appendAssumeCapacity(texture);
|
self.array.appendAssumeCapacity(texture);
|
||||||
@@ -250,12 +278,13 @@ pub fn getOrLoadAtom(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn loadTexture(
|
fn loadTexture(
|
||||||
engine: *Engine,
|
|
||||||
stbi: *media.stbi,
|
|
||||||
filename: []const u8,
|
filename: []const u8,
|
||||||
usage: Texture.Usage,
|
usage: Texture.Usage,
|
||||||
io: std.Io,
|
device_allocation: *DeviceAllocation,
|
||||||
) !Texture {
|
) !Texture {
|
||||||
|
const io = ctx.io;
|
||||||
|
const stbi = ctx.stbi;
|
||||||
|
|
||||||
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 +314,17 @@ 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,
|
||||||
|
.device_allocation = device_allocation,
|
||||||
.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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
162
src/main.zig
162
src/main.zig
@@ -1,11 +1,12 @@
|
|||||||
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 media = @import("media");
|
||||||
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 +26,23 @@ 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;
|
||||||
|
}
|
||||||
|
ctx.stbi = try ctx.allocator_persistent.create(media.stbi);
|
||||||
|
ctx.stbi.* = .init(ctx.allocator_general, ctx.io);
|
||||||
|
defer ctx.stbi.deinit();
|
||||||
|
|
||||||
|
// --- 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,59 +56,98 @@ 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.textures = try .init();
|
||||||
|
defer {
|
||||||
|
ctx.textures.deinit();
|
||||||
|
ctx.textures = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.materials = try .init();
|
||||||
|
defer {
|
||||||
|
ctx.materials.deinit();
|
||||||
|
ctx.materials = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.gui = try .init();
|
||||||
|
defer {
|
||||||
|
ctx.gui.deinit();
|
||||||
|
ctx.gui = 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();
|
||||||
const dt: f32 = @floatCast(t2 - t1);
|
const dt: f32 = @floatCast(t2 - t1);
|
||||||
t1 = t2;
|
t1 = t2;
|
||||||
|
|
||||||
|
ctx.gui.beginFrame();
|
||||||
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 +158,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 +183,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 => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const vm = @import("vecmath");
|
const vm = @import("vecmath");
|
||||||
|
|
||||||
const Materials = @import("assets/Materials.zig");
|
const Materials = @import("engine/Materials.zig");
|
||||||
const GenericBuffer = @import("engine/GenericBuffer.zig").GenericBuffer;
|
const GenericBuffer = @import("engine/GenericBuffer.zig").GenericBuffer;
|
||||||
const Textures = @import("assets/Textures.zig");
|
const Textures = @import("engine/Textures.zig");
|
||||||
|
|
||||||
pub const VertexBuffer = GenericBuffer(void, Vertex);
|
pub const VertexBuffer = GenericBuffer(void, Vertex);
|
||||||
pub const IndexBuffer = GenericBuffer(void, Index);
|
pub const IndexBuffer = GenericBuffer(void, Index);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ const std = @import("std");
|
|||||||
const c = @import("const.zig");
|
const c = @import("const.zig");
|
||||||
const vm = @import("vecmath");
|
const vm = @import("vecmath");
|
||||||
|
|
||||||
const Materials = @import("assets/Materials.zig");
|
const Materials = @import("engine/Materials.zig");
|
||||||
|
|
||||||
pub const Orientation = enum(u3) {
|
pub const Orientation = enum(u3) {
|
||||||
negative_x = 0b111,
|
negative_x = 0b111,
|
||||||
|
|||||||
Reference in New Issue
Block a user