Refactor literally everything
This commit is contained in:
@@ -1,13 +1,15 @@
|
|||||||
#version 460
|
#version 460
|
||||||
#extension GL_EXT_nonuniform_qualifier : require
|
#extension GL_EXT_nonuniform_qualifier : require
|
||||||
#extension GL_EXT_scalar_block_layout : require
|
#extension GL_EXT_scalar_block_layout : require
|
||||||
|
#extension GL_EXT_shader_16bit_storage : require
|
||||||
|
|
||||||
in Varyings {
|
in Varyings {
|
||||||
layout(location = 0) vec3 positionVS;
|
layout(location = 0) flat uint instance;
|
||||||
layout(location = 1) vec2 texCoord;
|
layout(location = 1) vec3 positionVS;
|
||||||
layout(location = 2) vec3 normalVS;
|
layout(location = 2) vec2 texCoord;
|
||||||
layout(location = 3) vec3 tangentVS;
|
layout(location = 3) vec3 normalVS;
|
||||||
layout(location = 4) vec3 bitangentVS;
|
layout(location = 4) vec3 tangentVS;
|
||||||
|
layout(location = 5) vec3 bitangentVS;
|
||||||
} var;
|
} var;
|
||||||
|
|
||||||
#include "main_common.glsl"
|
#include "main_common.glsl"
|
||||||
@@ -78,13 +80,14 @@ vec4 texture2DAA(texture2D tex, vec2 texCoord) {
|
|||||||
return texture(sampler2D(tex, _Sampler), texCoord);
|
return texture(sampler2D(tex, _Sampler), texCoord);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MATERIAL _Materials[_Object.material]
|
#define OBJECT _Object[var.instance]
|
||||||
|
#define MATERIAL _Materials[uint(OBJECT.material)]
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec4 baseColorTexel = texture2DAA(_Textures[MATERIAL.baseColorTexture], var.texCoord);
|
vec4 baseColorTexel = texture2DAA(_Textures[uint(MATERIAL.baseColorTexture)], var.texCoord);
|
||||||
vec4 occlusionRoughnessMetallicTexel = texture2DAA(_Textures[MATERIAL.occlusionRoughnessMetallicTexture], var.texCoord);
|
vec4 occlusionRoughnessMetallicTexel = texture2DAA(_Textures[uint(MATERIAL.occlusionRoughnessMetallicTexture)], var.texCoord);
|
||||||
vec4 normalTexel = texture2DAA(_Textures[MATERIAL.normalTexture], var.texCoord);
|
vec4 normalTexel = texture2DAA(_Textures[uint(MATERIAL.normalTexture)], var.texCoord);
|
||||||
vec4 emissiveTexel = texture2DAA(_Textures[MATERIAL.emissiveTexture], var.texCoord);
|
vec4 emissiveTexel = texture2DAA(_Textures[uint(MATERIAL.emissiveTexture)], var.texCoord);
|
||||||
|
|
||||||
vec3 baseColor = MATERIAL.baseColor * baseColorTexel.rgb;
|
vec3 baseColor = MATERIAL.baseColor * baseColorTexel.rgb;
|
||||||
float occlusion = 1.0 + MATERIAL.occlusionTextureStrength * (occlusionRoughnessMetallicTexel.r - 1.0);
|
float occlusion = 1.0 + MATERIAL.occlusionTextureStrength * (occlusionRoughnessMetallicTexel.r - 1.0);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#version 460
|
#version 460
|
||||||
#extension GL_EXT_nonuniform_qualifier : require
|
#extension GL_EXT_nonuniform_qualifier : require
|
||||||
#extension GL_EXT_scalar_block_layout : require
|
#extension GL_EXT_scalar_block_layout : require
|
||||||
|
#extension GL_EXT_shader_16bit_storage : require
|
||||||
|
|
||||||
layout(location = 0) in vec3 positionOS;
|
layout(location = 0) in vec3 positionOS;
|
||||||
layout(location = 1) in vec2 texCoord;
|
layout(location = 1) in vec2 texCoord;
|
||||||
@@ -8,29 +9,33 @@ layout(location = 2) in vec3 normalOS;
|
|||||||
layout(location = 3) in vec4 tangentOS;
|
layout(location = 3) in vec4 tangentOS;
|
||||||
|
|
||||||
out Varyings {
|
out Varyings {
|
||||||
layout(location = 0) vec3 positionVS;
|
layout(location = 0) flat uint instance;
|
||||||
layout(location = 1) vec2 texCoord;
|
layout(location = 1) vec3 positionVS;
|
||||||
layout(location = 2) vec3 normalVS;
|
layout(location = 2) vec2 texCoord;
|
||||||
layout(location = 3) vec3 tangentVS;
|
layout(location = 3) vec3 normalVS;
|
||||||
layout(location = 4) vec3 bitangentVS;
|
layout(location = 4) vec3 tangentVS;
|
||||||
|
layout(location = 5) vec3 bitangentVS;
|
||||||
} var;
|
} var;
|
||||||
|
|
||||||
#include "main_common.glsl"
|
#include "main_common.glsl"
|
||||||
|
|
||||||
|
#define OBJECT _Object[gl_InstanceIndex]
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec3 positionWS = (_Object.matrixOStoWS * vec4(positionOS, 1.0)).xyz;
|
vec3 positionWS = (OBJECT.matrixOStoWS * vec4(positionOS, 1.0)).xyz;
|
||||||
vec3 positionVS = (_Global.matrixWStoVS * vec4(positionWS, 1.0)).xyz;
|
vec3 positionVS = (_Global.matrixWStoVS * vec4(positionWS, 1.0)).xyz;
|
||||||
vec4 positionCS = _Global.matrixVStoCS * vec4(positionVS, 1.0);
|
vec4 positionCS = _Global.matrixVStoCS * vec4(positionVS, 1.0);
|
||||||
|
|
||||||
vec3 normalWS = normalize((_Object.matrixOStoWSNormal * vec4(normalOS, 0.0)).xyz);
|
vec3 normalWS = normalize((OBJECT.matrixOStoWSNormal * vec4(normalOS, 0.0)).xyz);
|
||||||
vec3 normalVS = normalize((_Global.matrixWStoVS * vec4(normalWS, 0.0)).xyz);
|
vec3 normalVS = normalize((_Global.matrixWStoVS * vec4(normalWS, 0.0)).xyz);
|
||||||
|
|
||||||
vec3 tangentWS = normalize((_Object.matrixOStoWSNormal * vec4(tangentOS.xyz, 0.0)).xyz);
|
vec3 tangentWS = normalize((OBJECT.matrixOStoWSNormal * vec4(tangentOS.xyz, 0.0)).xyz);
|
||||||
vec3 tangentVS = normalize((_Global.matrixWStoVS * vec4(tangentWS, 0.0)).xyz);
|
vec3 tangentVS = normalize((_Global.matrixWStoVS * vec4(tangentWS, 0.0)).xyz);
|
||||||
|
|
||||||
vec3 bitangentVS = tangentOS.w * normalize(cross(normalVS, tangentVS));
|
vec3 bitangentVS = tangentOS.w * normalize(cross(normalVS, tangentVS));
|
||||||
|
|
||||||
gl_Position = positionCS;
|
gl_Position = positionCS;
|
||||||
|
var.instance = gl_InstanceIndex;
|
||||||
var.positionVS = positionVS;
|
var.positionVS = positionVS;
|
||||||
var.texCoord = texCoord;
|
var.texCoord = texCoord;
|
||||||
var.normalVS = normalVS;
|
var.normalVS = normalVS;
|
||||||
|
|||||||
@@ -12,16 +12,24 @@ struct DirectionalLight {
|
|||||||
|
|
||||||
struct Material {
|
struct Material {
|
||||||
vec3 baseColor;
|
vec3 baseColor;
|
||||||
uint baseColorTexture;
|
|
||||||
vec3 emissive;
|
vec3 emissive;
|
||||||
uint emissiveTexture;
|
|
||||||
float ior;
|
float ior;
|
||||||
float metallic;
|
float metallic;
|
||||||
float normalScale;
|
float normalScale;
|
||||||
uint normalTexture;
|
|
||||||
uint occlusionRoughnessMetallicTexture;
|
|
||||||
float occlusionTextureStrength;
|
float occlusionTextureStrength;
|
||||||
float roughness;
|
float roughness;
|
||||||
|
|
||||||
|
uint16_t baseColorTexture;
|
||||||
|
uint16_t emissiveTexture;
|
||||||
|
uint16_t normalTexture;
|
||||||
|
uint16_t occlusionRoughnessMetallicTexture;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ObjectUniforms {
|
||||||
|
mat4 matrixOStoWS;
|
||||||
|
mat4 matrixOStoWSNormal;
|
||||||
|
|
||||||
|
uint16_t material;
|
||||||
};
|
};
|
||||||
|
|
||||||
layout(set = 0, binding = 0, scalar) uniform GlobalUniforms {
|
layout(set = 0, binding = 0, scalar) uniform GlobalUniforms {
|
||||||
@@ -47,11 +55,8 @@ layout(set = 0, binding = 3, scalar) readonly buffer Materials {
|
|||||||
layout(set = 0, binding = 4) uniform sampler _Sampler;
|
layout(set = 0, binding = 4) uniform sampler _Sampler;
|
||||||
layout(set = 0, binding = 5) uniform texture2D _Textures[];
|
layout(set = 0, binding = 5) uniform texture2D _Textures[];
|
||||||
|
|
||||||
// --- SET 1 --- PER OBJECT ----------------------------------------------------
|
// --- SET 1 --- PER BATCH -----------------------------------------------------
|
||||||
|
|
||||||
layout(set = 1, binding = 0, scalar) uniform ObjectUniforms {
|
layout(set = 1, binding = 0, scalar) readonly buffer ObjectsUniforms {
|
||||||
mat4 matrixOStoWS;
|
ObjectUniforms _Object[];
|
||||||
mat4 matrixOStoWSNormal;
|
};
|
||||||
|
|
||||||
uint material;
|
|
||||||
} _Object;
|
|
||||||
|
|||||||
917
src/Game.zig
917
src/Game.zig
File diff suppressed because it is too large
Load Diff
@@ -1,70 +0,0 @@
|
|||||||
pub const Atoms = @This();
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
allocator: std.mem.Allocator,
|
|
||||||
string_arena_state: std.heap.ArenaAllocator.State,
|
|
||||||
map: Map,
|
|
||||||
array: Array,
|
|
||||||
|
|
||||||
pub const Atom = extern struct {
|
|
||||||
atom: u32,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Map = std.StringHashMapUnmanaged(Atom);
|
|
||||||
pub const Array = std.ArrayList([]const u8);
|
|
||||||
|
|
||||||
pub fn init(allocator: std.mem.Allocator) Atoms {
|
|
||||||
return .{
|
|
||||||
.allocator = allocator,
|
|
||||||
.string_arena_state = .{},
|
|
||||||
.map = .empty,
|
|
||||||
.array = .empty,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(self: *Atoms) void {
|
|
||||||
self.string_arena_state.promote(std.heap.page_allocator).deinit();
|
|
||||||
self.map.deinit(self.allocator);
|
|
||||||
self.array.deinit(self.allocator);
|
|
||||||
self.* = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn getString(self: *const Atoms, atom: Atom) ?[]const u8 {
|
|
||||||
const index: usize = atom.atom;
|
|
||||||
return if (index < self.array.items.len) self.array.items[index] else null;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn getAtom(self: *const Atoms, string: []const u8) ?Atom {
|
|
||||||
return self.map.get(string);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn getOrPutAtom(self: *Atoms, string: []const u8) !Atom {
|
|
||||||
const entry = try self.map.getOrPut(self.allocator, string);
|
|
||||||
|
|
||||||
if (entry.found_existing) {
|
|
||||||
return entry.value_ptr.*;
|
|
||||||
} else {
|
|
||||||
errdefer _ = self.map.remove(string);
|
|
||||||
try self.array.ensureUnusedCapacity(self.allocator, 1);
|
|
||||||
const owned_string = try self.toOwnedString(string);
|
|
||||||
|
|
||||||
const index = self.array.items.len;
|
|
||||||
const atom: Atom = .{ .atom = @intCast(index) };
|
|
||||||
|
|
||||||
entry.key_ptr.* = owned_string;
|
|
||||||
entry.value_ptr.* = atom;
|
|
||||||
|
|
||||||
self.array.appendAssumeCapacity(owned_string);
|
|
||||||
|
|
||||||
return atom;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn toOwnedString(self: *Atoms, string: []const u8) ![]const u8 {
|
|
||||||
var string_arena = self.string_arena_state.promote(std.heap.page_allocator);
|
|
||||||
defer self.string_arena_state = string_arena.state;
|
|
||||||
|
|
||||||
const allocator = string_arena.allocator();
|
|
||||||
const owned_string = try allocator.dupe(u8, string);
|
|
||||||
return owned_string;
|
|
||||||
}
|
|
||||||
@@ -3,76 +3,112 @@ const std = @import("std");
|
|||||||
|
|
||||||
const vk = @import("vulkan");
|
const vk = @import("vulkan");
|
||||||
|
|
||||||
const Atoms = @import("Atoms.zig");
|
const atoms = @import("../engine/atoms.zig");
|
||||||
const Engine = @import("../engine/Engine.zig");
|
const Engine = @import("../engine/Engine.zig");
|
||||||
const StorageBuffer = @import("../engine/StorageBuffer.zig");
|
const GenericBuffer = @import("../engine/GenericBuffer.zig").GenericBuffer;
|
||||||
const Textures = @import("Textures.zig");
|
const Textures = @import("Textures.zig");
|
||||||
|
|
||||||
allocator: std.mem.Allocator,
|
const MaterialBuffer = GenericBuffer(void, Material);
|
||||||
|
|
||||||
map: Map,
|
map: Map,
|
||||||
storage_buffer: StorageBuffer,
|
material_buffer: MaterialBuffer,
|
||||||
next_id: Id,
|
next_id: Id,
|
||||||
|
|
||||||
pub const initial_capacity = 4;
|
// capacity * @sizeOf(Material) = 832 kiB
|
||||||
|
pub const capacity = std.math.maxInt(std.meta.Tag(Id));
|
||||||
|
|
||||||
pub const Id = extern struct {
|
pub const Key = struct { atom: atoms.Atom };
|
||||||
id: u32,
|
pub const Id = enum(u16) {
|
||||||
|
_,
|
||||||
|
|
||||||
|
pub fn next(self: Id) Id {
|
||||||
|
return @enumFromInt(@intFromEnum(self) + 1);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Map = std.AutoHashMapUnmanaged(Atoms.Atom, Id);
|
pub const Map = std.AutoHashMapUnmanaged(Key, Id);
|
||||||
|
|
||||||
pub const Material = extern struct {
|
pub const Material = extern struct {
|
||||||
base_color: [3]f32,
|
base_color: [3]f32,
|
||||||
base_color_texture: Textures.Id,
|
|
||||||
emissive: [3]f32,
|
emissive: [3]f32,
|
||||||
emissive_texture: Textures.Id,
|
|
||||||
ior: f32,
|
ior: f32,
|
||||||
metallic: f32,
|
metallic: f32,
|
||||||
normal_scale: f32,
|
normal_scale: f32,
|
||||||
normal_texture: Textures.Id,
|
|
||||||
occlusion_roughness_metallic_texture: Textures.Id,
|
|
||||||
occlusion_texture_strength: f32,
|
occlusion_texture_strength: f32,
|
||||||
roughness: f32,
|
roughness: f32,
|
||||||
|
|
||||||
|
base_color_texture: Textures.Id,
|
||||||
|
emissive_texture: Textures.Id,
|
||||||
|
normal_texture: Textures.Id,
|
||||||
|
occlusion_roughness_metallic_texture: Textures.Id,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn init(allocator: std.mem.Allocator, engine: *Engine) !Materials {
|
pub fn init(engine: *Engine, allocator: std.mem.Allocator) !Materials {
|
||||||
var map: Map = .empty;
|
var map: Map = .empty;
|
||||||
errdefer map.deinit(allocator);
|
errdefer map.deinit(allocator);
|
||||||
|
try map.ensureTotalCapacity(allocator, capacity);
|
||||||
|
|
||||||
var storage_buffer: StorageBuffer = try .init(engine, void, Material, initial_capacity);
|
var material_buffer = try MaterialBuffer.init(engine, .{
|
||||||
errdefer storage_buffer.deinit(engine);
|
.usage = .storage,
|
||||||
|
.target_queue = .graphics,
|
||||||
|
.array_capacity = capacity,
|
||||||
|
});
|
||||||
|
errdefer material_buffer.deinit(engine);
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.allocator = allocator,
|
|
||||||
.map = map,
|
.map = map,
|
||||||
.storage_buffer = storage_buffer,
|
.material_buffer = material_buffer,
|
||||||
.next_id = .{ .id = 0 },
|
.next_id = @enumFromInt(0),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Materials, engine: *Engine) void {
|
pub fn deinit(self: *Materials, engine: *Engine, allocator: std.mem.Allocator) void {
|
||||||
self.storage_buffer.deinit(engine);
|
self.material_buffer.deinit(engine);
|
||||||
self.map.deinit(self.allocator);
|
self.map.deinit(allocator);
|
||||||
self.* = undefined;
|
self.* = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getId(self: *const Materials, key: Atoms.Atom) ?Id {
|
pub fn getAtom(self: *const Materials, atom: atoms.Atom) ?Id {
|
||||||
|
const key: Key = .{ .atom = atom };
|
||||||
return self.map.get(key);
|
return self.map.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getOrLoadId(self: *Materials, engine: *Engine, textures: *Textures, atoms: *Atoms, key: Atoms.Atom) !Id {
|
pub fn getFilename(self: *const Materials, filename: []const u8) ?Id {
|
||||||
const entry = try self.map.getOrPut(self.allocator, key);
|
const atom = atoms.getAtom(filename) orelse return null;
|
||||||
|
const key: Key = .{ .atom = atom };
|
||||||
|
return self.map.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getOrLoadAtom(self: *Materials, engine: *Engine, textures: *Textures, atom: atoms.Atom, temp_allocator: std.mem.Allocator) !Id {
|
||||||
|
const key: Key = .{ .atom = atom };
|
||||||
|
const entry = self.map.getOrPutAssumeCapacity(key);
|
||||||
|
|
||||||
if (entry.found_existing) {
|
if (entry.found_existing) {
|
||||||
return entry.value_ptr.*;
|
return entry.value_ptr.*;
|
||||||
} else {
|
} else {
|
||||||
const id = try self.loadMaterial(engine, textures, atoms, key);
|
errdefer _ = self.map.remove(key);
|
||||||
|
const id = try self.loadMaterial(engine, textures, atoms.getString(atom), temp_allocator);
|
||||||
entry.value_ptr.* = id;
|
entry.value_ptr.* = id;
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn loadMaterial(self: *Materials, engine: *Engine, textures: *Textures, atoms: *Atoms, key: Atoms.Atom) !Id {
|
pub fn getOrLoadFilename(self: *Materials, engine: *Engine, textures: *Textures, filename: []const u8, temp_allocator: std.mem.Allocator) !Id {
|
||||||
|
const atom = try atoms.getOrPutAtom(filename);
|
||||||
|
const key: Key = .{ .atom = atom };
|
||||||
|
const entry = self.map.getOrPutAssumeCapacity(key);
|
||||||
|
|
||||||
|
if (entry.found_existing) {
|
||||||
|
return entry.value_ptr.*;
|
||||||
|
} else {
|
||||||
|
errdefer _ = self.map.remove(key);
|
||||||
|
const id = try self.loadMaterial(engine, textures, filename, temp_allocator);
|
||||||
|
entry.value_ptr.* = id;
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn loadMaterial(self: *Materials, engine: *Engine, textures: *Textures, filename: []const u8, temp_allocator: std.mem.Allocator) !Id {
|
||||||
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,
|
||||||
@@ -87,7 +123,6 @@ fn loadMaterial(self: *Materials, engine: *Engine, textures: *Textures, atoms: *
|
|||||||
roughness: f32 = 1,
|
roughness: f32 = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
const filename = atoms.getString(key).?;
|
|
||||||
std.log.debug("Loading material \"{s}\"...", .{filename});
|
std.log.debug("Loading material \"{s}\"...", .{filename});
|
||||||
|
|
||||||
const cwd = std.fs.cwd();
|
const cwd = std.fs.cwd();
|
||||||
@@ -102,10 +137,10 @@ fn loadMaterial(self: *Materials, engine: *Engine, textures: *Textures, atoms: *
|
|||||||
defer file.close();
|
defer file.close();
|
||||||
|
|
||||||
var file_reader = file.reader(&buffer);
|
var file_reader = file.reader(&buffer);
|
||||||
var json_reader: std.json.Reader = .init(self.allocator, &file_reader.interface);
|
var json_reader: std.json.Reader = .init(temp_allocator, &file_reader.interface);
|
||||||
defer json_reader.deinit();
|
defer json_reader.deinit();
|
||||||
|
|
||||||
const parsed: std.json.Parsed(MaterialJson) = try std.json.parseFromTokenSource(MaterialJson, self.allocator, &json_reader, .{
|
const parsed: std.json.Parsed(MaterialJson) = try std.json.parseFromTokenSource(MaterialJson, temp_allocator, &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,
|
||||||
@@ -116,65 +151,57 @@ fn loadMaterial(self: *Materials, engine: *Engine, textures: *Textures, atoms: *
|
|||||||
|
|
||||||
const base_color_texture = blk: {
|
const base_color_texture = blk: {
|
||||||
if (material_json.baseColorTexture) |name| {
|
if (material_json.baseColorTexture) |name| {
|
||||||
const atom = try atoms.getOrPutAtom(name);
|
break :blk try textures.getOrLoadFilename(engine, name, .base_color, temp_allocator);
|
||||||
break :blk try textures.getOrLoadId(engine, atoms, .{ .atom = atom, .usage = .base_color });
|
|
||||||
} else {
|
} else {
|
||||||
break :blk textures.empty_base_color;
|
break :blk .empty_base_color;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const emissive_texture = blk: {
|
const emissive_texture = blk: {
|
||||||
if (material_json.emissiveTexture) |name| {
|
if (material_json.emissiveTexture) |name| {
|
||||||
const atom = try atoms.getOrPutAtom(name);
|
break :blk try textures.getOrLoadFilename(engine, name, .emissive, temp_allocator);
|
||||||
break :blk try textures.getOrLoadId(engine, atoms, .{ .atom = atom, .usage = .emissive });
|
|
||||||
} else {
|
} else {
|
||||||
break :blk textures.empty_emissive;
|
break :blk .empty_emissive;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const normal_texture = blk: {
|
const normal_texture = blk: {
|
||||||
if (material_json.normalTexture) |name| {
|
if (material_json.normalTexture) |name| {
|
||||||
const atom = try atoms.getOrPutAtom(name);
|
break :blk try textures.getOrLoadFilename(engine, name, .normal, temp_allocator);
|
||||||
break :blk try textures.getOrLoadId(engine, atoms, .{ .atom = atom, .usage = .normal });
|
|
||||||
} else {
|
} else {
|
||||||
break :blk textures.empty_normal;
|
break :blk .empty_normal;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const occlusion_roughness_metallic_texture = blk: {
|
const occlusion_roughness_metallic_texture = blk: {
|
||||||
if (material_json.occlusionRoughnessMetallicTexture) |name| {
|
if (material_json.occlusionRoughnessMetallicTexture) |name| {
|
||||||
const atom = try atoms.getOrPutAtom(name);
|
break :blk try textures.getOrLoadFilename(engine, name, .occlusion_roughness_metallic, temp_allocator);
|
||||||
break :blk try textures.getOrLoadId(engine, atoms, .{ .atom = atom, .usage = .occlusion_roughness_metallic });
|
|
||||||
} else {
|
} else {
|
||||||
break :blk textures.empty_occlusuion_roughness_metallic;
|
break :blk .empty_occlusion_roughness_metallic;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const next_id = self.next_id;
|
try self.material_buffer.write(engine, .{
|
||||||
const offset: usize = next_id.id;
|
.element_offset = @intFromEnum(self.next_id),
|
||||||
|
.elements = &.{
|
||||||
if (offset >= self.storage_buffer.array_capacity) {
|
.{
|
||||||
try self.storage_buffer.enlarge(void, Material, engine, self.storage_buffer.array_capacity * 2);
|
.base_color = material_json.baseColor,
|
||||||
}
|
.emissive = material_json.emissive,
|
||||||
|
.ior = material_json.ior,
|
||||||
const materials = [_]Material{
|
.metallic = material_json.metallic,
|
||||||
.{
|
.normal_scale = material_json.normalScale,
|
||||||
.base_color = material_json.baseColor,
|
.occlusion_texture_strength = material_json.occlusionTextureStrength,
|
||||||
.base_color_texture = base_color_texture,
|
.roughness = material_json.roughness,
|
||||||
.emissive = material_json.emissive,
|
.base_color_texture = base_color_texture,
|
||||||
.emissive_texture = emissive_texture,
|
.emissive_texture = emissive_texture,
|
||||||
.ior = material_json.ior,
|
.normal_texture = normal_texture,
|
||||||
.metallic = material_json.metallic,
|
.occlusion_roughness_metallic_texture = occlusion_roughness_metallic_texture,
|
||||||
.normal_scale = material_json.normalScale,
|
},
|
||||||
.normal_texture = normal_texture,
|
|
||||||
.occlusion_roughness_metallic_texture = occlusion_roughness_metallic_texture,
|
|
||||||
.occlusion_texture_strength = material_json.occlusionTextureStrength,
|
|
||||||
.roughness = material_json.roughness,
|
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
|
|
||||||
try self.storage_buffer.writeOffset(void, Material, engine, {}, offset, &materials);
|
const id = self.next_id;
|
||||||
self.next_id = .{ .id = next_id.id + 1 };
|
self.next_id = self.next_id.next();
|
||||||
|
|
||||||
return next_id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,36 +3,41 @@ const std = @import("std");
|
|||||||
|
|
||||||
const stbi = @import("zstbi");
|
const stbi = @import("zstbi");
|
||||||
|
|
||||||
const Atoms = @import("Atoms.zig");
|
const atoms = @import("../engine/atoms.zig");
|
||||||
const Engine = @import("../engine/Engine.zig");
|
const Engine = @import("../engine/Engine.zig");
|
||||||
const Texture = @import("../engine/Texture.zig");
|
const Texture = @import("../engine/Texture.zig");
|
||||||
|
|
||||||
allocator: std.mem.Allocator,
|
|
||||||
map: Map,
|
map: Map,
|
||||||
array: Array,
|
textures: Array,
|
||||||
|
|
||||||
empty_base_color: Id,
|
pub const capacity = std.math.maxInt(std.meta.Tag(Id));
|
||||||
empty_emissive: Id,
|
|
||||||
empty_normal: Id,
|
|
||||||
empty_occlusuion_roughness_metallic: Id,
|
|
||||||
|
|
||||||
pub const Id = extern struct {
|
|
||||||
id: u32,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Key = struct {
|
pub const Key = struct {
|
||||||
atom: Atoms.Atom,
|
atom: atoms.Atom,
|
||||||
usage: Texture.Usage,
|
usage: Texture.Usage,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const Id = enum(u16) {
|
||||||
|
empty_base_color = 0,
|
||||||
|
empty_emissive = 1,
|
||||||
|
empty_normal = 2,
|
||||||
|
empty_occlusion_roughness_metallic = 3,
|
||||||
|
_,
|
||||||
|
|
||||||
|
pub fn next(self: Id) Id {
|
||||||
|
return @enumFromInt(@intFromEnum(self) + 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
pub const Map = std.AutoHashMapUnmanaged(Key, Id);
|
pub const Map = std.AutoHashMapUnmanaged(Key, Id);
|
||||||
pub const Array = std.ArrayList(Texture);
|
pub const Array = std.ArrayList(Texture);
|
||||||
|
|
||||||
pub fn init(allocator: std.mem.Allocator, engine: *Engine) !Textures {
|
pub fn init(engine: *Engine, allocator: std.mem.Allocator) !Textures {
|
||||||
var map: Map = .empty;
|
var map: Map = .empty;
|
||||||
errdefer map.deinit(allocator);
|
errdefer map.deinit(allocator);
|
||||||
|
try map.ensureTotalCapacity(allocator, capacity);
|
||||||
|
|
||||||
var array: Array = try .initCapacity(allocator, 4);
|
var array: Array = try .initCapacity(allocator, capacity);
|
||||||
errdefer {
|
errdefer {
|
||||||
for (array.items) |*texture| {
|
for (array.items) |*texture| {
|
||||||
texture.deinit(engine);
|
texture.deinit(engine);
|
||||||
@@ -40,79 +45,116 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine) !Textures {
|
|||||||
array.deinit(allocator);
|
array.deinit(allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
const empty_base_color: Id = .{ .id = @intCast(array.items.len) };
|
const empty_base_color_texture = try Texture.init(engine, .{
|
||||||
const empty_base_color_texture: Texture = try .init(engine, 1, 1, .base_color);
|
.width = 1,
|
||||||
array.appendAssumeCapacity(empty_base_color_texture);
|
.height = 1,
|
||||||
|
.usage = .base_color,
|
||||||
|
.target_queue = .graphics,
|
||||||
|
});
|
||||||
|
|
||||||
const empty_emissive: Id = .{ .id = @intCast(array.items.len) };
|
const empty_emissive_texture = try Texture.init(engine, .{
|
||||||
const empty_emissive_texture: Texture = try .init(engine, 1, 1, .emissive);
|
.width = 1,
|
||||||
array.appendAssumeCapacity(empty_emissive_texture);
|
.height = 1,
|
||||||
|
.usage = .emissive,
|
||||||
|
.target_queue = .graphics,
|
||||||
|
});
|
||||||
|
|
||||||
const empty_normal: Id = .{ .id = @intCast(array.items.len) };
|
const empty_normal_texture = try Texture.init(engine, .{
|
||||||
const empty_normal_texture: Texture = try .init(engine, 1, 1, .normal);
|
.width = 1,
|
||||||
array.appendAssumeCapacity(empty_normal_texture);
|
.height = 1,
|
||||||
|
.usage = .normal,
|
||||||
|
.target_queue = .graphics,
|
||||||
|
});
|
||||||
|
|
||||||
const empty_occlusuion_roughness_metallic: Id = .{ .id = @intCast(array.items.len) };
|
const empty_occlusuion_roughness_metallic_texture = try Texture.init(engine, .{
|
||||||
const empty_occlusuion_roughness_metallic_texture: Texture = try .init(engine, 1, 1, .occlusion_roughness_metallic);
|
.width = 1,
|
||||||
array.appendAssumeCapacity(empty_occlusuion_roughness_metallic_texture);
|
.height = 1,
|
||||||
|
.usage = .occlusion_roughness_metallic,
|
||||||
|
.target_queue = .graphics,
|
||||||
|
});
|
||||||
|
|
||||||
try empty_base_color_texture.write([4]u8, engine, &.{.{ 255, 255, 255, 255 }});
|
array.appendSliceAssumeCapacity(&.{
|
||||||
try empty_emissive_texture.write([4]f16, engine, &.{.{ 1.0, 1.0, 1.0, 1.0 }});
|
empty_base_color_texture,
|
||||||
try empty_normal_texture.write([4]i8, engine, &.{.{ 0, 0, 127, 127 }});
|
empty_emissive_texture,
|
||||||
try empty_occlusuion_roughness_metallic_texture.write([4]u8, engine, &.{.{ 255, 255, 255, 255 }});
|
empty_normal_texture,
|
||||||
|
empty_occlusuion_roughness_metallic_texture,
|
||||||
|
});
|
||||||
|
|
||||||
|
try empty_base_color_texture.writeSamples(u8, engine, &.{ 255, 255, 255, 255 });
|
||||||
|
try empty_emissive_texture.writeSamples(f16, engine, &.{ 1.0, 1.0, 1.0, 1.0 });
|
||||||
|
try empty_normal_texture.writeSamples(i8, engine, &.{ 0, 0, 127, 127 });
|
||||||
|
try empty_occlusuion_roughness_metallic_texture.writeSamples(u8, engine, &.{ 255, 255, 255, 255 });
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.allocator = allocator,
|
|
||||||
.map = map,
|
.map = map,
|
||||||
.array = array,
|
.textures = array,
|
||||||
|
|
||||||
.empty_base_color = empty_base_color,
|
|
||||||
.empty_emissive = empty_emissive,
|
|
||||||
.empty_normal = empty_normal,
|
|
||||||
.empty_occlusuion_roughness_metallic = empty_occlusuion_roughness_metallic,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Textures, engine: *Engine) void {
|
pub fn deinit(self: *Textures, engine: *Engine, allocator: std.mem.Allocator) void {
|
||||||
for (self.array.items) |*texture| {
|
for (self.textures.items) |*texture| {
|
||||||
texture.deinit(engine);
|
texture.deinit(engine);
|
||||||
}
|
}
|
||||||
self.array.deinit(self.allocator);
|
self.textures.deinit(allocator);
|
||||||
|
self.map.deinit(allocator);
|
||||||
|
self.* = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
self.map.deinit(self.allocator);
|
pub fn getAtom(self: *const Textures, atom: atoms.Atom, usage: Texture.Usage) ?Id {
|
||||||
|
const key: Key = .{ .atom = atom, .usage = usage };
|
||||||
|
return self.map.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getFilename(self: *const Textures, filename: []const u8, usage: Texture.Usage) ?Id {
|
||||||
|
const atom = atoms.getAtom(filename) orelse return null;
|
||||||
|
const key: Key = .{ .atom = atom, .usage = usage };
|
||||||
|
return self.map.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getTexture(self: *const Textures, id: Id) ?*Texture {
|
pub fn getTexture(self: *const Textures, id: Id) ?*Texture {
|
||||||
const index: usize = id.id;
|
const index: usize = id.id;
|
||||||
return if (index < self.array.items.len) &self.array.items[index] else null;
|
return if (index < self.textures.items.len) &self.textures.items[index] else null;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getId(self: *const Textures, key: Key) ?Id {
|
pub fn getId(self: *const Textures, key: Key) ?Id {
|
||||||
return self.map.get(key);
|
return self.map.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getOrLoadId(self: *Textures, engine: *Engine, atoms: *Atoms, key: Key) !Id {
|
pub fn getOrLoadAtom(self: *Textures, engine: *Engine, atom: atoms.Atom, usage: Texture.Usage, temp_allocator: std.mem.Allocator) !Id {
|
||||||
const entry = try self.map.getOrPut(self.allocator, key);
|
const key: Key = .{ .atom = atom, .usage = usage };
|
||||||
|
const entry = self.map.getOrPutAssumeCapacity(key);
|
||||||
|
|
||||||
if (entry.found_existing) {
|
if (entry.found_existing) {
|
||||||
return entry.value_ptr.*;
|
return entry.value_ptr.*;
|
||||||
} else {
|
} else {
|
||||||
errdefer _ = self.map.remove(key);
|
errdefer _ = self.map.remove(key);
|
||||||
try self.array.ensureUnusedCapacity(self.allocator, 1);
|
const texture = try self.loadTexture(engine, atoms.getString(atom), usage, temp_allocator);
|
||||||
const texture = try self.loadTexture(engine, atoms, key);
|
|
||||||
|
|
||||||
const id = self.nextId();
|
const id = self.nextId();
|
||||||
|
|
||||||
entry.value_ptr.* = id;
|
entry.value_ptr.* = id;
|
||||||
self.array.appendAssumeCapacity(texture);
|
self.textures.appendAssumeCapacity(texture);
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn loadTexture(self: *Textures, engine: *Engine, atoms: *Atoms, key: Key) !Texture {
|
pub fn getOrLoadFilename(self: *Textures, engine: *Engine, filename: []const u8, usage: Texture.Usage, temp_allocator: std.mem.Allocator) !Id {
|
||||||
const filename = atoms.getString(key.atom).?;
|
const atom = try atoms.getOrPutAtom(filename);
|
||||||
std.log.debug("Loading texture \"{s}\" as {s}...", .{ filename, @tagName(key.usage) });
|
const key: Key = .{ .atom = atom, .usage = usage };
|
||||||
|
const entry = self.map.getOrPutAssumeCapacity(key);
|
||||||
|
|
||||||
|
if (entry.found_existing) {
|
||||||
|
return entry.value_ptr.*;
|
||||||
|
} else {
|
||||||
|
errdefer _ = self.map.remove(key);
|
||||||
|
const texture = try loadTexture(engine, filename, usage, temp_allocator);
|
||||||
|
const id = self.nextId();
|
||||||
|
entry.value_ptr.* = id;
|
||||||
|
self.textures.appendAssumeCapacity(texture);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn loadTexture(engine: *Engine, filename: []const u8, usage: Texture.Usage, temp_allocator: std.mem.Allocator) !Texture {
|
||||||
|
std.log.debug("Loading texture \"{s}\" as {s}...", .{ filename, @tagName(usage) });
|
||||||
|
|
||||||
const cwd = std.fs.cwd();
|
const cwd = std.fs.cwd();
|
||||||
|
|
||||||
@@ -122,22 +164,26 @@ fn loadTexture(self: *Textures, engine: *Engine, atoms: *Atoms, key: Key) !Textu
|
|||||||
const file = try dir.openFile(filename, .{});
|
const file = try dir.openFile(filename, .{});
|
||||||
defer file.close();
|
defer file.close();
|
||||||
|
|
||||||
const file_buf = try file.readToEndAlloc(self.allocator, std.math.maxInt(usize));
|
const file_buf = try file.readToEndAlloc(temp_allocator, std.math.maxInt(usize));
|
||||||
defer self.allocator.free(file_buf);
|
defer temp_allocator.free(file_buf);
|
||||||
|
|
||||||
var img = try stbi.Image.loadFromMemory(file_buf, key.usage.sampleCount());
|
var img = try stbi.Image.loadFromMemory(file_buf, usage.samplesPerTexel());
|
||||||
defer img.deinit();
|
defer img.deinit();
|
||||||
std.debug.assert(img.num_components == key.usage.sampleCount());
|
std.debug.assert(img.num_components == usage.samplesPerTexel());
|
||||||
|
|
||||||
var texture: Texture = try .init(engine, img.width, img.height, key.usage);
|
var texture = try Texture.init(engine, .{
|
||||||
|
.width = img.width,
|
||||||
|
.height = img.height,
|
||||||
|
.usage = usage,
|
||||||
|
.target_queue = .graphics,
|
||||||
|
});
|
||||||
errdefer texture.deinit(engine);
|
errdefer texture.deinit(engine);
|
||||||
|
|
||||||
try texture.write(u8, engine, img.data);
|
try texture.writeRaw(engine, img.data);
|
||||||
|
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn nextId(self: *const Textures) Id {
|
fn nextId(self: *const Textures) Id {
|
||||||
const index = self.array.items.len;
|
const index = self.textures.items.len;
|
||||||
return .{ .id = @intCast(index) };
|
return @enumFromInt(index);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -281,7 +281,17 @@ pub fn init(allocator: std.mem.Allocator, maybe_window: ?*glfw.Window) !Engine {
|
|||||||
try enabled_extensions.appendBounded(vk.extensions.khr_swapchain.name);
|
try enabled_extensions.appendBounded(vk.extensions.khr_swapchain.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const enabled_features_vulkan10: vk.PhysicalDeviceFeatures = .{
|
||||||
|
.shader_int_16 = .true,
|
||||||
|
};
|
||||||
|
|
||||||
|
var enabled_features_vulkan11: vk.PhysicalDeviceVulkan11Features = .{
|
||||||
|
.storage_buffer_16_bit_access = .true,
|
||||||
|
.uniform_and_storage_buffer_16_bit_access = .true,
|
||||||
|
};
|
||||||
|
|
||||||
var enabled_features_vulkan12: vk.PhysicalDeviceVulkan12Features = .{
|
var enabled_features_vulkan12: vk.PhysicalDeviceVulkan12Features = .{
|
||||||
|
.p_next = &enabled_features_vulkan11,
|
||||||
.descriptor_binding_partially_bound = .true,
|
.descriptor_binding_partially_bound = .true,
|
||||||
.descriptor_binding_variable_descriptor_count = .true,
|
.descriptor_binding_variable_descriptor_count = .true,
|
||||||
.runtime_descriptor_array = .true,
|
.runtime_descriptor_array = .true,
|
||||||
@@ -294,6 +304,7 @@ pub fn init(allocator: std.mem.Allocator, maybe_window: ?*glfw.Window) !Engine {
|
|||||||
.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,
|
||||||
|
.p_enabled_features = &enabled_features_vulkan10,
|
||||||
}, &vk_allocator.interface);
|
}, &vk_allocator.interface);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -561,3 +572,541 @@ fn findPresentationQueueFamily(queue_families_properties: []const vk.QueueFamily
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- VULKAN WRAPPERS ---------------------------------------------------------
|
||||||
|
|
||||||
|
pub const BufferCreateInfo = struct {
|
||||||
|
flags: vk.BufferCreateFlags = .{},
|
||||||
|
size: vk.DeviceSize,
|
||||||
|
usage: vk.BufferUsageFlags,
|
||||||
|
queue_family_indices: []const u32 = &.{},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const DescriptorPoolCreateInfo = struct {
|
||||||
|
flags: vk.DescriptorPoolCreateFlags = .{},
|
||||||
|
max_sets: u32,
|
||||||
|
pool_sizes: []const vk.DescriptorPoolSize = &.{},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const DescriptorSetAllocateInfo = struct {
|
||||||
|
descriptor_pool: vk.DescriptorPool,
|
||||||
|
set_layouts: []const vk.DescriptorSetLayout,
|
||||||
|
variable_descriptor_counts: []const u32 = &.{},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const DescriptorSetLayoutBinding = struct {
|
||||||
|
binding: u32,
|
||||||
|
descriptor_type: vk.DescriptorType,
|
||||||
|
descriptor_count: u32,
|
||||||
|
stage_flags: vk.ShaderStageFlags,
|
||||||
|
immutable_samplers: []const vk.Sampler = &.{},
|
||||||
|
flags: vk.DescriptorBindingFlags = .{},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const DescriptorSetLayoutCreateInfo = struct {
|
||||||
|
flags: vk.DescriptorSetLayoutCreateFlags = .{},
|
||||||
|
bindings: []const DescriptorSetLayoutBinding = &.{},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const DescriptorSetsUpdateInfo = struct {
|
||||||
|
writes: []const WriteDescriptorSet = &.{},
|
||||||
|
copies: []const vk.CopyDescriptorSet = &.{},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const FramebufferCreateInfo = struct {
|
||||||
|
flags: vk.FramebufferCreateFlags = .{},
|
||||||
|
render_pass: vk.RenderPass,
|
||||||
|
attachments: []const vk.ImageView = &.{},
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
layers: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const GraphicsPipelineCreateInfo = struct {
|
||||||
|
flags: vk.PipelineCreateFlags = .{},
|
||||||
|
stages: []const PipelineShaderStageCreateInfo = &.{},
|
||||||
|
vertex_input_state: ?PipelineVertexInputStateCreateInfo = null,
|
||||||
|
input_assembly_state: ?vk.PipelineInputAssemblyStateCreateInfo = null,
|
||||||
|
tessellation_state: ?vk.PipelineTessellationStateCreateInfo = null,
|
||||||
|
viewport_state: ?PipelineViewportStateCreateInfo = null,
|
||||||
|
rasterization_state: ?vk.PipelineRasterizationStateCreateInfo = null,
|
||||||
|
multisample_state: ?vk.PipelineMultisampleStateCreateInfo = null,
|
||||||
|
depth_stencil_state: ?vk.PipelineDepthStencilStateCreateInfo = null,
|
||||||
|
color_blend_state: ?PipelineColorBlendStateCreateInfo = null,
|
||||||
|
dynamic_state: ?PipelineDynamicStateCreateInfo = null,
|
||||||
|
layout: vk.PipelineLayout = .null_handle,
|
||||||
|
render_pass: vk.RenderPass = .null_handle,
|
||||||
|
subpass: u32,
|
||||||
|
base_pipeline_handle: vk.Pipeline = .null_handle,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const ImageCreateInfo = struct {
|
||||||
|
flags: vk.ImageCreateFlags = .{},
|
||||||
|
image_type: vk.ImageType,
|
||||||
|
format: vk.Format,
|
||||||
|
extent: vk.Extent3D,
|
||||||
|
mip_levels: u32,
|
||||||
|
array_layers: u32,
|
||||||
|
samples: vk.SampleCountFlags,
|
||||||
|
tiling: vk.ImageTiling,
|
||||||
|
usage: vk.ImageUsageFlags,
|
||||||
|
queue_family_indices: []const u32 = &.{},
|
||||||
|
initial_layout: vk.ImageLayout,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const ImageViewCreateInfo = struct {
|
||||||
|
flags: vk.ImageViewCreateFlags = .{},
|
||||||
|
image: vk.Image,
|
||||||
|
view_type: vk.ImageViewType,
|
||||||
|
format: vk.Format,
|
||||||
|
components: vk.ComponentMapping = .{
|
||||||
|
.r = .identity,
|
||||||
|
.g = .identity,
|
||||||
|
.b = .identity,
|
||||||
|
.a = .identity,
|
||||||
|
},
|
||||||
|
subresource_range: vk.ImageSubresourceRange,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const PipelineColorBlendStateCreateInfo = struct {
|
||||||
|
flags: vk.PipelineColorBlendStateCreateFlags = .{},
|
||||||
|
logic_op_enable: vk.Bool32,
|
||||||
|
logic_op: vk.LogicOp,
|
||||||
|
attachments: []const vk.PipelineColorBlendAttachmentState = &.{},
|
||||||
|
blend_constants: [4]f32,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const PipelineDynamicStateCreateInfo = struct {
|
||||||
|
flags: vk.PipelineDynamicStateCreateFlags = .{},
|
||||||
|
dynamic_states: []const vk.DynamicState = &.{},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const PipelineLayoutCreateInfo = struct {
|
||||||
|
flags: vk.PipelineLayoutCreateFlags = .{},
|
||||||
|
set_layouts: []const vk.DescriptorSetLayout = &.{},
|
||||||
|
push_constant_ranges: []const vk.PushConstantRange = &.{},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const PipelineShaderStageCreateInfo = struct {
|
||||||
|
flags: vk.PipelineShaderStageCreateFlags = .{},
|
||||||
|
stage: vk.ShaderStageFlags,
|
||||||
|
module: vk.ShaderModule = .null_handle,
|
||||||
|
name: [*:0]const u8,
|
||||||
|
specialization_info: ?SpecializationInfo = null,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const PipelineVertexInputStateCreateInfo = struct {
|
||||||
|
flags: vk.PipelineVertexInputStateCreateFlags = .{},
|
||||||
|
vertex_binding_descriptions: []const vk.VertexInputBindingDescription = &.{},
|
||||||
|
vertex_attribute_descriptions: []const vk.VertexInputAttributeDescription = &.{},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const PipelineViewportStateCreateInfo = struct {
|
||||||
|
flags: vk.PipelineViewportStateCreateFlags = .{},
|
||||||
|
viewports: []const vk.Viewport = &.{},
|
||||||
|
scissors: []const vk.Rect2D = &.{},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const ShaderModuleCreateInfo = struct {
|
||||||
|
flags: vk.ShaderModuleCreateFlags = .{},
|
||||||
|
code: []align(@alignOf(u32)) const u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const SpecializationInfo = struct {
|
||||||
|
map_entries: []vk.SpecializationMapEntry = &.{},
|
||||||
|
data: []const u8 = &.{},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const WriteDescriptorSet = struct {
|
||||||
|
dst_set: vk.DescriptorSet,
|
||||||
|
dst_binding: u32,
|
||||||
|
dst_array_element: u32,
|
||||||
|
descriptor_type: vk.DescriptorType,
|
||||||
|
descriptor_infos: union(enum) {
|
||||||
|
image: []const vk.DescriptorImageInfo,
|
||||||
|
buffer: []const vk.DescriptorBufferInfo,
|
||||||
|
texel_buffer_view: []const vk.BufferView,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn createBuffer(self: *Engine, create_info: BufferCreateInfo) !vk.Buffer {
|
||||||
|
var arena: std.heap.ArenaAllocator = .init(self.vk_allocator.allocator);
|
||||||
|
defer arena.deinit();
|
||||||
|
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 buffer = self.device.createBuffer(&.{
|
||||||
|
.flags = create_info.flags,
|
||||||
|
.size = create_info.size,
|
||||||
|
.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,
|
||||||
|
}, &self.vk_allocator.interface);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn allocateDescriptorSets(self: *Engine, allocate_info: DescriptorSetAllocateInfo, descriptor_sets: []vk.DescriptorSet) !void {
|
||||||
|
std.debug.assert(descriptor_sets.len >= allocate_info.set_layouts.len);
|
||||||
|
|
||||||
|
const has_variable_descriptor_counts = allocate_info.variable_descriptor_counts.len > 0;
|
||||||
|
|
||||||
|
var p_next: ?*const anyopaque = null;
|
||||||
|
|
||||||
|
if (has_variable_descriptor_counts) {
|
||||||
|
p_next = &vk.DescriptorSetVariableDescriptorCountAllocateInfo{
|
||||||
|
.p_next = p_next,
|
||||||
|
.descriptor_set_count = @intCast(allocate_info.variable_descriptor_counts.len),
|
||||||
|
.p_descriptor_counts = allocate_info.variable_descriptor_counts.ptr,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
try self.device.allocateDescriptorSets(&.{
|
||||||
|
.p_next = p_next,
|
||||||
|
.descriptor_pool = allocate_info.descriptor_pool,
|
||||||
|
.descriptor_set_count = @intCast(allocate_info.set_layouts.len),
|
||||||
|
.p_set_layouts = allocate_info.set_layouts.ptr,
|
||||||
|
}, descriptor_sets.ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn createDescriptorSetLayout(self: *Engine, create_info: DescriptorSetLayoutCreateInfo) !vk.DescriptorSetLayout {
|
||||||
|
var arena: std.heap.ArenaAllocator = .init(self.vk_allocator.allocator);
|
||||||
|
defer arena.deinit();
|
||||||
|
const allocator = arena.allocator();
|
||||||
|
|
||||||
|
const has_binding_flags = blk: {
|
||||||
|
for (create_info.bindings) |binding| {
|
||||||
|
if (@as(vk.Flags, @bitCast(binding.flags)) != 0) {
|
||||||
|
break :blk true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break :blk false;
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
for (descriptor_set_layout_bindings_flags, create_info.bindings) |*x, binding| {
|
||||||
|
x.* = binding.flags;
|
||||||
|
}
|
||||||
|
p_next = &vk.DescriptorSetLayoutBindingFlagsCreateInfo{
|
||||||
|
.p_next = p_next,
|
||||||
|
.binding_count = @intCast(descriptor_set_layout_bindings_flags.len),
|
||||||
|
.p_binding_flags = descriptor_set_layout_bindings_flags.ptr,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const bindings = try allocator.alloc(vk.DescriptorSetLayoutBinding, create_info.bindings.len);
|
||||||
|
for (bindings, create_info.bindings) |*x, binding| {
|
||||||
|
x.* = .{
|
||||||
|
.binding = binding.binding,
|
||||||
|
.descriptor_type = binding.descriptor_type,
|
||||||
|
.descriptor_count = binding.descriptor_count,
|
||||||
|
.stage_flags = binding.stage_flags,
|
||||||
|
.p_immutable_samplers = binding.immutable_samplers.ptr,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const descriptor_set_layout = try self.device.createDescriptorSetLayout(&.{
|
||||||
|
.p_next = p_next,
|
||||||
|
.flags = create_info.flags,
|
||||||
|
.binding_count = @intCast(bindings.len),
|
||||||
|
.p_bindings = bindings.ptr,
|
||||||
|
}, &self.vk_allocator.interface);
|
||||||
|
return descriptor_set_layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn createDescriptorPool(self: *Engine, create_info: DescriptorPoolCreateInfo) !vk.DescriptorPool {
|
||||||
|
const descriptor_pool = try self.device.createDescriptorPool(&.{
|
||||||
|
.flags = create_info.flags,
|
||||||
|
.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);
|
||||||
|
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);
|
||||||
|
return fence;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn createFramebuffer(self: *Engine, create_info: FramebufferCreateInfo) !vk.Framebuffer {
|
||||||
|
const framebuffer = try self.device.createFramebuffer(&.{
|
||||||
|
.flags = create_info.flags,
|
||||||
|
.render_pass = create_info.render_pass,
|
||||||
|
.attachment_count = @intCast(create_info.attachments.len),
|
||||||
|
.p_attachments = create_info.attachments.ptr,
|
||||||
|
.width = create_info.width,
|
||||||
|
.height = create_info.height,
|
||||||
|
.layers = create_info.layers,
|
||||||
|
}, &self.vk_allocator.interface);
|
||||||
|
return framebuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn createGraphicsPipeline(self: *Engine, create_info: GraphicsPipelineCreateInfo) !vk.Pipeline {
|
||||||
|
var arena: std.heap.ArenaAllocator = .init(self.vk_allocator.allocator);
|
||||||
|
defer arena.deinit();
|
||||||
|
const allocator = arena.allocator();
|
||||||
|
|
||||||
|
const stages = try allocator.alloc(vk.PipelineShaderStageCreateInfo, create_info.stages.len);
|
||||||
|
for (stages, create_info.stages) |*x, *stage| {
|
||||||
|
x.* = .{
|
||||||
|
.flags = stage.flags,
|
||||||
|
.stage = stage.stage,
|
||||||
|
.p_name = stage.name,
|
||||||
|
.module = stage.module,
|
||||||
|
.p_specialization_info = blk: {
|
||||||
|
if (stage.specialization_info) |y| {
|
||||||
|
const specialization_info = try allocator.create(vk.SpecializationInfo);
|
||||||
|
specialization_info.* = .{
|
||||||
|
.map_entry_count = @intCast(y.map_entries.len),
|
||||||
|
.p_map_entries = y.map_entries.ptr,
|
||||||
|
.data_size = y.data.len,
|
||||||
|
.p_data = y.data.ptr,
|
||||||
|
};
|
||||||
|
break :blk specialization_info;
|
||||||
|
} else {
|
||||||
|
break :blk null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const graphics_pipeline_create_infos = [_]vk.GraphicsPipelineCreateInfo{
|
||||||
|
.{
|
||||||
|
.flags = create_info.flags,
|
||||||
|
.stage_count = @intCast(stages.len),
|
||||||
|
.p_stages = stages.ptr,
|
||||||
|
.p_vertex_input_state = if (create_info.vertex_input_state) |vertex_input_state| &.{
|
||||||
|
.flags = vertex_input_state.flags,
|
||||||
|
.vertex_binding_description_count = @intCast(vertex_input_state.vertex_binding_descriptions.len),
|
||||||
|
.p_vertex_binding_descriptions = vertex_input_state.vertex_binding_descriptions.ptr,
|
||||||
|
.vertex_attribute_description_count = @intCast(vertex_input_state.vertex_attribute_descriptions.len),
|
||||||
|
.p_vertex_attribute_descriptions = vertex_input_state.vertex_attribute_descriptions.ptr,
|
||||||
|
} else null,
|
||||||
|
.p_input_assembly_state = if (create_info.input_assembly_state) |*x| x else null,
|
||||||
|
.p_tessellation_state = if (create_info.tessellation_state) |*x| x else null,
|
||||||
|
.p_viewport_state = if (create_info.viewport_state) |viewport_state| &.{
|
||||||
|
.flags = viewport_state.flags,
|
||||||
|
.viewport_count = @intCast(viewport_state.viewports.len),
|
||||||
|
.p_viewports = viewport_state.viewports.ptr,
|
||||||
|
.scissor_count = @intCast(viewport_state.scissors.len),
|
||||||
|
.p_scissors = viewport_state.scissors.ptr,
|
||||||
|
} else null,
|
||||||
|
.p_rasterization_state = if (create_info.rasterization_state) |*x| x else null,
|
||||||
|
.p_multisample_state = if (create_info.multisample_state) |*x| x else null,
|
||||||
|
.p_depth_stencil_state = if (create_info.depth_stencil_state) |*x| x else null,
|
||||||
|
.p_color_blend_state = if (create_info.color_blend_state) |color_blend_state| &.{
|
||||||
|
.flags = color_blend_state.flags,
|
||||||
|
.logic_op_enable = color_blend_state.logic_op_enable,
|
||||||
|
.logic_op = color_blend_state.logic_op,
|
||||||
|
.attachment_count = @intCast(color_blend_state.attachments.len),
|
||||||
|
.p_attachments = color_blend_state.attachments.ptr,
|
||||||
|
.blend_constants = color_blend_state.blend_constants,
|
||||||
|
} else null,
|
||||||
|
.p_dynamic_state = if (create_info.dynamic_state) |dynamic_state| &.{
|
||||||
|
.flags = dynamic_state.flags,
|
||||||
|
.dynamic_state_count = @intCast(dynamic_state.dynamic_states.len),
|
||||||
|
.p_dynamic_states = dynamic_state.dynamic_states.ptr,
|
||||||
|
} else null,
|
||||||
|
.layout = create_info.layout,
|
||||||
|
.render_pass = create_info.render_pass,
|
||||||
|
.subpass = create_info.subpass,
|
||||||
|
.base_pipeline_handle = create_info.base_pipeline_handle,
|
||||||
|
.base_pipeline_index = -1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
var pipelines: [1]vk.Pipeline = undefined;
|
||||||
|
_ = try self.device.createGraphicsPipelines(.null_handle, graphics_pipeline_create_infos.len, &graphics_pipeline_create_infos, &self.vk_allocator.interface, &pipelines);
|
||||||
|
return pipelines[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn createImage(self: *Engine, create_info: ImageCreateInfo) !vk.Image {
|
||||||
|
var arena: std.heap.ArenaAllocator = .init(self.vk_allocator.allocator);
|
||||||
|
defer arena.deinit();
|
||||||
|
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 {
|
||||||
|
const image_view = try self.device.createImageView(&.{
|
||||||
|
.flags = create_info.flags,
|
||||||
|
.image = create_info.image,
|
||||||
|
.view_type = create_info.view_type,
|
||||||
|
.format = create_info.format,
|
||||||
|
.components = create_info.components,
|
||||||
|
.subresource_range = create_info.subresource_range,
|
||||||
|
}, &self.vk_allocator.interface);
|
||||||
|
return image_view;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn createPipelineLayout(self: *Engine, create_info: PipelineLayoutCreateInfo) !vk.PipelineLayout {
|
||||||
|
const pipeline_layout = try self.device.createPipelineLayout(&.{
|
||||||
|
.flags = create_info.flags,
|
||||||
|
.set_layout_count = @intCast(create_info.set_layouts.len),
|
||||||
|
.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);
|
||||||
|
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);
|
||||||
|
return sampler;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn createSemaphore(self: *Engine) !vk.Semaphore {
|
||||||
|
const semaphore = try self.device.createSemaphore(&.{}, &self.vk_allocator.interface);
|
||||||
|
return semaphore;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn createShaderModule(self: *Engine, create_info: ShaderModuleCreateInfo) !vk.ShaderModule {
|
||||||
|
const shader_module = try self.device.createShaderModule(&.{
|
||||||
|
.flags = create_info.flags,
|
||||||
|
.code_size = create_info.code.len,
|
||||||
|
.p_code = @ptrCast(create_info.code.ptr),
|
||||||
|
}, &self.vk_allocator.interface);
|
||||||
|
return shader_module;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroyBuffer(self: *Engine, buffer: vk.Buffer) void {
|
||||||
|
self.device.destroyBuffer(buffer, &self.vk_allocator.interface);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroyDescriptorPool(self: *Engine, descriptor_pool: vk.DescriptorPool) void {
|
||||||
|
self.device.destroyDescriptorPool(descriptor_pool, &self.vk_allocator.interface);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroyDescriptorSetLayout(self: *Engine, descriptor_set_layout: vk.DescriptorSetLayout) void {
|
||||||
|
self.device.destroyDescriptorSetLayout(descriptor_set_layout, &self.vk_allocator.interface);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroyFence(self: *Engine, fence: vk.Fence) void {
|
||||||
|
self.device.destroyFence(fence, &self.vk_allocator.interface);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroyFramebuffer(self: *Engine, framebuffer: vk.Framebuffer) void {
|
||||||
|
self.device.destroyFramebuffer(framebuffer, &self.vk_allocator.interface);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroyImage(self: *Engine, image: vk.Image) void {
|
||||||
|
self.device.destroyImage(image, &self.vk_allocator.interface);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroyImageView(self: *Engine, image_view: vk.ImageView) void {
|
||||||
|
self.device.destroyImageView(image_view, &self.vk_allocator.interface);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroyPipeline(self: *Engine, pipeline: vk.Pipeline) void {
|
||||||
|
self.device.destroyPipeline(pipeline, &self.vk_allocator.interface);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroyPipelineLayout(self: *Engine, pipeline_layout: vk.PipelineLayout) void {
|
||||||
|
self.device.destroyPipelineLayout(pipeline_layout, &self.vk_allocator.interface);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroySampler(self: *Engine, sampler: vk.Sampler) void {
|
||||||
|
self.device.destroySampler(sampler, &self.vk_allocator.interface);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroySemaphore(self: *Engine, semaphore: vk.Semaphore) void {
|
||||||
|
self.device.destroySemaphore(semaphore, &self.vk_allocator.interface);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroyShaderModule(self: *Engine, shader_module: vk.ShaderModule) void {
|
||||||
|
self.device.destroyShaderModule(shader_module, &self.vk_allocator.interface);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn freeMemory(self: *Engine, device_memory: vk.DeviceMemory) void {
|
||||||
|
self.device.freeMemory(device_memory, &self.vk_allocator.interface);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resetFence(self: *Engine, fence: vk.Fence) !void {
|
||||||
|
try self.device.resetFences(1, @ptrCast(&fence));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn updateDescriptorSets(self: *Engine, update_info: DescriptorSetsUpdateInfo) !void {
|
||||||
|
var arena: std.heap.ArenaAllocator = .init(self.vk_allocator.allocator);
|
||||||
|
defer arena.deinit();
|
||||||
|
const allocator = arena.allocator();
|
||||||
|
|
||||||
|
const descriptor_writes = try allocator.alloc(vk.WriteDescriptorSet, update_info.writes.len);
|
||||||
|
for (descriptor_writes, update_info.writes) |*x, write| {
|
||||||
|
x.* = switch (write.descriptor_infos) {
|
||||||
|
.image => |y| .{
|
||||||
|
.dst_set = write.dst_set,
|
||||||
|
.dst_binding = write.dst_binding,
|
||||||
|
.dst_array_element = write.dst_array_element,
|
||||||
|
.descriptor_count = @intCast(y.len),
|
||||||
|
.descriptor_type = write.descriptor_type,
|
||||||
|
.p_image_info = y.ptr,
|
||||||
|
.p_buffer_info = &.{},
|
||||||
|
.p_texel_buffer_view = &.{},
|
||||||
|
},
|
||||||
|
.buffer => |y| .{
|
||||||
|
.dst_set = write.dst_set,
|
||||||
|
.dst_binding = write.dst_binding,
|
||||||
|
.dst_array_element = write.dst_array_element,
|
||||||
|
.descriptor_count = @intCast(y.len),
|
||||||
|
.descriptor_type = write.descriptor_type,
|
||||||
|
.p_image_info = &.{},
|
||||||
|
.p_buffer_info = y.ptr,
|
||||||
|
.p_texel_buffer_view = &.{},
|
||||||
|
},
|
||||||
|
.texel_buffer_view => |y| .{
|
||||||
|
.dst_set = write.dst_set,
|
||||||
|
.dst_binding = write.dst_binding,
|
||||||
|
.dst_array_element = write.dst_array_element,
|
||||||
|
.descriptor_count = @intCast(y.len),
|
||||||
|
.descriptor_type = write.descriptor_type,
|
||||||
|
.p_image_info = &.{},
|
||||||
|
.p_buffer_info = &.{},
|
||||||
|
.p_texel_buffer_view = y.ptr,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
self.device.updateDescriptorSets(
|
||||||
|
@intCast(descriptor_writes.len),
|
||||||
|
descriptor_writes.ptr,
|
||||||
|
@intCast(update_info.copies.len),
|
||||||
|
update_info.copies.ptr,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn waitForFence(self: *Engine, fence: vk.Fence) !void {
|
||||||
|
_ = try self.device.waitForFences(1, @ptrCast(&fence), .true, std.math.maxInt(u64));
|
||||||
|
}
|
||||||
|
|||||||
210
src/engine/GenericBuffer.zig
Normal file
210
src/engine/GenericBuffer.zig
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const vk = @import("vulkan");
|
||||||
|
|
||||||
|
const Engine = @import("Engine.zig");
|
||||||
|
const StagingBuffer = @import("StagingBuffer.zig");
|
||||||
|
const TargetQueue = @import("TargetQueue.zig").TargetQueue;
|
||||||
|
|
||||||
|
pub const Usage = enum {
|
||||||
|
uniform,
|
||||||
|
storage,
|
||||||
|
index,
|
||||||
|
vertex,
|
||||||
|
indirect,
|
||||||
|
|
||||||
|
pub fn asBufferUsageFlags(self: Usage) vk.BufferUsageFlags {
|
||||||
|
return switch (self) {
|
||||||
|
.uniform => .{ .transfer_dst_bit = true, .uniform_buffer_bit = true },
|
||||||
|
.storage => .{ .transfer_dst_bit = true, .storage_buffer_bit = true },
|
||||||
|
.index => .{ .transfer_dst_bit = true, .index_buffer_bit = true },
|
||||||
|
.vertex => .{ .transfer_dst_bit = true, .vertex_buffer_bit = true },
|
||||||
|
.indirect => .{ .transfer_dst_bit = true, .indirect_buffer_bit = true },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const InitInfo = struct {
|
||||||
|
usage: Usage,
|
||||||
|
target_queue: TargetQueue,
|
||||||
|
array_capacity: u32 = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn GenericBuffer(comptime Header: type, comptime Element: type) type {
|
||||||
|
return struct {
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
buffer: vk.Buffer,
|
||||||
|
device_memory: vk.DeviceMemory,
|
||||||
|
target_queue: TargetQueue,
|
||||||
|
array_capacity: u32,
|
||||||
|
|
||||||
|
const header_size: u32 = @sizeOf(Header);
|
||||||
|
const element_size: u32 = @sizeOf(Element);
|
||||||
|
const array_offset: u32 = std.mem.alignForward(usize, header_size, @alignOf(Element));
|
||||||
|
|
||||||
|
pub const WriteInfo = struct {
|
||||||
|
header: ?Header = null,
|
||||||
|
element_offset: u32 = 0,
|
||||||
|
elements: []const Element = &.{},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn init(engine: *Engine, init_info: InitInfo) !Self {
|
||||||
|
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 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 buffer = try engine.createBuffer(.{
|
||||||
|
.size = size,
|
||||||
|
.usage = init_info.usage.asBufferUsageFlags(),
|
||||||
|
.queue_family_indices = &.{ target_queue_family, transfer_queue_family },
|
||||||
|
});
|
||||||
|
errdefer engine.destroyBuffer(buffer);
|
||||||
|
|
||||||
|
const memory_requirements = engine.device.getBufferMemoryRequirements(buffer);
|
||||||
|
const device_memory = try engine.allocate(memory_requirements, .{ .device_local_bit = true });
|
||||||
|
errdefer engine.freeMemory(device_memory);
|
||||||
|
|
||||||
|
try engine.device.bindBufferMemory(buffer, device_memory, 0);
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.buffer = buffer,
|
||||||
|
.device_memory = device_memory,
|
||||||
|
.target_queue = init_info.target_queue,
|
||||||
|
.array_capacity = init_info.array_capacity,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *Self, engine: *Engine) void {
|
||||||
|
engine.freeMemory(self.device_memory);
|
||||||
|
engine.destroyBuffer(self.buffer);
|
||||||
|
|
||||||
|
self.* = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(self: Self, engine: *Engine, write_info: WriteInfo) !void {
|
||||||
|
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);
|
||||||
|
|
||||||
|
const header_write_size: u32 = if (write_info.header != null) header_size else 0;
|
||||||
|
const array_write_size = element_count * element_size;
|
||||||
|
const array_write_offset: u32 = if (header_write_size > 0) array_offset else 0;
|
||||||
|
const write_size = array_write_offset + array_write_size;
|
||||||
|
|
||||||
|
if (write_size == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var regions_buffer: [2]vk.BufferCopy = undefined;
|
||||||
|
var regions: std.ArrayList(vk.BufferCopy) = .initBuffer(®ions_buffer);
|
||||||
|
|
||||||
|
// One continuous write
|
||||||
|
if (header_write_size > 0 and array_write_size > 0 and write_info.element_offset == 0) {
|
||||||
|
regions.appendAssumeCapacity(.{
|
||||||
|
.src_offset = 0,
|
||||||
|
.dst_offset = 0,
|
||||||
|
.size = write_size,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Separate writes for header and array (if they exist)
|
||||||
|
else {
|
||||||
|
if (header_write_size > 0) {
|
||||||
|
regions.appendAssumeCapacity(.{
|
||||||
|
.src_offset = 0,
|
||||||
|
.dst_offset = 0,
|
||||||
|
.size = header_write_size,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (array_write_size > 0) {
|
||||||
|
regions.appendAssumeCapacity(.{
|
||||||
|
.src_offset = array_write_offset,
|
||||||
|
.dst_offset = array_offset + write_info.element_offset * element_size,
|
||||||
|
.size = array_write_size,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
std.debug.assert(regions.items.len > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
var staging_buffer: StagingBuffer = try .init(engine, .{
|
||||||
|
.target_queue = self.target_queue,
|
||||||
|
.capacity = write_size,
|
||||||
|
});
|
||||||
|
defer staging_buffer.deinit(engine);
|
||||||
|
|
||||||
|
const staging_memory = try staging_buffer.map(engine);
|
||||||
|
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);
|
||||||
|
|
||||||
|
const command_buffer = try engine.allocateTransferCommandBuffer();
|
||||||
|
defer engine.freeTransferCommandBuffer(command_buffer);
|
||||||
|
|
||||||
|
try command_buffer.beginCommandBuffer(&.{ .flags = .{ .one_time_submit_bit = true } });
|
||||||
|
command_buffer.copyBuffer(
|
||||||
|
staging_buffer.buffer,
|
||||||
|
self.buffer,
|
||||||
|
@intCast(regions.items.len),
|
||||||
|
regions.items.ptr,
|
||||||
|
);
|
||||||
|
try command_buffer.endCommandBuffer();
|
||||||
|
|
||||||
|
const fence = try engine.createFence(.{});
|
||||||
|
defer engine.destroyFence(fence);
|
||||||
|
|
||||||
|
try engine.submitTransferCommandBuffer(command_buffer, fence);
|
||||||
|
try engine.waitForFence(fence);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn writeRaw(self: Self, engine: *Engine, data_offset: u32, data: []const u8) !void {
|
||||||
|
const array_size = self.array_capacity * element_size;
|
||||||
|
const size = array_offset + array_size;
|
||||||
|
|
||||||
|
const data_size = std.math.cast(u32, data.len) orelse return error.Overflow;
|
||||||
|
std.debug.assert(data_offset + data_size <= size);
|
||||||
|
|
||||||
|
const regions = [_]vk.BufferCopy{
|
||||||
|
.{
|
||||||
|
.src_offset = 0,
|
||||||
|
.dst_offset = data_offset,
|
||||||
|
.size = data_size,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
var staging_buffer = try StagingBuffer.init(engine, .{
|
||||||
|
.target_queue = self.target_queue,
|
||||||
|
.capacity = data_size,
|
||||||
|
});
|
||||||
|
defer staging_buffer.deinit(engine);
|
||||||
|
|
||||||
|
const staging_memory = try staging_buffer.map(engine);
|
||||||
|
@memcpy(staging_memory, data);
|
||||||
|
staging_buffer.unmap(engine);
|
||||||
|
|
||||||
|
const command_buffer = try engine.allocateTransferCommandBuffer();
|
||||||
|
defer engine.freeTransferCommandBuffer(command_buffer);
|
||||||
|
|
||||||
|
try command_buffer.beginCommandBuffer(&.{ .flags = .{ .one_time_submit_bit = true } });
|
||||||
|
command_buffer.copyBuffer(
|
||||||
|
staging_buffer.buffer,
|
||||||
|
self.buffer,
|
||||||
|
@intCast(regions.items.len),
|
||||||
|
regions.items.ptr,
|
||||||
|
);
|
||||||
|
try command_buffer.endCommandBuffer();
|
||||||
|
|
||||||
|
const fence = try engine.createFence(.{});
|
||||||
|
defer engine.destroyFence(fence);
|
||||||
|
|
||||||
|
try engine.submitTransferCommandBuffer(command_buffer, fence);
|
||||||
|
try engine.waitForFence(fence);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
const IndexBuffer = @This();
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const vk = @import("vulkan");
|
|
||||||
|
|
||||||
const Engine = @import("Engine.zig");
|
|
||||||
const StagingBuffer = @import("StagingBuffer.zig");
|
|
||||||
const QSM = @import("QueueSharingMode.zig");
|
|
||||||
|
|
||||||
buffer: vk.Buffer,
|
|
||||||
memory: vk.DeviceMemory,
|
|
||||||
index_count: usize,
|
|
||||||
|
|
||||||
pub fn init(engine: *Engine, index_count: usize) !IndexBuffer {
|
|
||||||
const size = std.math.mul(usize, index_count, @sizeOf(u16)) catch return error.OutOfMemory;
|
|
||||||
|
|
||||||
const qsm = QSM.resolve(engine.graphics_queue.allocation.family, engine.transfer_queue.allocation.family);
|
|
||||||
const buffer = try engine.device.createBuffer(&.{
|
|
||||||
.size = size,
|
|
||||||
.usage = .{
|
|
||||||
.transfer_dst_bit = true,
|
|
||||||
.index_buffer_bit = true,
|
|
||||||
},
|
|
||||||
.sharing_mode = qsm.sharing_mode,
|
|
||||||
.queue_family_index_count = qsm.queue_family_index_count,
|
|
||||||
.p_queue_family_indices = qsm.p_queue_family_indices,
|
|
||||||
}, &engine.vk_allocator.interface);
|
|
||||||
errdefer engine.device.destroyBuffer(buffer, &engine.vk_allocator.interface);
|
|
||||||
|
|
||||||
const memory_requirements = engine.device.getBufferMemoryRequirements(buffer);
|
|
||||||
const memory = try engine.allocate(memory_requirements, .{ .device_local_bit = true });
|
|
||||||
errdefer engine.device.freeMemory(memory, &engine.vk_allocator.interface);
|
|
||||||
|
|
||||||
try engine.device.bindBufferMemory(buffer, memory, 0);
|
|
||||||
|
|
||||||
return .{
|
|
||||||
.buffer = buffer,
|
|
||||||
.memory = memory,
|
|
||||||
.index_count = index_count,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(self: *IndexBuffer, engine: *Engine) void {
|
|
||||||
engine.device.freeMemory(self.memory, &engine.vk_allocator.interface);
|
|
||||||
engine.device.destroyBuffer(self.buffer, &engine.vk_allocator.interface);
|
|
||||||
|
|
||||||
self.* = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write(self: IndexBuffer, engine: *Engine, indices: []const u16) !void {
|
|
||||||
std.debug.assert(indices.len == self.index_count);
|
|
||||||
|
|
||||||
const fence = try engine.device.createFence(&.{}, &engine.vk_allocator.interface);
|
|
||||||
defer engine.device.destroyFence(fence, &engine.vk_allocator.interface);
|
|
||||||
|
|
||||||
const size = std.mem.sliceAsBytes(indices).len;
|
|
||||||
|
|
||||||
var staging_buffer: StagingBuffer = try .init(engine, std.mem.sliceAsBytes(indices), engine.graphics_queue.allocation.family);
|
|
||||||
defer staging_buffer.deinit(engine);
|
|
||||||
|
|
||||||
const command_buffer = try engine.allocateTransferCommandBuffer();
|
|
||||||
defer engine.freeTransferCommandBuffer(command_buffer);
|
|
||||||
|
|
||||||
try command_buffer.beginCommandBuffer(&.{ .flags = .{ .one_time_submit_bit = true } });
|
|
||||||
|
|
||||||
const regions = [_]vk.BufferCopy{
|
|
||||||
.{
|
|
||||||
.src_offset = 0,
|
|
||||||
.dst_offset = 0,
|
|
||||||
.size = size,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
command_buffer.copyBuffer(
|
|
||||||
staging_buffer.buffer,
|
|
||||||
self.buffer,
|
|
||||||
regions.len,
|
|
||||||
®ions,
|
|
||||||
);
|
|
||||||
|
|
||||||
try command_buffer.endCommandBuffer();
|
|
||||||
try engine.submitTransferCommandBuffer(command_buffer, fence);
|
|
||||||
|
|
||||||
_ = try engine.device.waitForFences(1, @ptrCast(&fence), .true, std.math.maxInt(u64));
|
|
||||||
}
|
|
||||||
@@ -4,51 +4,69 @@ const std = @import("std");
|
|||||||
const vk = @import("vulkan");
|
const vk = @import("vulkan");
|
||||||
|
|
||||||
const Engine = @import("Engine.zig");
|
const Engine = @import("Engine.zig");
|
||||||
const QSM = @import("QueueSharingMode.zig");
|
const TargetQueue = @import("TargetQueue.zig").TargetQueue;
|
||||||
|
|
||||||
buffer: vk.Buffer,
|
buffer: vk.Buffer,
|
||||||
memory: vk.DeviceMemory,
|
device_memory: vk.DeviceMemory,
|
||||||
|
capacity: u32,
|
||||||
|
|
||||||
pub fn init(engine: *Engine, data: []const u8, destination_queue_family: u32) !StagingBuffer {
|
pub const InitInfo = struct {
|
||||||
|
target_queue: TargetQueue,
|
||||||
|
capacity: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn init(engine: *Engine, init_info: InitInfo) !StagingBuffer {
|
||||||
|
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 transfer_queue_family = engine.transfer_queue.allocation.family;
|
||||||
|
|
||||||
const qsm = QSM.resolve(destination_queue_family, transfer_queue_family);
|
const buffer = try engine.createBuffer(.{
|
||||||
const buffer = try engine.device.createBuffer(&.{
|
.size = init_info.capacity,
|
||||||
.size = data.len,
|
|
||||||
.usage = .{
|
.usage = .{
|
||||||
.transfer_src_bit = true,
|
.transfer_src_bit = true,
|
||||||
},
|
},
|
||||||
.sharing_mode = qsm.sharing_mode,
|
.queue_family_indices = &.{ target_queue_family, transfer_queue_family },
|
||||||
.queue_family_index_count = qsm.queue_family_index_count,
|
});
|
||||||
.p_queue_family_indices = qsm.p_queue_family_indices,
|
errdefer engine.destroyBuffer(buffer);
|
||||||
}, &engine.vk_allocator.interface);
|
|
||||||
errdefer engine.device.destroyBuffer(buffer, &engine.vk_allocator.interface);
|
|
||||||
|
|
||||||
const memory_requirements = engine.device.getBufferMemoryRequirements(buffer);
|
const memory_requirements = engine.device.getBufferMemoryRequirements(buffer);
|
||||||
const memory = try engine.allocate(
|
const device_memory = try engine.allocate(
|
||||||
memory_requirements,
|
memory_requirements,
|
||||||
.{
|
.{
|
||||||
.host_visible_bit = true,
|
.host_visible_bit = true,
|
||||||
.host_coherent_bit = true,
|
.host_coherent_bit = true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
errdefer engine.device.freeMemory(memory, &engine.vk_allocator.interface);
|
errdefer engine.freeMemory(device_memory);
|
||||||
|
|
||||||
try engine.device.bindBufferMemory(buffer, memory, 0);
|
try engine.device.bindBufferMemory(buffer, device_memory, 0);
|
||||||
|
|
||||||
const mapped_memory: [*]u8 = @ptrCast(try engine.device.mapMemory(memory, 0, data.len, .{}) orelse return error.OutOfMemory);
|
|
||||||
defer engine.device.unmapMemory(memory);
|
|
||||||
@memcpy(mapped_memory, data);
|
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.buffer = buffer,
|
.buffer = buffer,
|
||||||
.memory = memory,
|
.device_memory = device_memory,
|
||||||
|
.capacity = init_info.capacity,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *StagingBuffer, engine: *Engine) void {
|
pub fn deinit(self: *StagingBuffer, engine: *Engine) void {
|
||||||
engine.device.freeMemory(self.memory, &engine.vk_allocator.interface);
|
engine.freeMemory(self.device_memory);
|
||||||
engine.device.destroyBuffer(self.buffer, &engine.vk_allocator.interface);
|
engine.destroyBuffer(self.buffer);
|
||||||
|
|
||||||
self.* = undefined;
|
self.* = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn map(self: StagingBuffer, engine: *Engine) ![]u8 {
|
||||||
|
const mapped_memory = try self.mapPartial(engine, 0, self.capacity);
|
||||||
|
return mapped_memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mapPartial(self: StagingBuffer, engine: *Engine, offset: u32, len: u32) ![]u8 {
|
||||||
|
const mapped_memory = try engine.device.mapMemory(self.device_memory, offset, len, .{});
|
||||||
|
return @as([*]u8, @ptrCast(mapped_memory))[0..len];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unmap(self: StagingBuffer, engine: *Engine) void {
|
||||||
|
engine.device.unmapMemory(self.device_memory);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,200 +0,0 @@
|
|||||||
const StorageBuffer = @This();
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const vk = @import("vulkan");
|
|
||||||
|
|
||||||
const Engine = @import("Engine.zig");
|
|
||||||
const StagingBuffer = @import("StagingBuffer.zig");
|
|
||||||
const QSM = @import("QueueSharingMode.zig");
|
|
||||||
|
|
||||||
buffer: vk.Buffer,
|
|
||||||
memory: vk.DeviceMemory,
|
|
||||||
prefix_size: usize,
|
|
||||||
element_size: usize,
|
|
||||||
array_offset: usize,
|
|
||||||
array_capacity: usize,
|
|
||||||
|
|
||||||
pub fn init(engine: *Engine, comptime PrefixType: type, comptime ElementType: type, array_capacity: usize) !StorageBuffer {
|
|
||||||
const prefix_size = @sizeOf(PrefixType);
|
|
||||||
const array_offset = std.mem.alignForward(usize, prefix_size, @alignOf(ElementType));
|
|
||||||
const element_size = @sizeOf(ElementType);
|
|
||||||
|
|
||||||
const array_capacity_in_bytes = std.math.mul(usize, array_capacity, element_size) catch return error.OutOfMemory;
|
|
||||||
const size = std.math.add(usize, array_offset, array_capacity_in_bytes) catch return error.OutOfMemory;
|
|
||||||
|
|
||||||
const qsm = QSM.resolve(engine.graphics_queue.allocation.family, engine.transfer_queue.allocation.family);
|
|
||||||
const buffer = try engine.device.createBuffer(&.{
|
|
||||||
.size = size,
|
|
||||||
.usage = .{
|
|
||||||
.transfer_src_bit = true,
|
|
||||||
.transfer_dst_bit = true,
|
|
||||||
.storage_buffer_bit = true,
|
|
||||||
},
|
|
||||||
.sharing_mode = qsm.sharing_mode,
|
|
||||||
.queue_family_index_count = qsm.queue_family_index_count,
|
|
||||||
.p_queue_family_indices = qsm.p_queue_family_indices,
|
|
||||||
}, &engine.vk_allocator.interface);
|
|
||||||
errdefer engine.device.destroyBuffer(buffer, &engine.vk_allocator.interface);
|
|
||||||
|
|
||||||
const memory_requirements = engine.device.getBufferMemoryRequirements(buffer);
|
|
||||||
const memory = try engine.allocate(memory_requirements, .{ .device_local_bit = true });
|
|
||||||
errdefer engine.device.freeMemory(memory, &engine.vk_allocator.interface);
|
|
||||||
|
|
||||||
try engine.device.bindBufferMemory(buffer, memory, 0);
|
|
||||||
|
|
||||||
return .{
|
|
||||||
.buffer = buffer,
|
|
||||||
.memory = memory,
|
|
||||||
.prefix_size = prefix_size,
|
|
||||||
.element_size = element_size,
|
|
||||||
.array_offset = array_offset,
|
|
||||||
.array_capacity = array_capacity,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(self: *StorageBuffer, engine: *Engine) void {
|
|
||||||
engine.device.freeMemory(self.memory, &engine.vk_allocator.interface);
|
|
||||||
engine.device.destroyBuffer(self.buffer, &engine.vk_allocator.interface);
|
|
||||||
|
|
||||||
self.* = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn enlarge(self: *StorageBuffer, comptime PrefixType: type, comptime ElementType: type, engine: *Engine, array_capacity: usize) !void {
|
|
||||||
std.debug.assert(array_capacity > self.array_capacity);
|
|
||||||
std.debug.assert(@sizeOf(PrefixType) == self.prefix_size);
|
|
||||||
std.debug.assert(@sizeOf(ElementType) == self.element_size);
|
|
||||||
std.debug.assert(std.mem.isAligned(self.array_offset, @alignOf(ElementType)));
|
|
||||||
|
|
||||||
const fence = try engine.device.createFence(&.{}, &engine.vk_allocator.interface);
|
|
||||||
defer engine.device.destroyFence(fence, &engine.vk_allocator.interface);
|
|
||||||
|
|
||||||
const prefix_size = @sizeOf(PrefixType);
|
|
||||||
const array_offset = std.mem.alignForward(usize, prefix_size, @alignOf(ElementType));
|
|
||||||
const element_size = @sizeOf(ElementType);
|
|
||||||
|
|
||||||
const old_array_size_in_bytes = self.array_capacity * @sizeOf(ElementType);
|
|
||||||
const old_size = self.array_offset + old_array_size_in_bytes;
|
|
||||||
|
|
||||||
const new_array_capacity_in_bytes = std.math.mul(usize, array_capacity, element_size) catch return error.OutOfMemory;
|
|
||||||
const new_size = std.math.add(usize, array_offset, new_array_capacity_in_bytes) catch return error.OutOfMemory;
|
|
||||||
|
|
||||||
const qsm = QSM.resolve(engine.graphics_queue.allocation.family, engine.transfer_queue.allocation.family);
|
|
||||||
const buffer = try engine.device.createBuffer(&.{
|
|
||||||
.size = new_size,
|
|
||||||
.usage = .{
|
|
||||||
.transfer_src_bit = true,
|
|
||||||
.transfer_dst_bit = true,
|
|
||||||
.storage_buffer_bit = true,
|
|
||||||
},
|
|
||||||
.sharing_mode = qsm.sharing_mode,
|
|
||||||
.queue_family_index_count = qsm.queue_family_index_count,
|
|
||||||
.p_queue_family_indices = qsm.p_queue_family_indices,
|
|
||||||
}, &engine.vk_allocator.interface);
|
|
||||||
errdefer engine.device.destroyBuffer(buffer, &engine.vk_allocator.interface);
|
|
||||||
|
|
||||||
const memory_requirements = engine.device.getBufferMemoryRequirements(buffer);
|
|
||||||
const memory = try engine.allocate(memory_requirements, .{ .device_local_bit = true });
|
|
||||||
errdefer engine.device.freeMemory(memory, &engine.vk_allocator.interface);
|
|
||||||
|
|
||||||
try engine.device.bindBufferMemory(buffer, memory, 0);
|
|
||||||
|
|
||||||
const command_buffer = try engine.allocateTransferCommandBuffer();
|
|
||||||
defer engine.freeTransferCommandBuffer(command_buffer);
|
|
||||||
|
|
||||||
try command_buffer.beginCommandBuffer(&.{ .flags = .{ .one_time_submit_bit = true } });
|
|
||||||
|
|
||||||
const regions = [_]vk.BufferCopy{
|
|
||||||
.{
|
|
||||||
.src_offset = 0,
|
|
||||||
.dst_offset = 0,
|
|
||||||
.size = old_size,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
command_buffer.copyBuffer(
|
|
||||||
self.buffer,
|
|
||||||
buffer,
|
|
||||||
regions.len,
|
|
||||||
®ions,
|
|
||||||
);
|
|
||||||
|
|
||||||
try command_buffer.endCommandBuffer();
|
|
||||||
try engine.submitTransferCommandBuffer(command_buffer, fence);
|
|
||||||
|
|
||||||
_ = try engine.device.waitForFences(1, @ptrCast(&fence), .true, std.math.maxInt(u64));
|
|
||||||
|
|
||||||
engine.device.freeMemory(self.memory, &engine.vk_allocator.interface);
|
|
||||||
engine.device.destroyBuffer(self.buffer, &engine.vk_allocator.interface);
|
|
||||||
|
|
||||||
self.buffer = buffer;
|
|
||||||
self.memory = memory;
|
|
||||||
self.array_capacity = array_capacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write(self: StorageBuffer, comptime PrefixType: type, comptime ElementType: type, engine: *Engine, prefix: PrefixType, elements: []const ElementType) !void {
|
|
||||||
try self.writeOffset(PrefixType, ElementType, engine, prefix, 0, elements);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn writeOffset(self: StorageBuffer, comptime PrefixType: type, comptime ElementType: type, engine: *Engine, prefix: PrefixType, offset: usize, elements: []const ElementType) !void {
|
|
||||||
std.debug.assert(offset + elements.len <= self.array_capacity);
|
|
||||||
std.debug.assert(@sizeOf(PrefixType) == self.prefix_size);
|
|
||||||
std.debug.assert(@sizeOf(ElementType) == self.element_size);
|
|
||||||
std.debug.assert(std.mem.isAligned(self.array_offset, @alignOf(ElementType)));
|
|
||||||
|
|
||||||
const array_size_in_bytes = elements.len * @sizeOf(ElementType);
|
|
||||||
const size = self.array_offset + array_size_in_bytes;
|
|
||||||
|
|
||||||
var regions_buffer: [2]vk.BufferCopy = undefined;
|
|
||||||
var regions: std.ArrayList(vk.BufferCopy) = .initBuffer(®ions_buffer);
|
|
||||||
|
|
||||||
if (self.prefix_size > 0) {
|
|
||||||
regions.appendAssumeCapacity(.{
|
|
||||||
.src_offset = 0,
|
|
||||||
.dst_offset = 0,
|
|
||||||
.size = self.prefix_size,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (array_size_in_bytes > 0) {
|
|
||||||
regions.appendAssumeCapacity(.{
|
|
||||||
.src_offset = self.array_offset,
|
|
||||||
.dst_offset = self.array_offset + offset * @sizeOf(ElementType),
|
|
||||||
.size = array_size_in_bytes,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (regions.items.len == 0) {
|
|
||||||
std.log.warn("Zero-length StorageBuffer({s}, {s}) write", .{ @typeName(PrefixType), @typeName(ElementType) });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fence = try engine.device.createFence(&.{}, &engine.vk_allocator.interface);
|
|
||||||
defer engine.device.destroyFence(fence, &engine.vk_allocator.interface);
|
|
||||||
|
|
||||||
const data = try engine.vk_allocator.allocator.alloc(u8, size);
|
|
||||||
defer engine.vk_allocator.allocator.free(data);
|
|
||||||
|
|
||||||
@memcpy(data[0..@sizeOf(PrefixType)], std.mem.asBytes(&prefix));
|
|
||||||
@memcpy(data[self.array_offset..], std.mem.sliceAsBytes(elements));
|
|
||||||
|
|
||||||
var staging_buffer: StagingBuffer = try .init(engine, data, engine.graphics_queue.allocation.family);
|
|
||||||
defer staging_buffer.deinit(engine);
|
|
||||||
|
|
||||||
const command_buffer = try engine.allocateTransferCommandBuffer();
|
|
||||||
defer engine.freeTransferCommandBuffer(command_buffer);
|
|
||||||
|
|
||||||
try command_buffer.beginCommandBuffer(&.{ .flags = .{ .one_time_submit_bit = true } });
|
|
||||||
|
|
||||||
command_buffer.copyBuffer(
|
|
||||||
staging_buffer.buffer,
|
|
||||||
self.buffer,
|
|
||||||
@intCast(regions.items.len),
|
|
||||||
regions.items.ptr,
|
|
||||||
);
|
|
||||||
|
|
||||||
try command_buffer.endCommandBuffer();
|
|
||||||
|
|
||||||
try engine.submitTransferCommandBuffer(command_buffer, fence);
|
|
||||||
|
|
||||||
_ = try engine.device.waitForFences(1, @ptrCast(&fence), .true, std.math.maxInt(u64));
|
|
||||||
}
|
|
||||||
@@ -6,11 +6,19 @@ const vk = @import("vulkan");
|
|||||||
const Engine = @import("Engine.zig");
|
const Engine = @import("Engine.zig");
|
||||||
const QSM = @import("QueueSharingMode.zig");
|
const QSM = @import("QueueSharingMode.zig");
|
||||||
|
|
||||||
engine: *Engine,
|
|
||||||
params: Params,
|
params: Params,
|
||||||
render_pass: vk.RenderPass,
|
render_pass: vk.RenderPass,
|
||||||
|
|
||||||
|
extent: vk.Extent2D = .{ .width = 0, .height = 0 },
|
||||||
swapchain: vk.SwapchainKHR = .null_handle,
|
swapchain: vk.SwapchainKHR = .null_handle,
|
||||||
swapchain_images: []SwapchainImage = &.{},
|
swapchain_images: []SwapchainImage = &.{},
|
||||||
|
image_index: u32 = 0,
|
||||||
|
semaphore_image_acquired: vk.Semaphore = .null_handle,
|
||||||
|
|
||||||
|
pub const PresentResult = enum {
|
||||||
|
optimal,
|
||||||
|
suboptimal,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn init(engine: *Engine) !Swapchain {
|
pub fn init(engine: *Engine) !Swapchain {
|
||||||
const params: Params = try .init(engine);
|
const params: Params = try .init(engine);
|
||||||
@@ -20,7 +28,7 @@ pub fn init(engine: *Engine) !Swapchain {
|
|||||||
.{
|
.{
|
||||||
.format = params.surface_format.format,
|
.format = params.surface_format.format,
|
||||||
.samples = .{ .@"1_bit" = true },
|
.samples = .{ .@"1_bit" = true },
|
||||||
.load_op = .dont_care,
|
.load_op = .clear,
|
||||||
.store_op = .store,
|
.store_op = .store,
|
||||||
.stencil_load_op = .dont_care,
|
.stencil_load_op = .dont_care,
|
||||||
.stencil_store_op = .dont_care,
|
.stencil_store_op = .dont_care,
|
||||||
@@ -54,47 +62,49 @@ pub fn init(engine: *Engine) !Swapchain {
|
|||||||
errdefer engine.device.destroyRenderPass(render_pass, &engine.vk_allocator.interface);
|
errdefer engine.device.destroyRenderPass(render_pass, &engine.vk_allocator.interface);
|
||||||
|
|
||||||
var swapchain: Swapchain = .{
|
var swapchain: Swapchain = .{
|
||||||
.engine = engine,
|
|
||||||
.params = params,
|
.params = params,
|
||||||
.render_pass = render_pass,
|
.render_pass = render_pass,
|
||||||
};
|
};
|
||||||
|
|
||||||
try recreate(&swapchain);
|
try recreate(&swapchain, engine);
|
||||||
return swapchain;
|
return swapchain;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Swapchain) void {
|
pub fn deinit(self: *Swapchain, engine: *Engine) void {
|
||||||
const allocator = self.engine.vk_allocator.allocator;
|
const allocator = engine.vk_allocator.allocator;
|
||||||
|
|
||||||
for (self.swapchain_images) |swapchain_image| {
|
for (self.swapchain_images) |swapchain_image| {
|
||||||
swapchain_image.deinit(self.engine);
|
swapchain_image.deinit(engine);
|
||||||
}
|
}
|
||||||
allocator.free(self.swapchain_images);
|
allocator.free(self.swapchain_images);
|
||||||
|
|
||||||
if (self.swapchain != .null_handle) {
|
if (self.swapchain != .null_handle) {
|
||||||
self.engine.device.destroySwapchainKHR(self.swapchain, &self.engine.vk_allocator.interface);
|
engine.device.destroySwapchainKHR(self.swapchain, &engine.vk_allocator.interface);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.engine.device.destroyRenderPass(self.render_pass, &self.engine.vk_allocator.interface);
|
engine.device.destroyRenderPass(self.render_pass, &engine.vk_allocator.interface);
|
||||||
|
|
||||||
self.* = undefined;
|
self.* = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn recreate(self: *Swapchain) !void {
|
pub fn recreate(self: *Swapchain, engine: *Engine) !void {
|
||||||
const mode = &self.engine.mode.surface;
|
const mode = &engine.mode.surface;
|
||||||
const allocator = self.engine.vk_allocator.allocator;
|
const allocator = engine.vk_allocator.allocator;
|
||||||
|
|
||||||
const old_swapchain = self.swapchain;
|
const old_swapchain = self.swapchain;
|
||||||
const old_swapchain_images = self.swapchain_images;
|
const old_swapchain_images = self.swapchain_images;
|
||||||
|
const old_semaphore_image_acquired = self.semaphore_image_acquired;
|
||||||
|
|
||||||
const extent = try getCurrentExtent(self.engine);
|
const extent = try getCurrentExtent(engine);
|
||||||
|
|
||||||
|
std.log.debug("Recreating swapchain with extent of {d}×{d}...", .{ extent.width, extent.height });
|
||||||
|
|
||||||
// --- CREATE NEW SWAPCHAIN ------------------------------------------------
|
// --- CREATE NEW SWAPCHAIN ------------------------------------------------
|
||||||
|
|
||||||
const surface_capabilities = try self.engine.instance.getPhysicalDeviceSurfaceCapabilitiesKHR(self.engine.physical_device, mode.surface);
|
const surface_capabilities = try engine.instance.getPhysicalDeviceSurfaceCapabilitiesKHR(engine.physical_device, mode.surface);
|
||||||
|
|
||||||
const qsm = QSM.resolve(self.engine.graphics_queue.allocation.family, mode.presentation_queue.allocation.family);
|
const qsm = QSM.resolve(engine.graphics_queue.allocation.family, mode.presentation_queue.allocation.family);
|
||||||
const new_swapchain = try self.engine.device.createSwapchainKHR(&.{
|
const new_swapchain = try engine.device.createSwapchainKHR(&.{
|
||||||
.surface = mode.surface,
|
.surface = mode.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,
|
||||||
@@ -110,8 +120,8 @@ pub fn recreate(self: *Swapchain) !void {
|
|||||||
.present_mode = .fifo_khr,
|
.present_mode = .fifo_khr,
|
||||||
.clipped = .true,
|
.clipped = .true,
|
||||||
.old_swapchain = old_swapchain,
|
.old_swapchain = old_swapchain,
|
||||||
}, &self.engine.vk_allocator.interface);
|
}, &engine.vk_allocator.interface);
|
||||||
errdefer self.engine.device.destroySwapchainKHR(new_swapchain, &self.engine.vk_allocator.interface);
|
errdefer engine.device.destroySwapchainKHR(new_swapchain, &engine.vk_allocator.interface);
|
||||||
|
|
||||||
// --- DESTROY OLD SWAPCHAIN AND ITS IMAGES --------------------------------
|
// --- DESTROY OLD SWAPCHAIN AND ITS IMAGES --------------------------------
|
||||||
|
|
||||||
@@ -120,29 +130,36 @@ pub fn recreate(self: *Swapchain) !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(self.engine);
|
swapchain_image.deinit(engine);
|
||||||
}
|
}
|
||||||
allocator.free(self.swapchain_images);
|
allocator.free(self.swapchain_images);
|
||||||
self.swapchain_images = &.{};
|
self.swapchain_images = &.{};
|
||||||
|
|
||||||
if (old_swapchain != .null_handle) {
|
if (old_swapchain != .null_handle) {
|
||||||
self.engine.device.destroySwapchainKHR(old_swapchain, &self.engine.vk_allocator.interface);
|
engine.device.destroySwapchainKHR(old_swapchain, &engine.vk_allocator.interface);
|
||||||
self.swapchain = .null_handle;
|
self.swapchain = .null_handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.extent = .{ .width = 0, .height = 0 };
|
||||||
|
|
||||||
|
if (old_semaphore_image_acquired != .null_handle) {
|
||||||
|
engine.destroySemaphore(old_semaphore_image_acquired);
|
||||||
|
self.semaphore_image_acquired = .null_handle;
|
||||||
|
}
|
||||||
|
|
||||||
// --- CREATE NEW SWAPCHAIN IMAGES -----------------------------------------
|
// --- CREATE NEW SWAPCHAIN IMAGES -----------------------------------------
|
||||||
|
|
||||||
const new_swapchain_images = blk: {
|
const new_swapchain_images = blk: {
|
||||||
const images = try self.engine.device.getSwapchainImagesAllocKHR(new_swapchain, allocator);
|
const images = try engine.device.getSwapchainImagesAllocKHR(new_swapchain, allocator);
|
||||||
defer allocator.free(images);
|
defer allocator.free(images);
|
||||||
|
|
||||||
var swapchain_images: std.ArrayList(SwapchainImage) = try .initCapacity(allocator, images.len);
|
var swapchain_images: std.ArrayList(SwapchainImage) = try .initCapacity(allocator, images.len);
|
||||||
errdefer swapchain_images.deinit(allocator);
|
errdefer swapchain_images.deinit(allocator);
|
||||||
|
|
||||||
errdefer for (swapchain_images.items) |x| x.deinit(self.engine);
|
errdefer for (swapchain_images.items) |x| x.deinit(engine);
|
||||||
|
|
||||||
for (images) |image| {
|
for (images) |image| {
|
||||||
swapchain_images.appendAssumeCapacity(try SwapchainImage.init(self.engine, .{
|
swapchain_images.appendAssumeCapacity(try SwapchainImage.init(engine, .{
|
||||||
.image = image,
|
.image = image,
|
||||||
.format = self.params.surface_format.format,
|
.format = self.params.surface_format.format,
|
||||||
.render_pass = self.render_pass,
|
.render_pass = self.render_pass,
|
||||||
@@ -154,20 +171,105 @@ pub fn recreate(self: *Swapchain) !void {
|
|||||||
};
|
};
|
||||||
errdefer {
|
errdefer {
|
||||||
for (new_swapchain_images) |swapchain_image| {
|
for (new_swapchain_images) |swapchain_image| {
|
||||||
swapchain_image.deinit(self.engine);
|
swapchain_image.deinit(engine);
|
||||||
}
|
}
|
||||||
allocator.free(new_swapchain_images);
|
allocator.free(new_swapchain_images);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- ACQUIRE NEXT IMAGE --------------------------------------------------
|
||||||
|
|
||||||
|
var semaphore_image_acquired = try engine.createSemaphore();
|
||||||
|
errdefer engine.destroySemaphore(semaphore_image_acquired);
|
||||||
|
|
||||||
|
const res = try engine.device.acquireNextImageKHR(new_swapchain, std.math.maxInt(u64), semaphore_image_acquired, .null_handle);
|
||||||
|
if (res.result == .not_ready or res.result == .timeout) {
|
||||||
|
return error.ImageAcquireFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
std.mem.swap(vk.Semaphore, &new_swapchain_images[res.image_index].semaphore_image_acquired, &semaphore_image_acquired);
|
||||||
|
|
||||||
// --- COMMIT --------------------------------------------------------------
|
// --- COMMIT --------------------------------------------------------------
|
||||||
|
|
||||||
|
self.extent = extent;
|
||||||
self.swapchain = new_swapchain;
|
self.swapchain = new_swapchain;
|
||||||
self.swapchain_images = new_swapchain_images;
|
self.swapchain_images = new_swapchain_images;
|
||||||
|
self.image_index = res.image_index;
|
||||||
|
self.semaphore_image_acquired = semaphore_image_acquired;
|
||||||
|
|
||||||
|
std.log.debug("Swapchain recreated.", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getCurrentExtent(self: *Engine) !vk.Extent2D {
|
pub fn present(self: *Swapchain, engine: *Engine, command_buffer: vk.CommandBuffer) !PresentResult {
|
||||||
const mode = &self.mode.surface;
|
const device = engine.device;
|
||||||
const surface_capabilities = try self.instance.getPhysicalDeviceSurfaceCapabilitiesKHR(self.physical_device, mode.surface);
|
const mode = &engine.mode.surface;
|
||||||
|
|
||||||
|
std.log.debug("Presenting command buffer {X}.", .{@intFromEnum(command_buffer)});
|
||||||
|
|
||||||
|
// --- WAIT FOR CURRENT FRAME TO FINISH ------------------------------------
|
||||||
|
|
||||||
|
const current_swapchain_image = &self.swapchain_images[self.image_index];
|
||||||
|
try engine.waitForFence(current_swapchain_image.fence);
|
||||||
|
if (current_swapchain_image.command_buffer != .null_handle) {
|
||||||
|
device.freeCommandBuffers(engine.graphics_command_pool, 1, @ptrCast(¤t_swapchain_image.command_buffer));
|
||||||
|
current_swapchain_image.command_buffer = .null_handle;
|
||||||
|
}
|
||||||
|
try engine.resetFence(current_swapchain_image.fence);
|
||||||
|
|
||||||
|
// --- SUBMIT COMMAND BUFFER -----------------------------------------------
|
||||||
|
|
||||||
|
const wait_semaphores = [_]vk.Semaphore{
|
||||||
|
current_swapchain_image.semaphore_image_acquired,
|
||||||
|
};
|
||||||
|
|
||||||
|
const pipeline_stages_flags = [_]vk.PipelineStageFlags{
|
||||||
|
.{ .top_of_pipe_bit = true },
|
||||||
|
};
|
||||||
|
|
||||||
|
std.debug.assert(wait_semaphores.len == pipeline_stages_flags.len);
|
||||||
|
|
||||||
|
const signal_semaphores = [_]vk.Semaphore{
|
||||||
|
current_swapchain_image.semaphore_render_finished,
|
||||||
|
};
|
||||||
|
|
||||||
|
current_swapchain_image.command_buffer = command_buffer;
|
||||||
|
try device.queueSubmit(engine.graphics_queue.handle, 1, &.{
|
||||||
|
.{
|
||||||
|
.wait_semaphore_count = wait_semaphores.len,
|
||||||
|
.p_wait_semaphores = &wait_semaphores,
|
||||||
|
.p_wait_dst_stage_mask = &pipeline_stages_flags,
|
||||||
|
.command_buffer_count = 1,
|
||||||
|
.p_command_buffers = @ptrCast(&command_buffer),
|
||||||
|
.signal_semaphore_count = signal_semaphores.len,
|
||||||
|
.p_signal_semaphores = &signal_semaphores,
|
||||||
|
},
|
||||||
|
}, current_swapchain_image.fence);
|
||||||
|
|
||||||
|
// --- PRESENT CURRENT FRAME -----------------------------------------------
|
||||||
|
|
||||||
|
_ = try device.queuePresentKHR(mode.presentation_queue.handle, &.{
|
||||||
|
.wait_semaphore_count = 1,
|
||||||
|
.p_wait_semaphores = @ptrCast(¤t_swapchain_image.semaphore_render_finished),
|
||||||
|
.swapchain_count = 1,
|
||||||
|
.p_swapchains = @ptrCast(&self.swapchain),
|
||||||
|
.p_image_indices = @ptrCast(&self.image_index),
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- ACQUIRE NEXT FRAME --------------------------------------------------
|
||||||
|
|
||||||
|
const res = try device.acquireNextImageKHR(self.swapchain, std.math.maxInt(u64), self.semaphore_image_acquired, .null_handle);
|
||||||
|
std.mem.swap(vk.Semaphore, &self.swapchain_images[res.image_index].semaphore_image_acquired, &self.semaphore_image_acquired);
|
||||||
|
self.image_index = res.image_index;
|
||||||
|
|
||||||
|
return switch (res.result) {
|
||||||
|
.success => .optimal,
|
||||||
|
.suboptimal_khr => .suboptimal,
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getCurrentExtent(engine: *Engine) !vk.Extent2D {
|
||||||
|
const mode = &engine.mode.surface;
|
||||||
|
const surface_capabilities = try engine.instance.getPhysicalDeviceSurfaceCapabilitiesKHR(engine.physical_device, mode.surface);
|
||||||
|
|
||||||
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;
|
||||||
@@ -223,6 +325,8 @@ const Params = struct {
|
|||||||
if (surface_capabilities.max_image_count > 0) surface_capabilities.max_image_count else std.math.maxInt(u32),
|
if (surface_capabilities.max_image_count > 0) surface_capabilities.max_image_count else std.math.maxInt(u32),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
std.log.debug("Using surface format \"{s}\" and color space \"{s}\" with {d} images.", .{ @tagName(surface_format.format), @tagName(surface_format.color_space), image_count });
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.surface_format = surface_format,
|
.surface_format = surface_format,
|
||||||
.image_count = image_count,
|
.image_count = image_count,
|
||||||
@@ -237,6 +341,7 @@ const SwapchainImage = struct {
|
|||||||
semaphore_render_finished: vk.Semaphore,
|
semaphore_render_finished: vk.Semaphore,
|
||||||
fence: vk.Fence,
|
fence: vk.Fence,
|
||||||
framebuffer: vk.Framebuffer,
|
framebuffer: vk.Framebuffer,
|
||||||
|
command_buffer: vk.CommandBuffer,
|
||||||
|
|
||||||
const InitProps = struct {
|
const InitProps = struct {
|
||||||
image: vk.Image,
|
image: vk.Image,
|
||||||
@@ -246,16 +351,10 @@ const SwapchainImage = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fn init(engine: *Engine, props: InitProps) !SwapchainImage {
|
fn init(engine: *Engine, props: InitProps) !SwapchainImage {
|
||||||
const image_view = try engine.device.createImageView(&.{
|
const image_view = try engine.createImageView(.{
|
||||||
.image = props.image,
|
.image = props.image,
|
||||||
.view_type = .@"2d",
|
.view_type = .@"2d",
|
||||||
.format = props.format,
|
.format = props.format,
|
||||||
.components = .{
|
|
||||||
.r = .identity,
|
|
||||||
.g = .identity,
|
|
||||||
.b = .identity,
|
|
||||||
.a = .identity,
|
|
||||||
},
|
|
||||||
.subresource_range = .{
|
.subresource_range = .{
|
||||||
.aspect_mask = .{ .color_bit = true },
|
.aspect_mask = .{ .color_bit = true },
|
||||||
.base_mip_level = 0,
|
.base_mip_level = 0,
|
||||||
@@ -263,31 +362,28 @@ const SwapchainImage = struct {
|
|||||||
.base_array_layer = 0,
|
.base_array_layer = 0,
|
||||||
.layer_count = 1,
|
.layer_count = 1,
|
||||||
},
|
},
|
||||||
}, &engine.vk_allocator.interface);
|
});
|
||||||
errdefer engine.device.destroyImageView(image_view, &engine.vk_allocator.interface);
|
errdefer engine.destroyImageView(image_view);
|
||||||
|
|
||||||
const semaphore_image_acquired = try engine.device.createSemaphore(&.{}, &engine.vk_allocator.interface);
|
const semaphore_image_acquired = try engine.createSemaphore();
|
||||||
errdefer engine.device.destroySemaphore(semaphore_image_acquired, &engine.vk_allocator.interface);
|
errdefer engine.destroySemaphore(semaphore_image_acquired);
|
||||||
|
|
||||||
const semaphore_render_finished = try engine.device.createSemaphore(&.{}, &engine.vk_allocator.interface);
|
const semaphore_render_finished = try engine.createSemaphore();
|
||||||
errdefer engine.device.destroySemaphore(semaphore_render_finished, &engine.vk_allocator.interface);
|
errdefer engine.destroySemaphore(semaphore_render_finished);
|
||||||
|
|
||||||
const fence = try engine.device.createFence(&.{
|
const fence = try engine.createFence(.{
|
||||||
.flags = .{ .signaled_bit = true },
|
.flags = .{ .signaled_bit = true },
|
||||||
}, &engine.vk_allocator.interface);
|
});
|
||||||
errdefer engine.device.destroyFence(fence, &engine.vk_allocator.interface);
|
errdefer engine.destroyFence(fence);
|
||||||
|
|
||||||
const attachments = [_]vk.ImageView{image_view};
|
const framebuffer = try engine.createFramebuffer(.{
|
||||||
|
|
||||||
const framebuffer = try engine.device.createFramebuffer(&.{
|
|
||||||
.render_pass = props.render_pass,
|
.render_pass = props.render_pass,
|
||||||
.attachment_count = attachments.len,
|
.attachments = &.{image_view},
|
||||||
.p_attachments = &attachments,
|
|
||||||
.width = props.extent.width,
|
.width = props.extent.width,
|
||||||
.height = props.extent.height,
|
.height = props.extent.height,
|
||||||
.layers = 1,
|
.layers = 1,
|
||||||
}, &engine.vk_allocator.interface);
|
});
|
||||||
errdefer engine.device.destroyFramebuffer(framebuffer, &engine.vk_allocator.interface);
|
errdefer engine.destroyFramebuffer(framebuffer);
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.image = props.image,
|
.image = props.image,
|
||||||
@@ -296,20 +392,16 @@ const SwapchainImage = struct {
|
|||||||
.semaphore_render_finished = semaphore_render_finished,
|
.semaphore_render_finished = semaphore_render_finished,
|
||||||
.fence = fence,
|
.fence = fence,
|
||||||
.framebuffer = framebuffer,
|
.framebuffer = framebuffer,
|
||||||
|
.command_buffer = .null_handle,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deinit(self: SwapchainImage, engine: *Engine) void {
|
fn deinit(self: SwapchainImage, engine: *Engine) void {
|
||||||
self.waitForFence(engine) catch return;
|
engine.waitForFence(self.fence) catch {};
|
||||||
engine.device.destroyFramebuffer(self.framebuffer, &engine.vk_allocator.interface);
|
engine.destroyFramebuffer(self.framebuffer);
|
||||||
engine.device.destroyFence(self.fence, &engine.vk_allocator.interface);
|
engine.destroyFence(self.fence);
|
||||||
engine.device.destroySemaphore(self.semaphore_render_finished, &engine.vk_allocator.interface);
|
engine.destroySemaphore(self.semaphore_render_finished);
|
||||||
engine.device.destroySemaphore(self.semaphore_image_acquired, &engine.vk_allocator.interface);
|
engine.destroySemaphore(self.semaphore_image_acquired);
|
||||||
engine.device.destroyImageView(self.image_view, &engine.vk_allocator.interface);
|
engine.destroyImageView(self.image_view);
|
||||||
}
|
|
||||||
|
|
||||||
fn waitForFence(self: SwapchainImage, engine: *Engine) !void {
|
|
||||||
const fences = [_]vk.Fence{self.fence};
|
|
||||||
_ = try engine.device.waitForFences(fences.len, &fences, .true, std.math.maxInt(u64));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
4
src/engine/TargetQueue.zig
Normal file
4
src/engine/TargetQueue.zig
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
pub const TargetQueue = enum {
|
||||||
|
graphics,
|
||||||
|
compute,
|
||||||
|
};
|
||||||
@@ -5,7 +5,7 @@ const vk = @import("vulkan");
|
|||||||
|
|
||||||
const Engine = @import("Engine.zig");
|
const Engine = @import("Engine.zig");
|
||||||
const StagingBuffer = @import("StagingBuffer.zig");
|
const StagingBuffer = @import("StagingBuffer.zig");
|
||||||
const QSM = @import("QueueSharingMode.zig");
|
const TargetQueue = @import("TargetQueue.zig").TargetQueue;
|
||||||
|
|
||||||
pub const Usage = enum {
|
pub const Usage = enum {
|
||||||
base_color,
|
base_color,
|
||||||
@@ -13,7 +13,7 @@ pub const Usage = enum {
|
|||||||
occlusion_roughness_metallic,
|
occlusion_roughness_metallic,
|
||||||
emissive,
|
emissive,
|
||||||
|
|
||||||
pub fn format(self: Usage) vk.Format {
|
pub fn vkFormat(self: Usage) vk.Format {
|
||||||
return switch (self) {
|
return switch (self) {
|
||||||
.base_color => .r8g8b8a8_srgb,
|
.base_color => .r8g8b8a8_srgb,
|
||||||
.normal => .r8g8b8a8_snorm,
|
.normal => .r8g8b8a8_snorm,
|
||||||
@@ -22,7 +22,7 @@ pub const Usage = enum {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sampleCount(self: Usage) u32 {
|
pub fn samplesPerTexel(self: Usage) u32 {
|
||||||
return switch (self) {
|
return switch (self) {
|
||||||
.base_color => 4,
|
.base_color => 4,
|
||||||
.normal => 4,
|
.normal => 4,
|
||||||
@@ -34,47 +34,58 @@ pub const Usage = enum {
|
|||||||
pub fn SampleType(comptime self: Usage) type {
|
pub fn SampleType(comptime self: Usage) type {
|
||||||
return switch (self) {
|
return switch (self) {
|
||||||
.base_color => u8,
|
.base_color => u8,
|
||||||
.normal => u8,
|
.normal => i8,
|
||||||
.occlusion_roughness_metallic => u8,
|
.occlusion_roughness_metallic => u8,
|
||||||
.emissive => f16,
|
.emissive => f16,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sampleSize(self: Usage) u32 {
|
pub fn bytesPerSample(self: Usage) u32 {
|
||||||
return switch (self) {
|
return switch (self) {
|
||||||
inline else => |x| @sizeOf(SampleType(x)),
|
inline else => |x| @sizeOf(SampleType(x)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn TexelType(comptime self: Usage) type {
|
pub fn TexelType(comptime self: Usage) type {
|
||||||
return [self.sampleCount()]SampleType(self);
|
return [self.samplesPerTexel()]SampleType(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn texelSize(self: Usage) u32 {
|
pub fn bytesPerTexel(self: Usage) u32 {
|
||||||
return switch (self) {
|
return switch (self) {
|
||||||
inline else => |x| @sizeOf(TexelType(x)),
|
inline else => |x| @sizeOf(TexelType(x)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const InitInfo = struct {
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
usage: Usage,
|
||||||
|
target_queue: TargetQueue,
|
||||||
|
};
|
||||||
|
|
||||||
image: vk.Image,
|
image: vk.Image,
|
||||||
image_view: vk.ImageView,
|
image_view: vk.ImageView,
|
||||||
memory: vk.DeviceMemory,
|
device_memory: vk.DeviceMemory,
|
||||||
|
target_queue: TargetQueue,
|
||||||
|
|
||||||
width: u32,
|
width: u32,
|
||||||
height: u32,
|
height: u32,
|
||||||
usage: Usage,
|
usage: Usage,
|
||||||
|
|
||||||
pub fn init(engine: *Engine, width: u32, height: u32, usage: Usage) !Texture {
|
pub fn init(engine: *Engine, init_info: InitInfo) !Texture {
|
||||||
const format: vk.Format = usage.format();
|
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 qsm = QSM.resolve(engine.graphics_queue.allocation.family, engine.transfer_queue.allocation.family);
|
const image = try engine.createImage(.{
|
||||||
const image = try engine.device.createImage(&.{
|
|
||||||
.image_type = .@"2d",
|
.image_type = .@"2d",
|
||||||
.format = format,
|
.format = init_info.usage.vkFormat(),
|
||||||
.extent = .{
|
.extent = .{
|
||||||
.width = width,
|
.width = init_info.width,
|
||||||
.height = height,
|
.height = init_info.height,
|
||||||
.depth = 1,
|
.depth = 1,
|
||||||
},
|
},
|
||||||
.mip_levels = 1,
|
.mip_levels = 1,
|
||||||
@@ -85,29 +96,21 @@ pub fn init(engine: *Engine, width: u32, height: u32, usage: Usage) !Texture {
|
|||||||
.transfer_dst_bit = true,
|
.transfer_dst_bit = true,
|
||||||
.sampled_bit = true,
|
.sampled_bit = true,
|
||||||
},
|
},
|
||||||
.sharing_mode = qsm.sharing_mode,
|
.queue_family_indices = &.{ target_queue_family, transfer_queue_family },
|
||||||
.queue_family_index_count = qsm.queue_family_index_count,
|
|
||||||
.p_queue_family_indices = qsm.p_queue_family_indices,
|
|
||||||
.initial_layout = .undefined,
|
.initial_layout = .undefined,
|
||||||
}, &engine.vk_allocator.interface);
|
});
|
||||||
errdefer engine.device.destroyImage(image, &engine.vk_allocator.interface);
|
errdefer engine.destroyImage(image);
|
||||||
|
|
||||||
const memory_requirements = engine.device.getImageMemoryRequirements(image);
|
const memory_requirements = engine.device.getImageMemoryRequirements(image);
|
||||||
const memory = try engine.allocate(memory_requirements, .{ .device_local_bit = true });
|
const device_memory = try engine.allocate(memory_requirements, .{ .device_local_bit = true });
|
||||||
errdefer engine.device.freeMemory(memory, &engine.vk_allocator.interface);
|
errdefer engine.freeMemory(device_memory);
|
||||||
|
|
||||||
try engine.device.bindImageMemory(image, memory, 0);
|
try engine.device.bindImageMemory(image, device_memory, 0);
|
||||||
|
|
||||||
const image_view = try engine.device.createImageView(&.{
|
const image_view = try engine.createImageView(.{
|
||||||
.image = image,
|
.image = image,
|
||||||
.view_type = .@"2d",
|
.view_type = .@"2d",
|
||||||
.format = format,
|
.format = init_info.usage.vkFormat(),
|
||||||
.components = .{
|
|
||||||
.r = .identity,
|
|
||||||
.g = .identity,
|
|
||||||
.b = .identity,
|
|
||||||
.a = .identity,
|
|
||||||
},
|
|
||||||
.subresource_range = .{
|
.subresource_range = .{
|
||||||
.aspect_mask = .{ .color_bit = true },
|
.aspect_mask = .{ .color_bit = true },
|
||||||
.base_mip_level = 0,
|
.base_mip_level = 0,
|
||||||
@@ -115,49 +118,72 @@ pub fn init(engine: *Engine, width: u32, height: u32, usage: Usage) !Texture {
|
|||||||
.base_array_layer = 0,
|
.base_array_layer = 0,
|
||||||
.layer_count = 1,
|
.layer_count = 1,
|
||||||
},
|
},
|
||||||
}, &engine.vk_allocator.interface);
|
});
|
||||||
errdefer engine.device.destroyImageView(image_view, &engine.vk_allocator.interface);
|
errdefer engine.destroyImageView(image_view);
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.image = image,
|
.image = image,
|
||||||
.image_view = image_view,
|
.image_view = image_view,
|
||||||
.memory = memory,
|
.device_memory = device_memory,
|
||||||
.width = width,
|
.target_queue = init_info.target_queue,
|
||||||
.height = height,
|
.width = init_info.width,
|
||||||
.usage = usage,
|
.height = init_info.height,
|
||||||
|
.usage = init_info.usage,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Texture, engine: *Engine) void {
|
pub fn deinit(self: *Texture, engine: *Engine) void {
|
||||||
engine.device.destroyImageView(self.image_view, &engine.vk_allocator.interface);
|
engine.destroyImageView(self.image_view);
|
||||||
engine.device.freeMemory(self.memory, &engine.vk_allocator.interface);
|
engine.freeMemory(self.device_memory);
|
||||||
engine.device.destroyImage(self.image, &engine.vk_allocator.interface);
|
engine.destroyImage(self.image);
|
||||||
|
|
||||||
self.* = undefined;
|
self.* = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write(self: Texture, comptime T: type, engine: *Engine, data: []const T) !void {
|
pub fn writeTexels(self: Texture, comptime TexelType: type, engine: *Engine, texels: []const TexelType) !void {
|
||||||
const bytes_per_texel = self.usage.texelSize();
|
const texel_count = try std.math.mul(u32, self.width, self.height);
|
||||||
const bytes_per_row = self.width * bytes_per_texel;
|
std.debug.assert(texels.len == texel_count);
|
||||||
const byte_length = @as(usize, self.height) * bytes_per_row;
|
switch (self.usage) {
|
||||||
|
inline else => |x| std.debug.assert(TexelType == x.TexelType()),
|
||||||
|
}
|
||||||
|
|
||||||
std.debug.assert(data.len * @sizeOf(T) == byte_length);
|
try self.writeRaw(engine, std.mem.sliceAsBytes(texels));
|
||||||
|
}
|
||||||
|
|
||||||
const fence = try engine.device.createFence(&.{}, &engine.vk_allocator.interface);
|
pub fn writeSamples(self: Texture, comptime SampleType: type, engine: *Engine, samples: []const SampleType) !void {
|
||||||
defer engine.device.destroyFence(fence, &engine.vk_allocator.interface);
|
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);
|
||||||
|
switch (self.usage) {
|
||||||
|
inline else => |x| std.debug.assert(SampleType == x.SampleType()),
|
||||||
|
}
|
||||||
|
|
||||||
var staging_buffer: StagingBuffer = try .init(engine, std.mem.sliceAsBytes(data), engine.graphics_queue.allocation.family);
|
try self.writeRaw(engine, std.mem.sliceAsBytes(samples));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn writeRaw(self: Texture, engine: *Engine, data: []const u8) !void {
|
||||||
|
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);
|
||||||
|
|
||||||
|
var staging_buffer = try StagingBuffer.init(engine, .{
|
||||||
|
.capacity = @intCast(byte_length),
|
||||||
|
.target_queue = self.target_queue,
|
||||||
|
});
|
||||||
defer staging_buffer.deinit(engine);
|
defer staging_buffer.deinit(engine);
|
||||||
|
|
||||||
|
const staging_memory = try staging_buffer.map(engine);
|
||||||
|
@memcpy(staging_memory, data);
|
||||||
|
staging_buffer.unmap(engine);
|
||||||
|
|
||||||
|
// --- TRANSITION TO TRANSFER_DST_OPTIMAL AND COPY -----------------
|
||||||
|
|
||||||
const transfer_command_buffer = try engine.allocateTransferCommandBuffer();
|
const transfer_command_buffer = try engine.allocateTransferCommandBuffer();
|
||||||
defer engine.freeTransferCommandBuffer(transfer_command_buffer);
|
defer engine.freeTransferCommandBuffer(transfer_command_buffer);
|
||||||
|
|
||||||
const graphics_command_buffer = try engine.allocateGraphicsCommandBuffer();
|
|
||||||
defer engine.freeGraphicsCommandBuffer(graphics_command_buffer);
|
|
||||||
|
|
||||||
try transfer_command_buffer.beginCommandBuffer(&.{ .flags = .{ .one_time_submit_bit = true } });
|
try transfer_command_buffer.beginCommandBuffer(&.{ .flags = .{ .one_time_submit_bit = true } });
|
||||||
|
|
||||||
const pre_copy_barriers = [_]vk.ImageMemoryBarrier{
|
const transfer_transition_barriers = [_]vk.ImageMemoryBarrier{
|
||||||
.{
|
.{
|
||||||
.src_access_mask = .{},
|
.src_access_mask = .{},
|
||||||
.dst_access_mask = .{ .transfer_write_bit = true },
|
.dst_access_mask = .{ .transfer_write_bit = true },
|
||||||
@@ -184,8 +210,8 @@ pub fn write(self: Texture, comptime T: type, engine: *Engine, data: []const T)
|
|||||||
null,
|
null,
|
||||||
0,
|
0,
|
||||||
null,
|
null,
|
||||||
pre_copy_barriers.len,
|
transfer_transition_barriers.len,
|
||||||
&pre_copy_barriers,
|
&transfer_transition_barriers,
|
||||||
);
|
);
|
||||||
|
|
||||||
const regions = [_]vk.BufferImageCopy{
|
const regions = [_]vk.BufferImageCopy{
|
||||||
@@ -213,14 +239,28 @@ pub fn write(self: Texture, comptime T: type, engine: *Engine, data: []const T)
|
|||||||
);
|
);
|
||||||
|
|
||||||
try transfer_command_buffer.endCommandBuffer();
|
try transfer_command_buffer.endCommandBuffer();
|
||||||
try engine.submitTransferCommandBuffer(transfer_command_buffer, fence);
|
|
||||||
|
|
||||||
_ = try engine.device.waitForFences(1, @ptrCast(&fence), .true, std.math.maxInt(u64));
|
const semaphore = try engine.createSemaphore();
|
||||||
try engine.device.resetFences(1, @ptrCast(&fence));
|
defer engine.destroySemaphore(semaphore);
|
||||||
|
|
||||||
|
const transfer_submits = [_]vk.SubmitInfo{
|
||||||
|
.{
|
||||||
|
.command_buffer_count = 1,
|
||||||
|
.p_command_buffers = @ptrCast(&transfer_command_buffer.handle),
|
||||||
|
.signal_semaphore_count = 1,
|
||||||
|
.p_signal_semaphores = @ptrCast(&semaphore),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
try engine.device.queueSubmit(engine.transfer_queue.handle, transfer_submits.len, &transfer_submits, .null_handle);
|
||||||
|
|
||||||
|
// --- TRANSITION TO SHADER_READ_ONLY_OPTIMAL ----------------------
|
||||||
|
|
||||||
|
const graphics_command_buffer = try engine.allocateGraphicsCommandBuffer();
|
||||||
|
defer engine.freeGraphicsCommandBuffer(graphics_command_buffer);
|
||||||
|
|
||||||
try graphics_command_buffer.beginCommandBuffer(&.{ .flags = .{ .one_time_submit_bit = true } });
|
try graphics_command_buffer.beginCommandBuffer(&.{ .flags = .{ .one_time_submit_bit = true } });
|
||||||
|
|
||||||
const post_copy_barriers = [_]vk.ImageMemoryBarrier{
|
const graphics_transition_barriers = [_]vk.ImageMemoryBarrier{
|
||||||
.{
|
.{
|
||||||
.src_access_mask = .{ .transfer_write_bit = true },
|
.src_access_mask = .{ .transfer_write_bit = true },
|
||||||
.dst_access_mask = .{ .shader_read_bit = true },
|
.dst_access_mask = .{ .shader_read_bit = true },
|
||||||
@@ -247,12 +287,29 @@ pub fn write(self: Texture, comptime T: type, engine: *Engine, data: []const T)
|
|||||||
null,
|
null,
|
||||||
0,
|
0,
|
||||||
null,
|
null,
|
||||||
post_copy_barriers.len,
|
graphics_transition_barriers.len,
|
||||||
&post_copy_barriers,
|
&graphics_transition_barriers,
|
||||||
);
|
);
|
||||||
|
|
||||||
try graphics_command_buffer.endCommandBuffer();
|
try graphics_command_buffer.endCommandBuffer();
|
||||||
try engine.submitGraphicsCommandBuffer(graphics_command_buffer, fence);
|
|
||||||
|
|
||||||
_ = try engine.device.waitForFences(1, @ptrCast(&fence), .true, std.math.maxInt(u64));
|
const wait_stage_masks = [_]vk.PipelineStageFlags{
|
||||||
|
.{ .top_of_pipe_bit = true },
|
||||||
|
};
|
||||||
|
|
||||||
|
const graphics_submits = [_]vk.SubmitInfo{
|
||||||
|
.{
|
||||||
|
.command_buffer_count = 1,
|
||||||
|
.p_command_buffers = @ptrCast(&graphics_command_buffer.handle),
|
||||||
|
.wait_semaphore_count = 1,
|
||||||
|
.p_wait_semaphores = @ptrCast(&semaphore),
|
||||||
|
.p_wait_dst_stage_mask = &wait_stage_masks,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const fence = try engine.createFence(.{});
|
||||||
|
defer engine.destroyFence(fence);
|
||||||
|
|
||||||
|
try engine.device.queueSubmit(engine.graphics_queue.handle, graphics_submits.len, &graphics_submits, fence);
|
||||||
|
try engine.waitForFence(fence);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,89 +0,0 @@
|
|||||||
const VertexBuffer = @This();
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const vk = @import("vulkan");
|
|
||||||
|
|
||||||
const Engine = @import("Engine.zig");
|
|
||||||
const StagingBuffer = @import("StagingBuffer.zig");
|
|
||||||
const QSM = @import("QueueSharingMode.zig");
|
|
||||||
|
|
||||||
buffer: vk.Buffer,
|
|
||||||
memory: vk.DeviceMemory,
|
|
||||||
vertex_size: usize,
|
|
||||||
vertex_count: usize,
|
|
||||||
|
|
||||||
pub fn init(engine: *Engine, comptime VertexType: type, vertex_count: usize) !VertexBuffer {
|
|
||||||
const vertex_size = @sizeOf(VertexType);
|
|
||||||
const size = std.math.mul(usize, vertex_count, vertex_size) catch return error.OutOfMemory;
|
|
||||||
|
|
||||||
const qsm = QSM.resolve(engine.graphics_queue.allocation.family, engine.transfer_queue.allocation.family);
|
|
||||||
const buffer = try engine.device.createBuffer(&.{
|
|
||||||
.size = size,
|
|
||||||
.usage = .{
|
|
||||||
.transfer_dst_bit = true,
|
|
||||||
.vertex_buffer_bit = true,
|
|
||||||
},
|
|
||||||
.sharing_mode = qsm.sharing_mode,
|
|
||||||
.queue_family_index_count = qsm.queue_family_index_count,
|
|
||||||
.p_queue_family_indices = qsm.p_queue_family_indices,
|
|
||||||
}, &engine.vk_allocator.interface);
|
|
||||||
errdefer engine.device.destroyBuffer(buffer, &engine.vk_allocator.interface);
|
|
||||||
|
|
||||||
const memory_requirements = engine.device.getBufferMemoryRequirements(buffer);
|
|
||||||
const memory = try engine.allocate(memory_requirements, .{ .device_local_bit = true });
|
|
||||||
errdefer engine.device.freeMemory(memory, &engine.vk_allocator.interface);
|
|
||||||
|
|
||||||
try engine.device.bindBufferMemory(buffer, memory, 0);
|
|
||||||
|
|
||||||
return .{
|
|
||||||
.buffer = buffer,
|
|
||||||
.memory = memory,
|
|
||||||
.vertex_size = vertex_size,
|
|
||||||
.vertex_count = vertex_count,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(self: *VertexBuffer, engine: *Engine) void {
|
|
||||||
engine.device.freeMemory(self.memory, &engine.vk_allocator.interface);
|
|
||||||
engine.device.destroyBuffer(self.buffer, &engine.vk_allocator.interface);
|
|
||||||
|
|
||||||
self.* = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write(self: VertexBuffer, comptime VertexType: type, engine: *Engine, vertices: []const VertexType) !void {
|
|
||||||
std.debug.assert(vertices.len == self.vertex_count);
|
|
||||||
std.debug.assert(@sizeOf(VertexType) == self.vertex_size);
|
|
||||||
|
|
||||||
const fence = try engine.device.createFence(&.{}, &engine.vk_allocator.interface);
|
|
||||||
defer engine.device.destroyFence(fence, &engine.vk_allocator.interface);
|
|
||||||
|
|
||||||
const size = std.mem.sliceAsBytes(vertices).len;
|
|
||||||
|
|
||||||
var staging_buffer: StagingBuffer = try .init(engine, std.mem.sliceAsBytes(vertices), engine.graphics_queue.allocation.family);
|
|
||||||
defer staging_buffer.deinit(engine);
|
|
||||||
|
|
||||||
const command_buffer = try engine.allocateTransferCommandBuffer();
|
|
||||||
defer engine.freeTransferCommandBuffer(command_buffer);
|
|
||||||
|
|
||||||
try command_buffer.beginCommandBuffer(&.{ .flags = .{ .one_time_submit_bit = true } });
|
|
||||||
|
|
||||||
const regions = [_]vk.BufferCopy{
|
|
||||||
.{
|
|
||||||
.src_offset = 0,
|
|
||||||
.dst_offset = 0,
|
|
||||||
.size = size,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
command_buffer.copyBuffer(
|
|
||||||
staging_buffer.buffer,
|
|
||||||
self.buffer,
|
|
||||||
regions.len,
|
|
||||||
®ions,
|
|
||||||
);
|
|
||||||
|
|
||||||
try command_buffer.endCommandBuffer();
|
|
||||||
try engine.submitTransferCommandBuffer(command_buffer, fence);
|
|
||||||
|
|
||||||
_ = try engine.device.waitForFences(1, @ptrCast(&fence), .true, std.math.maxInt(u64));
|
|
||||||
}
|
|
||||||
75
src/engine/atoms.zig
Normal file
75
src/engine/atoms.zig
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
pub const Atoms = @This();
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
var allocator: std.mem.Allocator = undefined;
|
||||||
|
var string_arena: std.heap.ArenaAllocator = undefined;
|
||||||
|
var map: Map = undefined;
|
||||||
|
var array: Array = undefined;
|
||||||
|
var mutex: std.Thread.Mutex = undefined;
|
||||||
|
|
||||||
|
pub const Atom = enum(u32) { _ };
|
||||||
|
|
||||||
|
pub const Map = std.StringHashMapUnmanaged(Atom);
|
||||||
|
pub const Array = std.ArrayList([:0]const u8);
|
||||||
|
|
||||||
|
pub fn init(_allocator: std.mem.Allocator) void {
|
||||||
|
allocator = _allocator;
|
||||||
|
string_arena = .init(_allocator);
|
||||||
|
map = .{};
|
||||||
|
array = .empty;
|
||||||
|
mutex = .{};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit() void {
|
||||||
|
string_arena.deinit();
|
||||||
|
map.deinit(allocator);
|
||||||
|
array.deinit(allocator);
|
||||||
|
|
||||||
|
allocator = undefined;
|
||||||
|
string_arena = undefined;
|
||||||
|
map = undefined;
|
||||||
|
array = undefined;
|
||||||
|
mutex = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getString(atom: Atom) [:0]const u8 {
|
||||||
|
try mutex.lock();
|
||||||
|
defer mutex.unlock();
|
||||||
|
|
||||||
|
return array.items[atom.value];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getAtom(string: []const u8) ?Atom {
|
||||||
|
mutex.lock();
|
||||||
|
defer mutex.unlock();
|
||||||
|
|
||||||
|
return map.get(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getOrPutAtom(string: []const u8) !Atom {
|
||||||
|
mutex.lock();
|
||||||
|
defer mutex.unlock();
|
||||||
|
|
||||||
|
const entry = try map.getOrPut(allocator, string);
|
||||||
|
|
||||||
|
if (entry.found_existing) {
|
||||||
|
return entry.value_ptr.*;
|
||||||
|
} else {
|
||||||
|
errdefer _ = map.remove(string);
|
||||||
|
try array.ensureUnusedCapacity(allocator, 1);
|
||||||
|
|
||||||
|
const owned_string = try toOwnedString(string);
|
||||||
|
const atom: Atom = @enumFromInt(array.items.len);
|
||||||
|
|
||||||
|
entry.key_ptr.* = owned_string;
|
||||||
|
entry.value_ptr.* = atom;
|
||||||
|
|
||||||
|
array.appendAssumeCapacity(owned_string);
|
||||||
|
return atom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn toOwnedString(string: []const u8) ![:0]const u8 {
|
||||||
|
const owned_string = try string_arena.allocator().dupeZ(u8, string);
|
||||||
|
return owned_string;
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ const vk = @import("vulkan");
|
|||||||
|
|
||||||
const c = @import("const.zig");
|
const c = @import("const.zig");
|
||||||
|
|
||||||
|
const atoms = @import("engine/atoms.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,6 +26,9 @@ pub fn main() !void {
|
|||||||
allocator = gpa.allocator();
|
allocator = gpa.allocator();
|
||||||
temp_allocator = fba.threadSafeAllocator();
|
temp_allocator = fba.threadSafeAllocator();
|
||||||
|
|
||||||
|
atoms.init(allocator);
|
||||||
|
defer atoms.deinit();
|
||||||
|
|
||||||
stbi.init(allocator);
|
stbi.init(allocator);
|
||||||
defer stbi.deinit();
|
defer stbi.deinit();
|
||||||
|
|
||||||
@@ -52,9 +56,9 @@ pub fn main() !void {
|
|||||||
defer engine.deinit();
|
defer engine.deinit();
|
||||||
|
|
||||||
var swapchain = try Swapchain.init(&engine);
|
var swapchain = try Swapchain.init(&engine);
|
||||||
defer swapchain.deinit();
|
defer swapchain.deinit(&engine);
|
||||||
|
|
||||||
var game = try Game.init(allocator, &swapchain);
|
var game = try Game.init(allocator, &engine, &swapchain);
|
||||||
defer game.deinit();
|
defer game.deinit();
|
||||||
|
|
||||||
var t1 = glfw.getTime();
|
var t1 = glfw.getTime();
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
const Vector3 = @import("Vector3.zig").Vector3;
|
const Vector3 = @import("Vector3.zig").Vector3;
|
||||||
const Vector4 = @import("Vector3.zig").Vector4;
|
const Vector4 = @import("Vector3.zig").Vector4;
|
||||||
|
|
||||||
@@ -28,6 +30,11 @@ pub const Vector2 = extern struct {
|
|||||||
return self.vector;
|
return self.vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub inline fn asArrayNorm(self: Vector2, comptime T: type) [2]T {
|
||||||
|
const scale_vector: Vector = @splat(std.math.maxInt(T));
|
||||||
|
return @as(@Vector(2, T), @intFromFloat(@round(self.vector * scale_vector)));
|
||||||
|
}
|
||||||
|
|
||||||
pub inline fn asVector3(self: Vector2, z: f32) Vector3 {
|
pub inline fn asVector3(self: Vector2, z: f32) Vector3 {
|
||||||
const z_vector: @Vector(3, f32) = .{ undefined, undefined, z };
|
const z_vector: @Vector(3, f32) = .{ undefined, undefined, z };
|
||||||
return .{ .vector = @shuffle(f32, self.vector, z_vector, [_]i32{ 0, 1, ~@as(i32, 2) }) };
|
return .{ .vector = @shuffle(f32, self.vector, z_vector, [_]i32{ 0, 1, ~@as(i32, 2) }) };
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
const Quaternion = @import("Quaternion.zig").Quaternion;
|
const Quaternion = @import("Quaternion.zig").Quaternion;
|
||||||
const Vector2 = @import("Vector2.zig").Vector2;
|
const Vector2 = @import("Vector2.zig").Vector2;
|
||||||
const Vector4 = @import("Vector4.zig").Vector4;
|
const Vector4 = @import("Vector4.zig").Vector4;
|
||||||
@@ -30,6 +32,11 @@ pub const Vector3 = extern struct {
|
|||||||
return self.vector;
|
return self.vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub inline fn asArrayNorm(self: Vector3, comptime T: type) [3]T {
|
||||||
|
const scale_vector: Vector = @splat(std.math.maxInt(T));
|
||||||
|
return @as(@Vector(3, T), @intFromFloat(@round(self.vector * scale_vector)));
|
||||||
|
}
|
||||||
|
|
||||||
pub inline fn asVector2(self: Vector3) Vector2 {
|
pub inline fn asVector2(self: Vector3) Vector2 {
|
||||||
return .{ .vector = @shuffle(f32, self.vector, undefined, [_]i32{ 0, 1 }) };
|
return .{ .vector = @shuffle(f32, self.vector, undefined, [_]i32{ 0, 1 }) };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
const Vector2 = @import("Vector2.zig").Vector2;
|
const Vector2 = @import("Vector2.zig").Vector2;
|
||||||
const Vector3 = @import("Vector3.zig").Vector3;
|
const Vector3 = @import("Vector3.zig").Vector3;
|
||||||
|
|
||||||
@@ -30,6 +32,11 @@ pub const Vector4 = extern struct {
|
|||||||
return self.vector;
|
return self.vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub inline fn asArrayNorm(self: Vector4, comptime T: type) [4]T {
|
||||||
|
const scale_vector: Vector = @splat(std.math.maxInt(T));
|
||||||
|
return @as(@Vector(4, T), @intFromFloat(@round(self.vector * scale_vector)));
|
||||||
|
}
|
||||||
|
|
||||||
pub inline fn asVector2(self: Vector4) Vector2 {
|
pub inline fn asVector2(self: Vector4) Vector2 {
|
||||||
return .{ .vector = @shuffle(f32, self.vector, undefined, [_]i32{ 0, 1 }) };
|
return .{ .vector = @shuffle(f32, self.vector, undefined, [_]i32{ 0, 1 }) };
|
||||||
}
|
}
|
||||||
|
|||||||
23
src/voxel.zig
Normal file
23
src/voxel.zig
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
pub const Orientation = enum(u4) {
|
||||||
|
negative_x,
|
||||||
|
positive_x,
|
||||||
|
negative_y,
|
||||||
|
positive_y,
|
||||||
|
negative_z,
|
||||||
|
positive_z,
|
||||||
|
};
|
||||||
|
|
||||||
|
// ┌────────────────── x
|
||||||
|
// │ ┌────────────── y
|
||||||
|
// │ │ ┌───────── z
|
||||||
|
// │ │ │ ┌───── orientation
|
||||||
|
// │ │ │ │ ┌ material
|
||||||
|
// ┌┴─┐┌┴─┐ ┌┴─┐┌┴─┐ ┌┴──────────────┐
|
||||||
|
// 10987654 32109876 54321098 76543210
|
||||||
|
pub const Wall = packed struct(u32) {
|
||||||
|
material: u16,
|
||||||
|
orientation: Orientation,
|
||||||
|
z: u4,
|
||||||
|
y: u4,
|
||||||
|
x: u4,
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user