Make use of some globals to stop typing engine everywhere.
This commit is contained in:
@@ -1,16 +1,9 @@
|
||||
//! Module for string interning. A string can be converted to a stable integer
|
||||
//! constant, called an *atom*. The value of an atom for a given string is
|
||||
//! guaranteed to be stable throughout a program's runtime, but not across
|
||||
//! different runs. There can be no more than 2¹⁶ atoms.
|
||||
//!
|
||||
//! Use this module to convert string IDs into numbers, so that they can be
|
||||
//! compared more easily. The users of this module should import it with
|
||||
//! `@import("Atom.zig").Atom` and use the methods available in the `Atom` type.
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
const ctx = @import("../AppContext.zig");
|
||||
|
||||
pub const Atom = enum(u16) {
|
||||
// VOLATILE Synchronize explicit values with `init` implementation.
|
||||
// VOLATILE Synchronize explicit values with `Atoms.init` implementation.
|
||||
|
||||
/// Atom representing an empty string, i.e. `""`.
|
||||
empty,
|
||||
@@ -36,29 +29,32 @@ pub const Atom = enum(u16) {
|
||||
/// Turn a string into an atom. Returns either an existing atom or makes a
|
||||
/// new one, if necessary. This will always produce a valid atom. Will not
|
||||
/// return any error if the atom already exists.
|
||||
pub fn fromString(string: []const u8, io: std.Io) error{ Canceled, OutOfMemory, OutOfAtoms }!Atom {
|
||||
try mutex.lock(io);
|
||||
defer mutex.unlock(io);
|
||||
pub fn fromString(string: []const u8) error{ OutOfMemory, OutOfAtoms }!Atom {
|
||||
const allocator_general = ctx.allocator_general;
|
||||
const allocator_persistent = ctx.allocator_persistent;
|
||||
const io = ctx.io;
|
||||
const atoms = ctx.atoms;
|
||||
|
||||
std.debug.assert(initialized);
|
||||
atoms.mutex.lockUncancelable(io);
|
||||
defer atoms.mutex.unlock(io);
|
||||
|
||||
const entry = try map.getOrPut(allocator, string);
|
||||
const entry = try atoms.map.getOrPut(allocator_general, string);
|
||||
|
||||
if (entry.found_existing) {
|
||||
return entry.value_ptr.*;
|
||||
} else {
|
||||
errdefer _ = map.remove(string);
|
||||
const atom = Atom.fromIndexSafe(array.items.len) catch |err| switch (err) {
|
||||
errdefer _ = atoms.map.remove(string);
|
||||
const atom = Atom.fromIndexSafe(atoms.array.items.len) catch |err| switch (err) {
|
||||
error.Overflow => return error.OutOfAtoms,
|
||||
};
|
||||
|
||||
try array.ensureUnusedCapacity(allocator, 1);
|
||||
const owned_string = try toOwnedString(string);
|
||||
try atoms.array.ensureUnusedCapacity(allocator_general, 1);
|
||||
const owned_string = try allocator_persistent.dupeZ(u8, string);
|
||||
|
||||
entry.key_ptr.* = owned_string;
|
||||
entry.value_ptr.* = atom;
|
||||
|
||||
array.appendAssumeCapacity(owned_string);
|
||||
atoms.array.appendAssumeCapacity(owned_string);
|
||||
return atom;
|
||||
}
|
||||
}
|
||||
@@ -66,13 +62,14 @@ pub const Atom = enum(u16) {
|
||||
/// Turn a string into an atom, if the string has been already registered as
|
||||
/// an atom. Returns `null` otherwise. This will always produce a valid
|
||||
/// atom.
|
||||
pub fn fromStringIfExists(string: []const u8, io: std.Io) error{Canceled}!?Atom {
|
||||
try mutex.lock(io);
|
||||
defer mutex.unlock(io);
|
||||
pub fn fromStringIfExists(string: []const u8) ?Atom {
|
||||
const io = ctx.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.
|
||||
@@ -81,87 +78,13 @@ pub const Atom = enum(u16) {
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
try mutex.lock(io);
|
||||
defer mutex.unlock(io);
|
||||
pub fn toString(self: Atom) [:0]const u8 {
|
||||
const io = ctx.io;
|
||||
const atoms = ctx.atoms;
|
||||
|
||||
std.debug.assert(initialized);
|
||||
atoms.mutex.lockUncancelable(io);
|
||||
defer atoms.mutex.unlock(io);
|
||||
|
||||
return array.items[self.toInt()];
|
||||
return atoms.array.items[self.toInt()];
|
||||
}
|
||||
};
|
||||
|
||||
/// Flag for debug purposes, to catch misuses of the API.
|
||||
var initialized: bool = false;
|
||||
|
||||
/// Allocator used for `map` and `array`. Also used as a child allocator for
|
||||
/// `string_arena`.
|
||||
var allocator: std.mem.Allocator = undefined;
|
||||
|
||||
/// Allocator for all string values. All values of `map` and keys of `map` are
|
||||
/// allocated with this arena. The strings are allocated with a null terminator
|
||||
/// for interoperability with libraries.
|
||||
var string_arena: std.heap.ArenaAllocator = undefined;
|
||||
|
||||
/// Maps a string value to an atom value.
|
||||
var map: std.StringHashMapUnmanaged(Atom) = undefined;
|
||||
|
||||
/// Maps an atom value to a string.
|
||||
var array: std.ArrayList([:0]const u8) = undefined;
|
||||
|
||||
/// Protects all reads and writes to `map` and `array`.
|
||||
var mutex: std.Io.Mutex = .init;
|
||||
|
||||
pub fn init(_allocator: std.mem.Allocator, io: std.Io) !void {
|
||||
try mutex.lock(io);
|
||||
defer mutex.unlock(io);
|
||||
|
||||
std.debug.assert(!initialized);
|
||||
|
||||
allocator = _allocator;
|
||||
string_arena = .init(_allocator);
|
||||
map = .{};
|
||||
array = .empty;
|
||||
initialized = true;
|
||||
|
||||
// VOLATILE Synchronize with explicit values on top of `Atom` type.
|
||||
try map.put(allocator, "", .empty);
|
||||
try array.append(allocator, "");
|
||||
}
|
||||
|
||||
pub fn deinit(io: std.Io) void {
|
||||
mutex.lockUncancelable(io);
|
||||
defer mutex.unlock(io);
|
||||
|
||||
std.log.scoped(.deinit).debug("Deinitializing atoms", .{});
|
||||
std.debug.assert(initialized);
|
||||
|
||||
string_arena.deinit();
|
||||
map.deinit(allocator);
|
||||
array.deinit(allocator);
|
||||
|
||||
allocator = undefined;
|
||||
string_arena = undefined;
|
||||
map = undefined;
|
||||
array = undefined;
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
/// Dump all atoms in a readable format. Use for debugging. Does not flush the
|
||||
/// writer.
|
||||
pub fn dump(writer: std.Io.Writer, io: std.Io) void {
|
||||
mutex.lockUncancelable(io);
|
||||
defer mutex.unlock(io);
|
||||
|
||||
std.debug.assert(initialized);
|
||||
|
||||
for (array.items, 0..) |string, i| {
|
||||
const atom: u32 = @intCast(i);
|
||||
writer.print("0x{X:0<8} {s}\n", .{ atom, string });
|
||||
}
|
||||
}
|
||||
|
||||
fn toOwnedString(string: []const u8) ![:0]const u8 {
|
||||
const owned_string = try string_arena.allocator().dupeZ(u8, string);
|
||||
return owned_string;
|
||||
}
|
||||
|
||||
80
src/engine/Atoms.zig
Normal file
80
src/engine/Atoms.zig
Normal file
@@ -0,0 +1,80 @@
|
||||
//! Module for string interning. A string can be converted to a stable integer
|
||||
//! constant, called an *atom*. The value of an atom for a given string is
|
||||
//! guaranteed to be stable throughout a program's runtime, but not across
|
||||
//! different runs. There can be no more than 2¹⁶ atoms.
|
||||
//!
|
||||
//! Use this module to convert string IDs into numbers, so that they can be
|
||||
//! compared more easily. The users of this module should instead import
|
||||
//! `@import("Atom.zig").Atom` and use the methods available in the `Atom` type.
|
||||
//!
|
||||
//! This module is intended to be initialized once and persist until the end of
|
||||
//! the whole application. Trying to use it in any other way will result in
|
||||
//! weird behavior.
|
||||
|
||||
const Atoms = @This();
|
||||
const std = @import("std");
|
||||
|
||||
const ctx = @import("../AppContext.zig");
|
||||
|
||||
const Atom = @import("Atom.zig").Atom;
|
||||
|
||||
/// Maps a string value to an atom value. Uses `allocator_general`.
|
||||
map: std.StringHashMapUnmanaged(Atom),
|
||||
|
||||
/// Maps an atom value to a string. Uses `allocator_general`.
|
||||
array: std.ArrayList([:0]const u8),
|
||||
|
||||
/// Protects all reads and writes to `map` and `array`.
|
||||
mutex: std.Io.Mutex,
|
||||
|
||||
pub fn init() !*Atoms {
|
||||
const allocator_general = ctx.allocator_general;
|
||||
const allocator_persistent = ctx.allocator_persistent;
|
||||
|
||||
const atoms = try allocator_persistent.create(Atoms);
|
||||
errdefer allocator_persistent.destroy(atoms);
|
||||
|
||||
atoms.* = .{
|
||||
.map = .empty,
|
||||
.array = .empty,
|
||||
.mutex = .init,
|
||||
};
|
||||
|
||||
// VOLATILE Synchronize with explicit values on top of `Atom` type.
|
||||
try atoms.map.put(allocator_general, "", .empty);
|
||||
try atoms.array.append(allocator_general, "");
|
||||
|
||||
return atoms;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Atoms) void {
|
||||
const allocator_general = ctx.allocator_general;
|
||||
const io = ctx.io;
|
||||
|
||||
{
|
||||
// VOLATILE Unlock mutex before `self.* = undefined` line.
|
||||
self.mutex.lockUncancelable(io);
|
||||
defer self.mutex.unlock(io);
|
||||
|
||||
std.log.scoped(.deinit).debug("Deinitializing {*}", .{self});
|
||||
|
||||
self.map.deinit(allocator_general);
|
||||
self.array.deinit(allocator_general);
|
||||
}
|
||||
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
/// Dump all atoms in a readable format. Use for debugging. Does not flush the
|
||||
/// writer.
|
||||
pub fn dump(self: *const Atoms, writer: *std.Io.Writer) void {
|
||||
const io = ctx.io;
|
||||
|
||||
self.mutex.lockUncancelable(io);
|
||||
defer self.mutex.unlock(io);
|
||||
|
||||
for (self.array.items, 0..) |string, i| {
|
||||
const atom: u16 = @intCast(i);
|
||||
writer.print("0x{X:0<4} {s}\n", .{ atom, string });
|
||||
}
|
||||
}
|
||||
@@ -9,28 +9,31 @@
|
||||
const CommandBuffer = @This();
|
||||
const std = @import("std");
|
||||
|
||||
const ctx = @import("../AppContext.zig");
|
||||
const vk = @import("vulkan");
|
||||
|
||||
const Engine = @import("Engine.zig");
|
||||
const QueueType = @import("QueueType.zig").QueueType;
|
||||
|
||||
proxy: vk.CommandBufferProxy,
|
||||
allocator: std.mem.Allocator,
|
||||
queue_type: QueueType,
|
||||
|
||||
pub fn init(engine: *Engine, queue_type: QueueType) !CommandBuffer {
|
||||
pub fn init(queue_type: QueueType) !CommandBuffer {
|
||||
const engine = ctx.engine;
|
||||
|
||||
const handle = try engine.allocateCommandBuffer(.{
|
||||
.queue_type = queue_type,
|
||||
.level = .primary,
|
||||
});
|
||||
return .{
|
||||
.proxy = .init(handle, engine.device.wrapper),
|
||||
.allocator = engine.vk_allocator.allocator,
|
||||
.queue_type = queue_type,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *CommandBuffer, engine: *Engine) void {
|
||||
pub fn deinit(self: *CommandBuffer) void {
|
||||
const engine = ctx.engine;
|
||||
|
||||
engine.freeCommandBuffer(.{
|
||||
.queue_type = self.queue_type,
|
||||
.command_buffer = self.proxy.handle,
|
||||
@@ -38,7 +41,9 @@ pub fn deinit(self: *CommandBuffer, engine: *Engine) void {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -96,11 +101,9 @@ pub fn beginCommandBuffer(self: CommandBuffer) !void {
|
||||
}
|
||||
|
||||
pub fn beginRendering(self: CommandBuffer, rendering_info: RenderingInfo) !void {
|
||||
var arena: std.heap.ArenaAllocator = .init(self.allocator);
|
||||
defer arena.deinit();
|
||||
const allocator = arena.allocator();
|
||||
const allocator_frame = ctx.allocator_frame;
|
||||
|
||||
const color_attachments = try allocator.alloc(vk.RenderingAttachmentInfo, rendering_info.color_attachments.len);
|
||||
const color_attachments = try allocator_frame.alloc(vk.RenderingAttachmentInfo, rendering_info.color_attachments.len);
|
||||
for (color_attachments, rendering_info.color_attachments) |*x, color_attachment| {
|
||||
x.* = .{
|
||||
.image_view = color_attachment.image_view,
|
||||
@@ -187,9 +190,11 @@ pub fn bindIndexBuffer(self: CommandBuffer, buffer: vk.Buffer, offset: u64, inde
|
||||
}
|
||||
|
||||
pub fn bindVertexBuffers(self: CommandBuffer, first_binding: u32, bindings: []const VertexBufferBinding) !void {
|
||||
const allocator_frame = ctx.allocator_frame;
|
||||
|
||||
var vertex_buffer_bindings = std.MultiArrayList(VertexBufferBinding){};
|
||||
defer vertex_buffer_bindings.deinit(self.allocator);
|
||||
try vertex_buffer_bindings.ensureTotalCapacity(self.allocator, bindings.len);
|
||||
defer vertex_buffer_bindings.deinit(allocator_frame);
|
||||
try vertex_buffer_bindings.ensureTotalCapacity(allocator_frame, bindings.len);
|
||||
|
||||
for (bindings) |binding| {
|
||||
vertex_buffer_bindings.appendAssumeCapacity(binding);
|
||||
|
||||
@@ -2,6 +2,7 @@ const Engine = @This();
|
||||
const std = @import("std");
|
||||
|
||||
const c = @import("../const.zig");
|
||||
const ctx = @import("../AppContext.zig");
|
||||
const glfw = @import("zglfw");
|
||||
const vk = @import("vulkan");
|
||||
|
||||
@@ -10,17 +11,6 @@ const QueueType = @import("QueueType.zig").QueueType;
|
||||
const VkAllocator = @import("VkAllocator.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 {
|
||||
queue_families_properties_buffer: [max_queue_families]vk.QueueFamilyProperties,
|
||||
queue_families_in_use: [max_queue_families]u32,
|
||||
@@ -69,18 +59,15 @@ const QueueAllocations = struct {
|
||||
graphics_queue: Queue.Allocation,
|
||||
compute_queue: Queue.Allocation,
|
||||
transfer_queue: Queue.Allocation,
|
||||
/// Defined only in `surface` mode, `undefined` otherwise.
|
||||
presentation_queue: Queue.Allocation,
|
||||
};
|
||||
|
||||
const DebugUtilsMessenger = if (debug) vk.DebugUtilsMessengerEXT else void;
|
||||
|
||||
mode: Mode,
|
||||
vk_allocator: *VkAllocator,
|
||||
|
||||
base: vk.BaseWrapper,
|
||||
instance: vk.InstanceProxy,
|
||||
device: vk.DeviceProxy,
|
||||
surface: vk.SurfaceKHR,
|
||||
|
||||
debug_utils_messenger: DebugUtilsMessenger,
|
||||
physical_device: vk.PhysicalDevice,
|
||||
@@ -90,6 +77,7 @@ memory_heaps: std.ArrayList(vk.MemoryHeap),
|
||||
graphics_queue: Queue,
|
||||
compute_queue: Queue,
|
||||
transfer_queue: Queue,
|
||||
presentation_queue: Queue,
|
||||
|
||||
graphics_command_pool: vk.CommandPool,
|
||||
compute_command_pool: vk.CommandPool,
|
||||
@@ -99,15 +87,12 @@ prng_ptr: *std.Random.Pcg,
|
||||
random: std.Random,
|
||||
|
||||
const debug = @import("builtin").mode == .Debug;
|
||||
|
||||
const vk_application_info: vk.ApplicationInfo = .{
|
||||
.p_application_name = c.app_name,
|
||||
.application_version = @bitCast(vk_version),
|
||||
.p_engine_name = c.app_name,
|
||||
.engine_version = @bitCast(vk_version),
|
||||
.api_version = @bitCast(vk.API_VERSION_1_4),
|
||||
};
|
||||
|
||||
const vk_version: vk.Version = vk.makeApiVersion(
|
||||
0,
|
||||
c.app_version.major,
|
||||
c.app_version.minor,
|
||||
c.app_version.patch,
|
||||
);
|
||||
const vk_debug_utils_messenger_create_info: vk.DebugUtilsMessengerCreateInfoEXT = .{
|
||||
.message_severity = .{
|
||||
.verbose_bit_ext = false,
|
||||
@@ -124,16 +109,13 @@ const vk_debug_utils_messenger_create_info: vk.DebugUtilsMessengerCreateInfoEXT
|
||||
.p_user_data = null,
|
||||
};
|
||||
|
||||
const vk_version: vk.Version = vk.makeApiVersion(
|
||||
0,
|
||||
c.app_version.major,
|
||||
c.app_version.minor,
|
||||
c.app_version.patch,
|
||||
);
|
||||
pub fn init() !*Engine {
|
||||
const allocator_persistent = ctx.allocator_persistent;
|
||||
const io = ctx.io;
|
||||
const window = ctx.window;
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator, io: std.Io, maybe_window: ?*glfw.Window) !Engine {
|
||||
const vk_allocator = try VkAllocator.init(allocator, io);
|
||||
errdefer vk_allocator.deinit();
|
||||
const engine = try allocator_persistent.create(Engine);
|
||||
errdefer allocator_persistent.destroy(engine);
|
||||
|
||||
// --- LOAD BASE DISPATCH --------------------------------------------------
|
||||
|
||||
@@ -149,35 +131,39 @@ pub fn init(allocator: std.mem.Allocator, io: std.Io, maybe_window: ?*glfw.Windo
|
||||
try enabled_extensions.appendBounded(vk.extensions.ext_debug_utils.name);
|
||||
}
|
||||
|
||||
if (maybe_window != null) {
|
||||
const glfw_instance_extensions = try glfw.getRequiredInstanceExtensions();
|
||||
try enabled_extensions.appendSliceBounded(glfw_instance_extensions);
|
||||
}
|
||||
const glfw_instance_extensions = try glfw.getRequiredInstanceExtensions();
|
||||
try enabled_extensions.appendSliceBounded(glfw_instance_extensions);
|
||||
|
||||
const enabled_layers: []const [*:0]const u8 = if (debug) &.{"VK_LAYER_KHRONOS_validation"} else &.{};
|
||||
|
||||
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,
|
||||
.pp_enabled_layer_names = enabled_layers.ptr,
|
||||
.enabled_extension_count = @intCast(enabled_extensions.items.len),
|
||||
.pp_enabled_extension_names = enabled_extensions.items.ptr,
|
||||
.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);
|
||||
errdefer allocator.destroy(instance_wrapper_ptr);
|
||||
const instance_wrapper_ptr = try allocator_persistent.create(vk.InstanceWrapper);
|
||||
errdefer allocator_persistent.destroy(instance_wrapper_ptr);
|
||||
|
||||
instance_wrapper_ptr.* = .load(instance_handle, base.dispatch.vkGetInstanceProcAddr.?);
|
||||
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 ----------------------------------------
|
||||
|
||||
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 {
|
||||
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 -------------------
|
||||
@@ -208,20 +194,20 @@ pub fn init(allocator: std.mem.Allocator, io: std.Io, maybe_window: ?*glfw.Windo
|
||||
|
||||
const physical_device_memory_properties = instance.getPhysicalDeviceMemoryProperties(physical_device);
|
||||
|
||||
var memory_types = try std.ArrayList(vk.MemoryType).initCapacity(allocator, vk.MAX_MEMORY_TYPES);
|
||||
errdefer memory_types.deinit(allocator);
|
||||
var memory_types = try std.ArrayList(vk.MemoryType).initCapacity(allocator_persistent, vk.MAX_MEMORY_TYPES);
|
||||
errdefer memory_types.deinit(allocator_persistent);
|
||||
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);
|
||||
errdefer memory_heaps.deinit(allocator);
|
||||
var memory_heaps = try std.ArrayList(vk.MemoryHeap).initCapacity(allocator_persistent, vk.MAX_MEMORY_HEAPS);
|
||||
errdefer memory_heaps.deinit(allocator_persistent);
|
||||
memory_heaps.appendSliceAssumeCapacity(physical_device_memory_properties.memory_heaps[0..physical_device_memory_properties.memory_heap_count]);
|
||||
|
||||
// --- CREATE SURFACE ------------------------------------------------------
|
||||
|
||||
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 {
|
||||
if (maybe_window != null) instance.destroySurfaceKHR(surface, &vk_allocator.interface);
|
||||
instance.destroySurfaceKHR(surface, ctx.vk_allocator.capture());
|
||||
}
|
||||
|
||||
// --- ALLOCATE QUEUES -----------------------------------------------------
|
||||
@@ -238,12 +224,12 @@ pub fn init(allocator: std.mem.Allocator, io: std.Io, maybe_window: ?*glfw.Windo
|
||||
const graphics_queue_family = findGraphicsQueueFamily(queue_families_properties) orelse return error.NoGraphicsQueue;
|
||||
const compute_queue_family = findComputeQueueFamily(queue_families_properties) orelse return error.NoComputeQueue;
|
||||
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 compute_queue_index = queue_allocator.allocateQueueIndex(compute_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;
|
||||
|
||||
@@ -261,7 +247,7 @@ pub fn init(allocator: std.mem.Allocator, io: std.Io, maybe_window: ?*glfw.Windo
|
||||
.graphics_queue = .{ .family = graphics_queue_family, .index = graphics_queue_index },
|
||||
.compute_queue = .{ .family = compute_queue_family, .index = compute_queue_index },
|
||||
.transfer_queue = .{ .family = transfer_queue_family, .index = transfer_queue_index },
|
||||
.presentation_queue = if (maybe_window != null) .{ .family = presentation_queue_family, .index = presentation_queue_index } else undefined,
|
||||
.presentation_queue = .{ .family = presentation_queue_family, .index = presentation_queue_index },
|
||||
};
|
||||
};
|
||||
|
||||
@@ -271,9 +257,7 @@ pub fn init(allocator: std.mem.Allocator, io: std.Io, maybe_window: ?*glfw.Windo
|
||||
var enabled_extensions_buffer: [16][*:0]const u8 = undefined;
|
||||
var enabled_extensions: 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 = .{
|
||||
.features = .{
|
||||
@@ -319,35 +303,35 @@ pub fn init(allocator: std.mem.Allocator, io: std.Io, maybe_window: ?*glfw.Windo
|
||||
.p_queue_create_infos = queue_create_info.items.ptr,
|
||||
.enabled_extension_count = @intCast(enabled_extensions.items.len),
|
||||
.pp_enabled_extension_names = enabled_extensions.items.ptr,
|
||||
}, &vk_allocator.interface);
|
||||
}, ctx.vk_allocator.capture());
|
||||
};
|
||||
|
||||
const device_wrapper_ptr = try allocator.create(vk.DeviceWrapper);
|
||||
errdefer allocator.destroy(device_wrapper_ptr);
|
||||
const device_wrapper_ptr = try allocator_persistent.create(vk.DeviceWrapper);
|
||||
errdefer allocator_persistent.destroy(device_wrapper_ptr);
|
||||
|
||||
device_wrapper_ptr.* = .load(device_handle, instance.wrapper.dispatch.vkGetDeviceProcAddr.?);
|
||||
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 ------------------------------------
|
||||
|
||||
const graphics_command_pool = try device.createCommandPool(&.{
|
||||
.flags = .{ .transient_bit = true },
|
||||
.queue_family_index = queue_allocations.graphics_queue.family,
|
||||
}, &vk_allocator.interface);
|
||||
errdefer device.destroyCommandPool(graphics_command_pool, &vk_allocator.interface);
|
||||
}, ctx.vk_allocator.capture());
|
||||
errdefer device.destroyCommandPool(graphics_command_pool, ctx.vk_allocator.capture());
|
||||
|
||||
const compute_command_pool = try device.createCommandPool(&.{
|
||||
.flags = .{ .transient_bit = true },
|
||||
.queue_family_index = queue_allocations.compute_queue.family,
|
||||
}, &vk_allocator.interface);
|
||||
errdefer device.destroyCommandPool(compute_command_pool, &vk_allocator.interface);
|
||||
}, ctx.vk_allocator.capture());
|
||||
errdefer device.destroyCommandPool(compute_command_pool, ctx.vk_allocator.capture());
|
||||
|
||||
const transfer_command_pool = try device.createCommandPool(&.{
|
||||
.flags = .{ .transient_bit = true },
|
||||
.queue_family_index = queue_allocations.transfer_queue.family,
|
||||
}, &vk_allocator.interface);
|
||||
errdefer device.destroyCommandPool(transfer_command_pool, &vk_allocator.interface);
|
||||
}, ctx.vk_allocator.capture());
|
||||
errdefer device.destroyCommandPool(transfer_command_pool, ctx.vk_allocator.capture());
|
||||
|
||||
const graphics_queue = Queue.init(
|
||||
device.getDeviceQueue(
|
||||
@@ -370,18 +354,18 @@ pub fn init(allocator: std.mem.Allocator, io: std.Io, maybe_window: ?*glfw.Windo
|
||||
),
|
||||
queue_allocations.transfer_queue,
|
||||
);
|
||||
const presentation_queue: Queue = if (maybe_window != null) .init(
|
||||
const presentation_queue = Queue.init(
|
||||
device.getDeviceQueue(
|
||||
queue_allocations.presentation_queue.family,
|
||||
queue_allocations.presentation_queue.index,
|
||||
),
|
||||
queue_allocations.presentation_queue,
|
||||
) else undefined;
|
||||
);
|
||||
|
||||
// --- CREATE AND SEED RNG -------------------------------------------------
|
||||
|
||||
const prng_ptr = try allocator.create(std.Random.Pcg);
|
||||
errdefer allocator.destroy(prng_ptr);
|
||||
const prng_ptr = try allocator_persistent.create(std.Random.Pcg);
|
||||
errdefer allocator_persistent.destroy(prng_ptr);
|
||||
|
||||
const timestamp: u64 = @bitCast(@as(i64, @truncate(std.Io.Timestamp.now(io, .awake).nanoseconds)));
|
||||
prng_ptr.* = .init(timestamp);
|
||||
@@ -389,17 +373,11 @@ pub fn init(allocator: std.mem.Allocator, io: std.Io, maybe_window: ?*glfw.Windo
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
return .{
|
||||
.mode = if (maybe_window) |window| .{ .surface = .{
|
||||
.window = window,
|
||||
.surface = surface,
|
||||
.presentation_queue = presentation_queue,
|
||||
} } else .{ .headless = {} },
|
||||
.vk_allocator = vk_allocator,
|
||||
|
||||
engine.* = .{
|
||||
.base = base,
|
||||
.instance = instance,
|
||||
.device = device,
|
||||
.surface = surface,
|
||||
|
||||
.debug_utils_messenger = debug_utils_messenger,
|
||||
.physical_device = physical_device,
|
||||
@@ -409,6 +387,7 @@ pub fn init(allocator: std.mem.Allocator, io: std.Io, maybe_window: ?*glfw.Windo
|
||||
.graphics_queue = graphics_queue,
|
||||
.compute_queue = compute_queue,
|
||||
.transfer_queue = transfer_queue,
|
||||
.presentation_queue = presentation_queue,
|
||||
|
||||
.graphics_command_pool = graphics_command_pool,
|
||||
.compute_command_pool = compute_command_pool,
|
||||
@@ -417,35 +396,23 @@ pub fn init(allocator: std.mem.Allocator, io: std.Io, maybe_window: ?*glfw.Windo
|
||||
.prng_ptr = prng_ptr,
|
||||
.random = random,
|
||||
};
|
||||
|
||||
return engine;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Engine) void {
|
||||
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);
|
||||
self.device.destroyCommandPool(self.compute_command_pool, &self.vk_allocator.interface);
|
||||
self.device.destroyCommandPool(self.transfer_command_pool, &self.vk_allocator.interface);
|
||||
if (debug) self.instance.destroyDebugUtilsMessengerEXT(self.debug_utils_messenger, ctx.vk_allocator.capture());
|
||||
|
||||
self.device.destroyDevice(&self.vk_allocator.interface);
|
||||
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.instance.destroyInstance(ctx.vk_allocator.capture());
|
||||
|
||||
self.* = undefined;
|
||||
}
|
||||
@@ -458,7 +425,7 @@ pub fn allocate(self: *const Engine, memory_requirements: vk.MemoryRequirements,
|
||||
return try self.device.allocateMemory(&.{
|
||||
.allocation_size = memory_requirements.size,
|
||||
.memory_type_index = @truncate(i),
|
||||
}, &self.vk_allocator.interface);
|
||||
}, ctx.vk_allocator.capture());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -536,6 +503,8 @@ pub fn queueSubmit(
|
||||
command_buffer: vk.CommandBuffer,
|
||||
submit_info: SubmitInfo,
|
||||
) !void {
|
||||
const allocator_frame = ctx.allocator_frame;
|
||||
|
||||
const queue = switch (queue_type) {
|
||||
.graphics => self.graphics_queue.handle,
|
||||
.compute => self.compute_queue.handle,
|
||||
@@ -545,8 +514,8 @@ pub fn queueSubmit(
|
||||
const command_buffers = [_]vk.CommandBuffer{command_buffer};
|
||||
|
||||
var wait_semaphores = std.MultiArrayList(WaitSemaphore){};
|
||||
defer wait_semaphores.deinit(self.vk_allocator.allocator);
|
||||
try wait_semaphores.ensureTotalCapacity(self.vk_allocator.allocator, submit_info.wait_semaphores.len);
|
||||
defer wait_semaphores.deinit(allocator_frame);
|
||||
try wait_semaphores.ensureTotalCapacity(allocator_frame, submit_info.wait_semaphores.len);
|
||||
|
||||
for (submit_info.wait_semaphores) |wait_semaphore| {
|
||||
wait_semaphores.appendAssumeCapacity(wait_semaphore);
|
||||
@@ -577,7 +546,7 @@ pub fn queuePresent(self: *Engine, present_info: PresentInfo) !vk.Result {
|
||||
const swapchains = [_]vk.SwapchainKHR{present_info.swapchain};
|
||||
const 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),
|
||||
.p_wait_semaphores = present_info.wait_semaphores.ptr,
|
||||
.swapchain_count = swapchains.len,
|
||||
@@ -974,13 +943,11 @@ pub fn bindImageMemory(self: *Engine, image: vk.Image, memory: vk.DeviceMemory,
|
||||
}
|
||||
|
||||
pub fn createBuffer(self: *Engine, create_info: BufferCreateInfo) !vk.Buffer {
|
||||
var arena = self.makeArena();
|
||||
defer arena.deinit();
|
||||
const allocator = arena.allocator();
|
||||
const allocator_frame = ctx.allocator_frame;
|
||||
|
||||
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, {});
|
||||
try queue_family_indices_set.put(allocator_frame, queue_family_index, {});
|
||||
}
|
||||
|
||||
const queue_family_indices = queue_family_indices_set.keys();
|
||||
@@ -992,14 +959,12 @@ pub fn createBuffer(self: *Engine, create_info: BufferCreateInfo) !vk.Buffer {
|
||||
.sharing_mode = if (queue_family_indices.len > 1) .concurrent else .exclusive,
|
||||
.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,
|
||||
}, &self.vk_allocator.interface);
|
||||
}, ctx.vk_allocator.capture());
|
||||
return buffer;
|
||||
}
|
||||
|
||||
pub fn createDescriptorSetLayout(self: *Engine, create_info: DescriptorSetLayoutCreateInfo) !vk.DescriptorSetLayout {
|
||||
var arena = self.makeArena();
|
||||
defer arena.deinit();
|
||||
const allocator = arena.allocator();
|
||||
const allocator_frame = ctx.allocator_frame;
|
||||
|
||||
const has_binding_flags = blk: {
|
||||
for (create_info.bindings) |binding| {
|
||||
@@ -1013,7 +978,7 @@ pub fn createDescriptorSetLayout(self: *Engine, create_info: DescriptorSetLayout
|
||||
var p_next: ?*const anyopaque = null;
|
||||
|
||||
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| {
|
||||
x.* = binding.flags;
|
||||
}
|
||||
@@ -1024,7 +989,7 @@ pub fn createDescriptorSetLayout(self: *Engine, create_info: DescriptorSetLayout
|
||||
};
|
||||
}
|
||||
|
||||
const bindings = try allocator.alloc(vk.DescriptorSetLayoutBinding, create_info.bindings.len);
|
||||
const bindings = try allocator_frame.alloc(vk.DescriptorSetLayoutBinding, create_info.bindings.len);
|
||||
for (bindings, create_info.bindings) |*x, binding| {
|
||||
x.* = .{
|
||||
.binding = binding.binding,
|
||||
@@ -1040,7 +1005,7 @@ pub fn createDescriptorSetLayout(self: *Engine, create_info: DescriptorSetLayout
|
||||
.flags = create_info.flags,
|
||||
.binding_count = @intCast(bindings.len),
|
||||
.p_bindings = bindings.ptr,
|
||||
}, &self.vk_allocator.interface);
|
||||
}, ctx.vk_allocator.capture());
|
||||
return descriptor_set_layout;
|
||||
}
|
||||
|
||||
@@ -1050,21 +1015,19 @@ pub fn createDescriptorPool(self: *Engine, create_info: DescriptorPoolCreateInfo
|
||||
.max_sets = create_info.max_sets,
|
||||
.pool_size_count = @intCast(create_info.pool_sizes.len),
|
||||
.p_pool_sizes = create_info.pool_sizes.ptr,
|
||||
}, &self.vk_allocator.interface);
|
||||
}, ctx.vk_allocator.capture());
|
||||
return descriptor_pool;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
pub fn createGraphicsPipeline(self: *Engine, create_info: GraphicsPipelineCreateInfo) !vk.Pipeline {
|
||||
var arena = self.makeArena();
|
||||
defer arena.deinit();
|
||||
const allocator = arena.allocator();
|
||||
const allocator_frame = ctx.allocator_frame;
|
||||
|
||||
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| {
|
||||
x.* = .{
|
||||
.flags = stage.flags,
|
||||
@@ -1073,7 +1036,7 @@ pub fn createGraphicsPipeline(self: *Engine, create_info: GraphicsPipelineCreate
|
||||
.module = stage.module,
|
||||
.p_specialization_info = blk: {
|
||||
if (stage.specialization_info) |y| {
|
||||
const specialization_info = try allocator.create(vk.SpecializationInfo);
|
||||
const specialization_info = try allocator_frame.create(vk.SpecializationInfo);
|
||||
specialization_info.* = .{
|
||||
.map_entry_count = @intCast(y.map_entries.len),
|
||||
.p_map_entries = y.map_entries.ptr,
|
||||
@@ -1142,18 +1105,16 @@ pub fn createGraphicsPipeline(self: *Engine, create_info: GraphicsPipelineCreate
|
||||
};
|
||||
|
||||
var pipelines: [1]vk.Pipeline = undefined;
|
||||
_ = 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];
|
||||
}
|
||||
|
||||
pub fn createImage(self: *Engine, create_info: ImageCreateInfo) !vk.Image {
|
||||
var arena = self.makeArena();
|
||||
defer arena.deinit();
|
||||
const allocator = arena.allocator();
|
||||
const allocator_frame = ctx.allocator_frame;
|
||||
|
||||
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, {});
|
||||
try queue_family_indices_set.put(allocator_frame, queue_family_index, {});
|
||||
}
|
||||
|
||||
const queue_family_indices = queue_family_indices_set.keys();
|
||||
@@ -1172,7 +1133,7 @@ pub fn createImage(self: *Engine, create_info: ImageCreateInfo) !vk.Image {
|
||||
.queue_family_index_count = if (queue_family_indices.len > 1) @intCast(queue_family_indices.len) else 0,
|
||||
.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);
|
||||
}, ctx.vk_allocator.capture());
|
||||
return image;
|
||||
}
|
||||
|
||||
@@ -1184,7 +1145,7 @@ pub fn createImageView(self: *Engine, create_info: ImageViewCreateInfo) !vk.Imag
|
||||
.format = create_info.format,
|
||||
.components = create_info.components,
|
||||
.subresource_range = create_info.subresource_range,
|
||||
}, &self.vk_allocator.interface);
|
||||
}, ctx.vk_allocator.capture());
|
||||
return image_view;
|
||||
}
|
||||
|
||||
@@ -1195,17 +1156,17 @@ pub fn createPipelineLayout(self: *Engine, create_info: PipelineLayoutCreateInfo
|
||||
.p_set_layouts = create_info.set_layouts.ptr,
|
||||
.push_constant_range_count = @intCast(create_info.push_constant_ranges.len),
|
||||
.p_push_constant_ranges = create_info.push_constant_ranges.ptr,
|
||||
}, &self.vk_allocator.interface);
|
||||
}, ctx.vk_allocator.capture());
|
||||
return pipeline_layout;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1214,18 +1175,16 @@ pub fn createShaderModule(self: *Engine, create_info: ShaderModuleCreateInfo) !v
|
||||
.flags = create_info.flags,
|
||||
.code_size = create_info.code.len,
|
||||
.p_code = @ptrCast(create_info.code.ptr),
|
||||
}, &self.vk_allocator.interface);
|
||||
}, ctx.vk_allocator.capture());
|
||||
return shader_module;
|
||||
}
|
||||
|
||||
pub fn createSwapchain(self: *Engine, create_info: SwapchainCreateInfo) !vk.SwapchainKHR {
|
||||
var arena = self.makeArena();
|
||||
defer arena.deinit();
|
||||
const allocator = arena.allocator();
|
||||
const allocator_frame = ctx.allocator_frame;
|
||||
|
||||
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, {});
|
||||
try queue_family_indices_set.put(allocator_frame, queue_family_index, {});
|
||||
}
|
||||
|
||||
const queue_family_indices = queue_family_indices_set.keys();
|
||||
@@ -1247,56 +1206,56 @@ pub fn createSwapchain(self: *Engine, create_info: SwapchainCreateInfo) !vk.Swap
|
||||
.present_mode = create_info.present_mode,
|
||||
.clipped = create_info.clipped,
|
||||
.old_swapchain = create_info.old_swapchain,
|
||||
}, &self.vk_allocator.interface);
|
||||
}, ctx.vk_allocator.capture());
|
||||
return swapchain;
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
self.device.destroySwapchainKHR(swapchain, &self.vk_allocator.interface);
|
||||
self.device.destroySwapchainKHR(swapchain, ctx.vk_allocator.capture());
|
||||
}
|
||||
|
||||
pub fn deviceWaitIdle(self: *Engine) !void {
|
||||
@@ -1315,7 +1274,7 @@ pub fn freeDescriptorSet(self: *Engine, descriptor_pool: vk.DescriptorPool, desc
|
||||
}
|
||||
|
||||
pub fn freeMemory(self: *Engine, device_memory: vk.DeviceMemory) void {
|
||||
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 {
|
||||
@@ -1327,12 +1286,12 @@ pub fn getImageMemoryRequirements(self: *Engine, image: vk.Image) vk.MemoryRequi
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1356,11 +1315,9 @@ pub fn unmapMemory(self: *Engine, memory: vk.DeviceMemory) void {
|
||||
}
|
||||
|
||||
pub fn updateDescriptorSets(self: *Engine, update_info: DescriptorSetsUpdateInfo) !void {
|
||||
var arena = self.makeArena();
|
||||
defer arena.deinit();
|
||||
const allocator = arena.allocator();
|
||||
const allocator_frame = ctx.allocator_frame;
|
||||
|
||||
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| {
|
||||
x.* = switch (write.descriptor_infos) {
|
||||
.image => |y| .{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const std = @import("std");
|
||||
|
||||
const ctx = @import("../AppContext.zig");
|
||||
const vk = @import("vulkan");
|
||||
|
||||
const CommandBuffer = @import("CommandBuffer.zig");
|
||||
@@ -51,7 +52,9 @@ pub fn GenericBuffer(comptime Header: type, comptime Element: type) type {
|
||||
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 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 {
|
||||
std.log.scoped(.deinit).debug("Deinitializing {*} with {*}", .{ self, engine });
|
||||
pub fn deinit(self: *Self) void {
|
||||
const engine = ctx.engine;
|
||||
|
||||
std.log.scoped(.deinit).debug("Deinitializing {*}", .{self});
|
||||
|
||||
engine.freeMemory(self.device_memory);
|
||||
engine.destroyBuffer(self.buffer);
|
||||
@@ -97,7 +102,9 @@ pub fn GenericBuffer(comptime Header: type, comptime Element: type) type {
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
var staging_buffer: StagingBuffer = try .init(engine, .{
|
||||
var staging_buffer: StagingBuffer = try .init(.{
|
||||
.target_queue = self.target_queue,
|
||||
.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| {
|
||||
@memcpy(staging_memory[0..header_size], std.mem.asBytes(&header));
|
||||
}
|
||||
@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);
|
||||
defer command_buffer.deinit(engine);
|
||||
var command_buffer: CommandBuffer = try .init(.transfer);
|
||||
defer command_buffer.deinit();
|
||||
|
||||
try command_buffer.beginCommandBuffer();
|
||||
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(.{});
|
||||
defer engine.destroyFence(fence);
|
||||
|
||||
try command_buffer.submit(engine, .{ .fence = fence });
|
||||
try command_buffer.submit(.{ .fence = 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 size = array_offset + array_size;
|
||||
|
||||
|
||||
@@ -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 std = @import("std");
|
||||
|
||||
const ctx = @import("../AppContext.zig");
|
||||
const media = @import("media");
|
||||
const shaders = @import("../shaders.zig");
|
||||
const vk = @import("vulkan");
|
||||
@@ -29,14 +30,15 @@ pipeline: vk.Pipeline,
|
||||
|
||||
pub fn load(
|
||||
filename: []const u8,
|
||||
engine: *Engine,
|
||||
swapchain: *Swapchain,
|
||||
stbi: *media.stbi,
|
||||
cube_size: u32,
|
||||
global_uniforms_buffer: vk.Buffer,
|
||||
temp_allocator: std.mem.Allocator,
|
||||
io: std.Io,
|
||||
) !Skybox {
|
||||
const allocator_frame = ctx.allocator_frame;
|
||||
const io = ctx.io;
|
||||
const engine = ctx.engine;
|
||||
const swapchain = ctx.swapchain;
|
||||
|
||||
std.log.debug("Loading skybox \"{s}\"...", .{filename});
|
||||
|
||||
// --- LOAD IMAGE FROM MEMORY ----------------------------------------------
|
||||
@@ -46,8 +48,7 @@ pub fn load(
|
||||
var dir = try cwd.openDir(io, "assets", .{});
|
||||
defer dir.close(io);
|
||||
|
||||
const file_buf = try dir.readFileAlloc(io, filename, temp_allocator, .unlimited);
|
||||
defer temp_allocator.free(file_buf);
|
||||
const file_buf = try dir.readFileAlloc(io, filename, allocator_frame, .unlimited);
|
||||
|
||||
const img = try stbi.loadHdrBuf(file_buf);
|
||||
defer stbi.freeHdr(img);
|
||||
@@ -68,15 +69,15 @@ pub fn load(
|
||||
|
||||
// --- 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)),
|
||||
.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)));
|
||||
staging_buffer.unmap(engine);
|
||||
staging_buffer.unmap();
|
||||
|
||||
// --- CREATE EQUIRECTANGULAR IMAGE ----------------------------------------
|
||||
|
||||
@@ -126,8 +127,8 @@ pub fn load(
|
||||
|
||||
// --- TRANSITION TO TRANSFER_DST_OPTIMAL AND COPY -------------------------
|
||||
|
||||
var transfer_command_buffer = try CommandBuffer.init(engine, .transfer);
|
||||
defer transfer_command_buffer.deinit(engine);
|
||||
var transfer_command_buffer = try CommandBuffer.init(.transfer);
|
||||
defer transfer_command_buffer.deinit();
|
||||
|
||||
try transfer_command_buffer.beginCommandBuffer();
|
||||
transfer_command_buffer.pipelineBarrier(.{
|
||||
@@ -174,14 +175,14 @@ pub fn load(
|
||||
);
|
||||
try transfer_command_buffer.endCommandBuffer();
|
||||
|
||||
try transfer_command_buffer.submit(engine, .{
|
||||
try transfer_command_buffer.submit(.{
|
||||
.signal_semaphores = &.{semaphore_transfer_transition},
|
||||
});
|
||||
|
||||
// --- TRANSITION TO SHADER_READ_ONLY_OPTIMAL ------------------------------
|
||||
|
||||
var transition1_command_buffer = try CommandBuffer.init(engine, .compute);
|
||||
defer transition1_command_buffer.deinit(engine);
|
||||
var transition1_command_buffer = try CommandBuffer.init(.compute);
|
||||
defer transition1_command_buffer.deinit();
|
||||
|
||||
try transition1_command_buffer.beginCommandBuffer();
|
||||
transition1_command_buffer.pipelineBarrier(.{
|
||||
@@ -208,7 +209,7 @@ pub fn load(
|
||||
});
|
||||
try transition1_command_buffer.endCommandBuffer();
|
||||
|
||||
try transition1_command_buffer.submit(engine, .{
|
||||
try transition1_command_buffer.submit(.{
|
||||
.wait_semaphores = &.{.{ .semaphore = semaphore_transfer_transition }},
|
||||
.signal_semaphores = &.{semaphore_transition_compute},
|
||||
});
|
||||
@@ -326,7 +327,7 @@ pub fn load(
|
||||
.base_pipeline_handle = .null_handle,
|
||||
.base_pipeline_index = -1,
|
||||
},
|
||||
}, &engine.vk_allocator.interface, @ptrCast(&compute_pipeline));
|
||||
}, ctx.vk_allocator.capture(), @ptrCast(&compute_pipeline));
|
||||
defer engine.destroyPipeline(compute_pipeline);
|
||||
|
||||
const compute_descriptor_pool = try engine.createDescriptorPool(.{
|
||||
@@ -392,8 +393,8 @@ pub fn load(
|
||||
|
||||
// 1. Run compute shader
|
||||
|
||||
var compute_command_buffer = try CommandBuffer.init(engine, .compute);
|
||||
defer compute_command_buffer.deinit(engine);
|
||||
var compute_command_buffer = try CommandBuffer.init(.compute);
|
||||
defer compute_command_buffer.deinit();
|
||||
|
||||
try compute_command_buffer.beginCommandBuffer();
|
||||
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);
|
||||
try compute_command_buffer.endCommandBuffer();
|
||||
|
||||
try compute_command_buffer.submit(engine, .{
|
||||
try compute_command_buffer.submit(.{
|
||||
.wait_semaphores = &.{
|
||||
.{
|
||||
.semaphore = semaphore_transition_compute,
|
||||
@@ -435,8 +436,8 @@ pub fn load(
|
||||
|
||||
// 2. Transition cubemap
|
||||
|
||||
var transition2_command_buffer = try CommandBuffer.init(engine, .graphics);
|
||||
defer transition2_command_buffer.deinit(engine);
|
||||
var transition2_command_buffer = try CommandBuffer.init(.graphics);
|
||||
defer transition2_command_buffer.deinit();
|
||||
|
||||
try transition2_command_buffer.beginCommandBuffer();
|
||||
transition2_command_buffer.pipelineBarrier(.{
|
||||
@@ -463,7 +464,7 @@ pub fn load(
|
||||
});
|
||||
try transition2_command_buffer.endCommandBuffer();
|
||||
|
||||
try transition2_command_buffer.submit(engine, .{
|
||||
try transition2_command_buffer.submit(.{
|
||||
.wait_semaphores = &.{.{ .semaphore = semaphore_compute_transition }},
|
||||
.fence = fence,
|
||||
});
|
||||
@@ -474,12 +475,12 @@ pub fn load(
|
||||
|
||||
// --- SKYBOX PIPELINE -----------------------------------------------------
|
||||
|
||||
var vertex_buffer = try GenericBuffer(void, vm.Vector3).init(engine, .{
|
||||
var vertex_buffer = try GenericBuffer(void, vm.Vector3).init(.{
|
||||
.usage = .vertex,
|
||||
.target_queue = .graphics,
|
||||
.array_capacity = 8,
|
||||
});
|
||||
errdefer vertex_buffer.deinit(engine);
|
||||
errdefer vertex_buffer.deinit();
|
||||
|
||||
// 6━━━━7
|
||||
// ╱│ ╱┃
|
||||
@@ -489,7 +490,7 @@ pub fn load(
|
||||
// ┃╱ ┃╱ │╱
|
||||
// 0━━━━1 O────X
|
||||
|
||||
try vertex_buffer.write(engine, .{
|
||||
try vertex_buffer.write(.{
|
||||
.elements = &.{
|
||||
.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,
|
||||
.target_queue = .graphics,
|
||||
.array_capacity = 36,
|
||||
});
|
||||
errdefer index_buffer.deinit(engine);
|
||||
errdefer index_buffer.deinit();
|
||||
|
||||
try index_buffer.write(engine, .{
|
||||
try index_buffer.write(.{
|
||||
.elements = &.{
|
||||
// Positive X
|
||||
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.destroyPipelineLayout(self.pipeline_layout);
|
||||
engine.destroyDescriptorPool(self.descriptor_pool);
|
||||
engine.destroyDescriptorSetLayout(self.descriptor_set_layout);
|
||||
|
||||
engine.destroySampler(self.sampler);
|
||||
self.index_buffer.deinit(engine);
|
||||
self.vertex_buffer.deinit(engine);
|
||||
self.index_buffer.deinit();
|
||||
self.vertex_buffer.deinit();
|
||||
|
||||
engine.destroyImageView(self.image_view);
|
||||
engine.freeMemory(self.device_memory);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const StagingBuffer = @This();
|
||||
const std = @import("std");
|
||||
|
||||
const ctx = @import("../AppContext.zig");
|
||||
const vk = @import("vulkan");
|
||||
|
||||
const Engine = @import("Engine.zig");
|
||||
@@ -15,7 +16,9 @@ pub const InitInfo = struct {
|
||||
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) {
|
||||
.graphics => engine.graphics_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 {
|
||||
std.log.scoped(.deinit).debug("Deinitializing {*} with {*}", .{ self, engine });
|
||||
pub fn deinit(self: *StagingBuffer) void {
|
||||
const engine = ctx.engine;
|
||||
|
||||
std.log.scoped(.deinit).debug("Deinitializing {*}", .{self});
|
||||
|
||||
engine.freeMemory(self.device_memory);
|
||||
engine.destroyBuffer(self.buffer);
|
||||
@@ -59,16 +64,20 @@ pub fn deinit(self: *StagingBuffer, engine: *Engine) void {
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
pub fn map(self: StagingBuffer, engine: *Engine) ![]u8 {
|
||||
const mapped_memory = try self.mapPartial(engine, 0, self.capacity);
|
||||
pub fn map(self: StagingBuffer) ![]u8 {
|
||||
const mapped_memory = try self.mapPartial(0, self.capacity);
|
||||
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, .{});
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
const Swapchain = @This();
|
||||
const std = @import("std");
|
||||
|
||||
const ctx = @import("../AppContext.zig");
|
||||
const vk = @import("vulkan");
|
||||
|
||||
const CommandBuffer = @import("CommandBuffer.zig");
|
||||
const Engine = @import("Engine.zig");
|
||||
const QSM = @import("QueueSharingMode.zig");
|
||||
const Texture = @import("Texture.zig");
|
||||
const WaitSemaphore = @import("WaitSemaphore.zig");
|
||||
|
||||
@@ -13,6 +13,7 @@ params: Params,
|
||||
|
||||
extent: vk.Extent2D = .{ .width = 0, .height = 0 },
|
||||
swapchain: vk.SwapchainKHR = .null_handle,
|
||||
/// Allocated with `allocator_general`.
|
||||
swapchain_images: []SwapchainImage = &.{},
|
||||
depth_texture: ?Texture = null,
|
||||
image_index: ?u32 = null,
|
||||
@@ -24,28 +25,33 @@ pub const PresentResult = enum {
|
||||
suboptimal,
|
||||
};
|
||||
|
||||
pub fn init(engine: *Engine) !Swapchain {
|
||||
const params: Params = try .init(engine);
|
||||
var swapchain: Swapchain = .{
|
||||
.params = params,
|
||||
pub fn init() !*Swapchain {
|
||||
const allocator_persistent = ctx.allocator_persistent;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Swapchain, engine: *Engine) void {
|
||||
std.log.scoped(.deinit).debug("Deinitializing {*} with {*}", .{ self, engine });
|
||||
pub fn deinit(self: *Swapchain) void {
|
||||
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| {
|
||||
swapchain_image.deinit(engine);
|
||||
swapchain_image.deinit();
|
||||
}
|
||||
allocator.free(self.swapchain_images);
|
||||
allocator_general.free(self.swapchain_images);
|
||||
|
||||
if (self.depth_texture) |*depth_texture| {
|
||||
depth_texture.deinit(engine);
|
||||
depth_texture.deinit();
|
||||
}
|
||||
|
||||
if (self.swapchain != .null_handle) {
|
||||
@@ -58,11 +64,15 @@ pub fn deinit(self: *Swapchain, engine: *Engine) void {
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
pub fn recreate(self: *Swapchain, engine: *Engine) !void {
|
||||
try engine.deviceWaitIdle(); // TODO LMAO
|
||||
pub fn recreate(self: *Swapchain) !void {
|
||||
const allocator_general = ctx.allocator_general;
|
||||
const allocator_frame = ctx.allocator_frame;
|
||||
const engine = ctx.engine;
|
||||
|
||||
const mode = &engine.mode.surface;
|
||||
const allocator = engine.vk_allocator.allocator;
|
||||
// TODO This is very "LMAO just wait for all" way to synchronize. It might
|
||||
// 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_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_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 });
|
||||
|
||||
@@ -78,7 +88,7 @@ pub fn recreate(self: *Swapchain, engine: *Engine) !void {
|
||||
|
||||
const surface_capabilities = try engine.getPhysicalDeviceSurfaceCapabilities();
|
||||
const new_swapchain = try engine.createSwapchain(.{
|
||||
.surface = mode.surface,
|
||||
.surface = engine.surface,
|
||||
.min_image_count = self.params.image_count,
|
||||
.image_format = self.params.surface_format.format,
|
||||
.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 },
|
||||
.queue_family_indices = &.{
|
||||
engine.graphics_queue.allocation.family,
|
||||
mode.presentation_queue.allocation.family,
|
||||
engine.presentation_queue.allocation.family,
|
||||
},
|
||||
.pre_transform = surface_capabilities.current_transform,
|
||||
.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.
|
||||
|
||||
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 = &.{};
|
||||
|
||||
if (old_depth_texture.*) |*depth_texture| {
|
||||
depth_texture.deinit(engine);
|
||||
depth_texture.deinit();
|
||||
}
|
||||
|
||||
if (old_swapchain != .null_handle) {
|
||||
@@ -134,28 +144,27 @@ pub fn recreate(self: *Swapchain, engine: *Engine) !void {
|
||||
|
||||
// --- CREATE DEPTH TEXTURE ------------------------------------------------
|
||||
|
||||
var new_depth_texture = try Texture.init(engine, .{
|
||||
var new_depth_texture = try Texture.init(.{
|
||||
.width = extent.width,
|
||||
.height = extent.height,
|
||||
.target_queue = .graphics,
|
||||
.usage = .depth,
|
||||
.name = "@Depth",
|
||||
});
|
||||
errdefer new_depth_texture.deinit(engine);
|
||||
errdefer new_depth_texture.deinit();
|
||||
|
||||
// --- CREATE NEW SWAPCHAIN IMAGES -----------------------------------------
|
||||
|
||||
const new_swapchain_images = blk: {
|
||||
const images = try engine.getSwapchainImagesAlloc(new_swapchain, allocator);
|
||||
defer allocator.free(images);
|
||||
const images = try engine.getSwapchainImagesAlloc(new_swapchain, allocator_frame);
|
||||
|
||||
var swapchain_images: std.ArrayList(SwapchainImage) = try .initCapacity(allocator, images.len);
|
||||
errdefer swapchain_images.deinit(allocator);
|
||||
var swapchain_images: std.ArrayList(SwapchainImage) = try .initCapacity(allocator_general, images.len);
|
||||
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| {
|
||||
swapchain_images.appendAssumeCapacity(try SwapchainImage.init(engine, .{
|
||||
swapchain_images.appendAssumeCapacity(try SwapchainImage.init(.{
|
||||
.image = image,
|
||||
.format = self.params.surface_format.format,
|
||||
.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 {
|
||||
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 ----------------------------------------
|
||||
@@ -196,7 +205,9 @@ pub fn recreate(self: *Swapchain, engine: *Engine) !void {
|
||||
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.resetFence(self.fence);
|
||||
|
||||
@@ -212,14 +223,15 @@ pub const PresentInfo = struct {
|
||||
wait_semaphores: []const WaitSemaphore = &.{},
|
||||
};
|
||||
|
||||
pub fn present(self: *Swapchain, engine: *Engine, present_info: PresentInfo) !void {
|
||||
const allocator = engine.vk_allocator.allocator;
|
||||
pub fn present(self: *Swapchain, present_info: PresentInfo) !void {
|
||||
const allocator_frame = ctx.allocator_frame;
|
||||
const engine = ctx.engine;
|
||||
|
||||
const current_swapchain_image = &self.swapchain_images[self.image_index.?];
|
||||
|
||||
// --- SUBMIT COMMAND BUFFER -----------------------------------------------
|
||||
|
||||
var wait_semaphores: std.ArrayList(WaitSemaphore) = try .initCapacity(allocator, 1 + present_info.wait_semaphores.len);
|
||||
defer wait_semaphores.deinit(allocator);
|
||||
var wait_semaphores: std.ArrayList(WaitSemaphore) = try .initCapacity(allocator_frame, 1 + present_info.wait_semaphores.len);
|
||||
|
||||
wait_semaphores.appendAssumeCapacity(.{
|
||||
.semaphore = current_swapchain_image.semaphore_image_acquired,
|
||||
@@ -227,7 +239,7 @@ pub fn present(self: *Swapchain, engine: *Engine, present_info: PresentInfo) !vo
|
||||
});
|
||||
wait_semaphores.appendSliceAssumeCapacity(present_info.wait_semaphores);
|
||||
|
||||
try present_info.command_buffer.submit(engine, .{
|
||||
try present_info.command_buffer.submit(.{
|
||||
.wait_semaphores = wait_semaphores.items,
|
||||
.signal_semaphores = &.{current_swapchain_image.semaphore_render_finished},
|
||||
.fence = self.fence,
|
||||
@@ -242,15 +254,17 @@ pub fn present(self: *Swapchain, engine: *Engine, present_info: PresentInfo) !vo
|
||||
});
|
||||
}
|
||||
|
||||
fn getCurrentExtent(engine: *Engine) !vk.Extent2D {
|
||||
const mode = &engine.mode.surface;
|
||||
fn getCurrentExtent() !vk.Extent2D {
|
||||
const window = ctx.window;
|
||||
const engine = ctx.engine;
|
||||
|
||||
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)) {
|
||||
return surface_capabilities.current_extent;
|
||||
}
|
||||
|
||||
const framebuffer_width, const framebuffer_height = mode.window.getFramebufferSize();
|
||||
const framebuffer_width, const framebuffer_height = window.getFramebufferSize();
|
||||
|
||||
return .{
|
||||
.width = std.math.clamp(
|
||||
@@ -270,14 +284,14 @@ const Params = struct {
|
||||
surface_format: vk.SurfaceFormatKHR,
|
||||
image_count: u32,
|
||||
|
||||
pub fn init(engine: *Engine) !Params {
|
||||
const allocator = engine.vk_allocator.allocator;
|
||||
pub fn init() !Params {
|
||||
const allocator_frame = ctx.allocator_frame;
|
||||
const engine = ctx.engine;
|
||||
|
||||
const surface_capabilities = try engine.getPhysicalDeviceSurfaceCapabilities();
|
||||
|
||||
const surface_format = blk: {
|
||||
const surface_formats = try engine.getPhysicalDeviceSurfaceFormatsAlloc(allocator);
|
||||
defer allocator.free(surface_formats);
|
||||
const surface_formats = try engine.getPhysicalDeviceSurfaceFormatsAlloc(allocator_frame);
|
||||
|
||||
// Look for 8-bit BGRA sRGB surface format.
|
||||
|
||||
@@ -323,7 +337,9 @@ const SwapchainImage = struct {
|
||||
index: usize,
|
||||
};
|
||||
|
||||
fn init(engine: *Engine, props: InitProps) !SwapchainImage {
|
||||
fn init(props: InitProps) !SwapchainImage {
|
||||
const engine = ctx.engine;
|
||||
|
||||
const image_view = try engine.createImageView(.{
|
||||
.image = props.image,
|
||||
.view_type = .@"2d",
|
||||
@@ -355,8 +371,10 @@ const SwapchainImage = struct {
|
||||
};
|
||||
}
|
||||
|
||||
fn deinit(self: *SwapchainImage, engine: *Engine) void {
|
||||
std.log.scoped(.deinit).debug("Deinitializing {*} with {*}", .{ self, engine });
|
||||
fn deinit(self: *SwapchainImage) void {
|
||||
const engine = ctx.engine;
|
||||
|
||||
std.log.scoped(.deinit).debug("Deinitializing {*}", .{self});
|
||||
|
||||
engine.destroySemaphore(self.semaphore_render_finished);
|
||||
engine.destroySemaphore(self.semaphore_image_acquired);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const Texture = @This();
|
||||
const std = @import("std");
|
||||
|
||||
const ctx = @import("../AppContext.zig");
|
||||
const vk = @import("vulkan");
|
||||
|
||||
const CommandBuffer = @import("CommandBuffer.zig");
|
||||
@@ -79,18 +80,15 @@ width: u32,
|
||||
height: u32,
|
||||
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) {
|
||||
.graphics => engine.graphics_queue.allocation.family,
|
||||
.compute => engine.compute_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(.{
|
||||
.image_type = .@"2d",
|
||||
.format = init_info.usage.vkFormat(),
|
||||
@@ -108,7 +106,7 @@ pub fn init(engine: *Engine, init_info: InitInfo) !Texture {
|
||||
.transfer_dst_bit = init_info.usage != .depth,
|
||||
.sampled_bit = init_info.usage != .depth,
|
||||
},
|
||||
.queue_family_indices = queue_family_indices,
|
||||
.queue_family_indices = &.{ target_queue_family, transfer_queue_family },
|
||||
.initial_layout = .undefined,
|
||||
});
|
||||
errdefer engine.destroyImage(image);
|
||||
@@ -156,8 +154,10 @@ pub fn init(engine: *Engine, init_info: InitInfo) !Texture {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Texture, engine: *Engine) void {
|
||||
std.log.scoped(.deinit).debug("Deinitializing {*} with {*}", .{ self, engine });
|
||||
pub fn deinit(self: *Texture) void {
|
||||
const engine = ctx.engine;
|
||||
|
||||
std.log.scoped(.deinit).debug("Deinitializing {*}", .{self});
|
||||
|
||||
engine.destroyImageView(self.image_view);
|
||||
engine.freeMemory(self.device_memory);
|
||||
@@ -166,7 +166,9 @@ pub fn deinit(self: *Texture, engine: *Engine) void {
|
||||
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);
|
||||
std.debug.assert(texels.len == texel_count);
|
||||
switch (self.usage) {
|
||||
@@ -176,7 +178,7 @@ pub fn writeTexels(self: Texture, comptime TexelType: type, engine: *Engine, tex
|
||||
try self.writeRaw(engine, std.mem.sliceAsBytes(texels));
|
||||
}
|
||||
|
||||
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 sample_count = try std.math.mul(u32, texel_count, self.usage.samplesPerTexel());
|
||||
std.debug.assert(samples.len == sample_count);
|
||||
@@ -184,29 +186,31 @@ pub fn writeSamples(self: Texture, comptime SampleType: type, engine: *Engine, s
|
||||
inline else => |x| std.debug.assert(SampleType == x.SampleType()),
|
||||
}
|
||||
|
||||
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 byte_length = try std.math.mul(u32, texel_count, self.usage.bytesPerTexel());
|
||||
std.debug.assert(data.len == byte_length);
|
||||
std.debug.assert(self.usage != .depth);
|
||||
|
||||
var staging_buffer = try StagingBuffer.init(engine, .{
|
||||
var staging_buffer = try StagingBuffer.init(.{
|
||||
.capacity = @intCast(byte_length),
|
||||
.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);
|
||||
staging_buffer.unmap(engine);
|
||||
staging_buffer.unmap();
|
||||
|
||||
// --- TRANSITION TO TRANSFER_DST_OPTIMAL AND COPY -----------------
|
||||
|
||||
var transfer_command_buffer: CommandBuffer = try .init(engine, .transfer);
|
||||
defer transfer_command_buffer.deinit(engine);
|
||||
var transfer_command_buffer: CommandBuffer = try .init(.transfer);
|
||||
defer transfer_command_buffer.deinit();
|
||||
|
||||
try transfer_command_buffer.beginCommandBuffer();
|
||||
|
||||
@@ -259,14 +263,14 @@ pub fn writeRaw(self: Texture, engine: *Engine, data: []const u8) !void {
|
||||
const semaphore = try engine.createSemaphore();
|
||||
defer engine.destroySemaphore(semaphore);
|
||||
|
||||
try transfer_command_buffer.submit(engine, .{
|
||||
try transfer_command_buffer.submit(.{
|
||||
.signal_semaphores = &.{semaphore},
|
||||
});
|
||||
|
||||
// --- TRANSITION TO SHADER_READ_ONLY_OPTIMAL ----------------------
|
||||
|
||||
var graphics_command_buffer: CommandBuffer = try .init(engine, .graphics);
|
||||
defer graphics_command_buffer.deinit(engine);
|
||||
var graphics_command_buffer: CommandBuffer = try .init(.graphics);
|
||||
defer graphics_command_buffer.deinit();
|
||||
|
||||
try graphics_command_buffer.beginCommandBuffer();
|
||||
|
||||
@@ -298,7 +302,7 @@ pub fn writeRaw(self: Texture, engine: *Engine, data: []const u8) !void {
|
||||
const fence = try engine.createFence(.{});
|
||||
defer engine.destroyFence(fence);
|
||||
|
||||
try graphics_command_buffer.submit(engine, .{
|
||||
try graphics_command_buffer.submit(.{
|
||||
.wait_semaphores = &.{.{ .semaphore = semaphore }},
|
||||
.fence = fence,
|
||||
});
|
||||
|
||||
@@ -1,13 +1,43 @@
|
||||
const VkAllocator = @This();
|
||||
const std = @import("std");
|
||||
|
||||
const ctx = @import("../AppContext.zig");
|
||||
const vk = @import("vulkan");
|
||||
|
||||
allocator: std.mem.Allocator,
|
||||
allocations: std.AutoHashMapUnmanaged(*anyopaque, usize) = .empty,
|
||||
mutex: std.Io.Mutex = .init,
|
||||
allocated_bytes: usize = 0,
|
||||
io: std.Io,
|
||||
const Allocation = struct {
|
||||
len: usize,
|
||||
stack_trace: [stack_frames]usize,
|
||||
|
||||
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,
|
||||
|
||||
@@ -16,48 +46,66 @@ interface: vk.AllocationCallbacks,
|
||||
// around it, but it would add much complexity. Instead, we allocate everything
|
||||
// with the same, relatively big alignment and hope for the best.
|
||||
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);
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator, io: std.Io) !*VkAllocator {
|
||||
// NOTE We allocate the structure to pin its address.
|
||||
const self = try allocator.create(VkAllocator);
|
||||
pub fn init() !*VkAllocator {
|
||||
const allocator_persistent = ctx.allocator_persistent;
|
||||
|
||||
self.* = .{
|
||||
.allocator = allocator,
|
||||
// NOTE We allocate the structure to pin its address.
|
||||
const vk_allocator = try allocator_persistent.create(VkAllocator);
|
||||
errdefer allocator_persistent.destroy(vk_allocator);
|
||||
|
||||
vk_allocator.* = .{
|
||||
.allocations = .empty,
|
||||
.mutex = .init,
|
||||
.allocated_bytes = 0,
|
||||
.interface = .{
|
||||
.p_user_data = self,
|
||||
.p_user_data = vk_allocator,
|
||||
.pfn_allocation = &allocationFunction,
|
||||
.pfn_reallocation = &reallocationFunction,
|
||||
.pfn_free = &freeFunction,
|
||||
},
|
||||
.io = io,
|
||||
};
|
||||
return self;
|
||||
return vk_allocator;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *VkAllocator) void {
|
||||
const allocator_general = ctx.allocator_general;
|
||||
|
||||
std.log.scoped(.deinit).debug("Deinitializing {*}", .{self});
|
||||
|
||||
if (self.allocated_bytes > 0) {
|
||||
log.warn("{d} byte(s) still allocated while deinitializing", .{self.allocated_bytes});
|
||||
}
|
||||
|
||||
if (self.allocations.size > 0) {
|
||||
log.warn("{d} allocation(s) still tracked while deinitializing", .{self.allocations.size});
|
||||
var it = self.allocations.iterator();
|
||||
var index: usize = 0;
|
||||
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 memory = @as([*]align(actual_alignment.toByteUnits()) u8, @ptrCast(@alignCast(entry.key_ptr.*)))[0..entry.value_ptr.*];
|
||||
self.allocator.free(memory);
|
||||
const ptr = entry.key_ptr.*;
|
||||
const allocation = entry.value_ptr;
|
||||
|
||||
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);
|
||||
|
||||
self.allocations.deinit(allocator_general);
|
||||
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(
|
||||
@@ -66,18 +114,21 @@ fn allocationFunction(
|
||||
alignment: usize,
|
||||
_: vk.SystemAllocationScope,
|
||||
) 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 desired_alignment = std.mem.Alignment.fromByteUnits(alignment);
|
||||
std.debug.assert(std.mem.Alignment.compare(actual_alignment, .gte, desired_alignment));
|
||||
|
||||
self.mutex.lock(self.io) catch return null;
|
||||
defer self.mutex.unlock(self.io);
|
||||
self.mutex.lockUncancelable(io);
|
||||
defer self.mutex.unlock(io);
|
||||
|
||||
self.allocations.ensureUnusedCapacity(self.allocator, 1) catch return null;
|
||||
const memory = self.allocator.alignedAlloc(u8, actual_alignment, size) catch return null;
|
||||
self.allocations.ensureUnusedCapacity(allocator_general, 1) 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;
|
||||
//log.debug("Allocated {d} bytes(s) at 0x{x}", .{ size, @intFromPtr(memory.ptr) });
|
||||
|
||||
@@ -91,34 +142,37 @@ fn reallocationFunction(
|
||||
alignment: usize,
|
||||
_: vk.SystemAllocationScope,
|
||||
) 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 desired_alignment = std.mem.Alignment.fromByteUnits(alignment);
|
||||
std.debug.assert(std.mem.Alignment.compare(actual_alignment, .gte, desired_alignment));
|
||||
|
||||
self.mutex.lock(self.io) catch return null;
|
||||
defer self.mutex.unlock(self.io);
|
||||
self.mutex.lockUncancelable(io);
|
||||
defer self.mutex.unlock(io);
|
||||
|
||||
// NOTE If we were pedantic, we would consider the fact that we might not
|
||||
// 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_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];
|
||||
} else blk_else: {
|
||||
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));
|
||||
|
||||
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.allocations.putAssumeCapacityNoClobber(memory.ptr, size);
|
||||
self.allocations.putAssumeCapacityNoClobber(memory.ptr, .capture(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) });
|
||||
|
||||
@@ -129,16 +183,19 @@ fn freeFunction(
|
||||
p_user_data: ?*anyopaque,
|
||||
maybe_p_memory: ?*anyopaque,
|
||||
) callconv(vk.vulkan_call_conv) void {
|
||||
const allocator_general = ctx.allocator_general;
|
||||
const io = ctx.io;
|
||||
|
||||
const self: *VkAllocator = @ptrCast(@alignCast(p_user_data.?));
|
||||
|
||||
if (maybe_p_memory) |p_memory| {
|
||||
self.mutex.lockUncancelable(self.io);
|
||||
defer self.mutex.unlock(self.io);
|
||||
self.mutex.lockUncancelable(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;
|
||||
const memory = @as([*]align(actual_alignment.toByteUnits()) u8, @ptrCast(@alignCast(p_memory)))[0..size];
|
||||
|
||||
self.allocator.free(memory);
|
||||
allocator_general.free(memory);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user