Refactor literally everything
This commit is contained in:
@@ -1,13 +1,15 @@
|
||||
#version 460
|
||||
#extension GL_EXT_nonuniform_qualifier : require
|
||||
#extension GL_EXT_scalar_block_layout : require
|
||||
#extension GL_EXT_shader_16bit_storage : require
|
||||
|
||||
in Varyings {
|
||||
layout(location = 0) vec3 positionVS;
|
||||
layout(location = 1) vec2 texCoord;
|
||||
layout(location = 2) vec3 normalVS;
|
||||
layout(location = 3) vec3 tangentVS;
|
||||
layout(location = 4) vec3 bitangentVS;
|
||||
layout(location = 0) flat uint instance;
|
||||
layout(location = 1) vec3 positionVS;
|
||||
layout(location = 2) vec2 texCoord;
|
||||
layout(location = 3) vec3 normalVS;
|
||||
layout(location = 4) vec3 tangentVS;
|
||||
layout(location = 5) vec3 bitangentVS;
|
||||
} var;
|
||||
|
||||
#include "main_common.glsl"
|
||||
@@ -78,13 +80,14 @@ vec4 texture2DAA(texture2D tex, vec2 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() {
|
||||
vec4 baseColorTexel = texture2DAA(_Textures[MATERIAL.baseColorTexture], var.texCoord);
|
||||
vec4 occlusionRoughnessMetallicTexel = texture2DAA(_Textures[MATERIAL.occlusionRoughnessMetallicTexture], var.texCoord);
|
||||
vec4 normalTexel = texture2DAA(_Textures[MATERIAL.normalTexture], var.texCoord);
|
||||
vec4 emissiveTexel = texture2DAA(_Textures[MATERIAL.emissiveTexture], var.texCoord);
|
||||
vec4 baseColorTexel = texture2DAA(_Textures[uint(MATERIAL.baseColorTexture)], var.texCoord);
|
||||
vec4 occlusionRoughnessMetallicTexel = texture2DAA(_Textures[uint(MATERIAL.occlusionRoughnessMetallicTexture)], var.texCoord);
|
||||
vec4 normalTexel = texture2DAA(_Textures[uint(MATERIAL.normalTexture)], var.texCoord);
|
||||
vec4 emissiveTexel = texture2DAA(_Textures[uint(MATERIAL.emissiveTexture)], var.texCoord);
|
||||
|
||||
vec3 baseColor = MATERIAL.baseColor * baseColorTexel.rgb;
|
||||
float occlusion = 1.0 + MATERIAL.occlusionTextureStrength * (occlusionRoughnessMetallicTexel.r - 1.0);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#version 460
|
||||
#extension GL_EXT_nonuniform_qualifier : require
|
||||
#extension GL_EXT_scalar_block_layout : require
|
||||
#extension GL_EXT_shader_16bit_storage : require
|
||||
|
||||
layout(location = 0) in vec3 positionOS;
|
||||
layout(location = 1) in vec2 texCoord;
|
||||
@@ -8,29 +9,33 @@ layout(location = 2) in vec3 normalOS;
|
||||
layout(location = 3) in vec4 tangentOS;
|
||||
|
||||
out Varyings {
|
||||
layout(location = 0) vec3 positionVS;
|
||||
layout(location = 1) vec2 texCoord;
|
||||
layout(location = 2) vec3 normalVS;
|
||||
layout(location = 3) vec3 tangentVS;
|
||||
layout(location = 4) vec3 bitangentVS;
|
||||
layout(location = 0) flat uint instance;
|
||||
layout(location = 1) vec3 positionVS;
|
||||
layout(location = 2) vec2 texCoord;
|
||||
layout(location = 3) vec3 normalVS;
|
||||
layout(location = 4) vec3 tangentVS;
|
||||
layout(location = 5) vec3 bitangentVS;
|
||||
} var;
|
||||
|
||||
#include "main_common.glsl"
|
||||
|
||||
#define OBJECT _Object[gl_InstanceIndex]
|
||||
|
||||
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;
|
||||
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 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 bitangentVS = tangentOS.w * normalize(cross(normalVS, tangentVS));
|
||||
|
||||
gl_Position = positionCS;
|
||||
var.instance = gl_InstanceIndex;
|
||||
var.positionVS = positionVS;
|
||||
var.texCoord = texCoord;
|
||||
var.normalVS = normalVS;
|
||||
|
||||
@@ -12,16 +12,24 @@ struct DirectionalLight {
|
||||
|
||||
struct Material {
|
||||
vec3 baseColor;
|
||||
uint baseColorTexture;
|
||||
vec3 emissive;
|
||||
uint emissiveTexture;
|
||||
float ior;
|
||||
float metallic;
|
||||
float normalScale;
|
||||
uint normalTexture;
|
||||
uint occlusionRoughnessMetallicTexture;
|
||||
float occlusionTextureStrength;
|
||||
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 {
|
||||
@@ -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 = 5) uniform texture2D _Textures[];
|
||||
|
||||
// --- SET 1 --- PER OBJECT ----------------------------------------------------
|
||||
// --- SET 1 --- PER BATCH -----------------------------------------------------
|
||||
|
||||
layout(set = 1, binding = 0, scalar) uniform ObjectUniforms {
|
||||
mat4 matrixOStoWS;
|
||||
mat4 matrixOStoWSNormal;
|
||||
|
||||
uint material;
|
||||
} _Object;
|
||||
layout(set = 1, binding = 0, scalar) readonly buffer ObjectsUniforms {
|
||||
ObjectUniforms _Object[];
|
||||
};
|
||||
|
||||
739
src/Game.zig
739
src/Game.zig
@@ -6,38 +6,118 @@ const vk = @import("vulkan");
|
||||
|
||||
const math = @import("math.zig");
|
||||
|
||||
const Atoms = @import("assets/Atoms.zig");
|
||||
const Textures = @import("assets/Textures.zig");
|
||||
const Materials = @import("assets/Materials.zig");
|
||||
const Textures = @import("assets/Textures.zig");
|
||||
const Engine = @import("engine/Engine.zig");
|
||||
const GenericBuffer = @import("engine/GenericBuffer.zig").GenericBuffer;
|
||||
const Swapchain = @import("engine/Swapchain.zig");
|
||||
const VertexBuffer = @import("engine/VertexBuffer.zig");
|
||||
const IndexBuffer = @import("engine/IndexBuffer.zig");
|
||||
const Iterator3 = math.Iterator3;
|
||||
const Matrix4x4 = math.Matrix4x4;
|
||||
const Quaternion = math.Quaternion;
|
||||
const Vector2 = math.Vector2;
|
||||
const Vector3 = math.Vector3;
|
||||
const Vector4 = math.Vector4;
|
||||
|
||||
const PointLight = extern struct {
|
||||
positionWS: [3]f32,
|
||||
color: [3]f32,
|
||||
|
||||
pub fn init(position_ws: Vector3, color: Vector3) PointLight {
|
||||
return .{
|
||||
.positionWS = position_ws.asArray(),
|
||||
.color = color.asArray(),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const DirectionalLight = extern struct {
|
||||
directionWS: [3]f32,
|
||||
color: [3]f32,
|
||||
|
||||
pub fn init(direction_ws: Vector3, color: Vector3) DirectionalLight {
|
||||
return .{
|
||||
.directionWS = direction_ws.asArray(),
|
||||
.color = color.asArray(),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const GlobalUniforms = extern struct {
|
||||
matrixWStoVS: [16]f32,
|
||||
matrixVStoCS: [16]f32,
|
||||
ambientLight: [3]f32,
|
||||
|
||||
pub fn init(matrix_ws_to_vs: Matrix4x4, matrix_vs_to_cs: Matrix4x4, ambient_light: Vector3) GlobalUniforms {
|
||||
return .{
|
||||
.matrixWStoVS = matrix_ws_to_vs.asArray(),
|
||||
.matrixVStoCS = matrix_vs_to_cs.asArray(),
|
||||
.ambientLight = ambient_light.asArray(),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const ObjectUniforms = extern struct {
|
||||
matrixOStoWS: [16]f32,
|
||||
matrixOStoWSNormal: [16]f32,
|
||||
|
||||
material: Materials.Id,
|
||||
|
||||
pub fn init(matrix_os_to_ws: Matrix4x4, matrix_ow_to_ws_normal: Matrix4x4, material: Materials.Id) ObjectUniforms {
|
||||
return .{
|
||||
.matrixOStoWS = matrix_os_to_ws.asArray(),
|
||||
.matrixOStoWSNormal = matrix_ow_to_ws_normal.asArray(),
|
||||
.material = material,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const Vertex = extern struct {
|
||||
positionOS: [3]f32,
|
||||
texCoord: [2]u16,
|
||||
normalOS: [3]i8,
|
||||
tangentOS: [4]i8,
|
||||
|
||||
pub fn init(position_os: Vector3, tex_coord: Vector2, normal_os: Vector3, tangent_os: Vector4) Vertex {
|
||||
return .{
|
||||
.positionOS = position_os.asArray(),
|
||||
.texCoord = tex_coord.asArrayNorm(u16),
|
||||
.normalOS = normal_os.asArrayNorm(i8),
|
||||
.tangentOS = tangent_os.asArrayNorm(i8),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const GlobalUniformsBuffer = GenericBuffer(GlobalUniforms, void);
|
||||
const PointLightBuffer = GenericBuffer(u32, PointLight);
|
||||
const DirectionalLightBuffer = GenericBuffer(u32, DirectionalLight);
|
||||
const ObjectUniformsBuffer = GenericBuffer(void, ObjectUniforms);
|
||||
|
||||
const VertexBuffer = GenericBuffer(void, Vertex);
|
||||
const IndexBuffer = GenericBuffer(void, u16);
|
||||
|
||||
const main_vert_spv align(4) = @embedFile("shaders/main_vert.spv").*;
|
||||
const main_frag_spv align(4) = @embedFile("shaders/main_frag.spv").*;
|
||||
|
||||
allocator: std.mem.Allocator,
|
||||
engine: *Engine,
|
||||
swapchain: *Swapchain,
|
||||
global_descriptor_set_layout: vk.DescriptorSetLayout,
|
||||
per_object_descriptor_set_layout: vk.DescriptorSetLayout,
|
||||
per_batch_descriptor_set_layout: vk.DescriptorSetLayout,
|
||||
descriptor_pool: vk.DescriptorPool,
|
||||
/// [0] GLOBAL, [1] PER OBJECT
|
||||
descriptor_sets: [2]vk.DescriptorSet,
|
||||
pipeline_layout: vk.PipelineLayout,
|
||||
pipeline: vk.Pipeline,
|
||||
|
||||
vertex_buffer: VertexBuffer,
|
||||
index_buffer: IndexBuffer,
|
||||
|
||||
atoms: Atoms,
|
||||
global_uniforms: GlobalUniformsBuffer,
|
||||
point_lights: PointLightBuffer,
|
||||
directional_lights: DirectionalLightBuffer,
|
||||
object_uniforms: ObjectUniformsBuffer,
|
||||
sampler: vk.Sampler,
|
||||
|
||||
materials: Materials,
|
||||
textures: Textures,
|
||||
|
||||
@@ -50,35 +130,51 @@ input_backwards: bool = false,
|
||||
input_left: bool = false,
|
||||
input_right: bool = false,
|
||||
|
||||
const max_textures = 1024;
|
||||
const max_point_lights = 1024;
|
||||
const max_directional_lights = 4;
|
||||
const max_objects = 1024;
|
||||
|
||||
const camera_near_plane = 0.1;
|
||||
const camera_vertical_fov_deg = 90.0;
|
||||
const camera_half_vertical_fov_rad = 0.5 * camera_vertical_fov_deg * std.math.rad_per_deg;
|
||||
const camera_mouse_sensitivity = 0.0015;
|
||||
|
||||
const max_point_lights = 100;
|
||||
const max_directional_lights = 4;
|
||||
|
||||
const player_speed = 5.0;
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator, swapchain: *Swapchain) !Game {
|
||||
var atoms = Atoms.init(allocator);
|
||||
errdefer atoms.deinit();
|
||||
pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain) !Game {
|
||||
var materials = try Materials.init(engine, allocator);
|
||||
errdefer materials.deinit(engine, allocator);
|
||||
|
||||
var materials = try Materials.init(allocator, swapchain.engine);
|
||||
errdefer materials.deinit(swapchain.engine);
|
||||
var textures = try Textures.init(engine, allocator);
|
||||
errdefer textures.deinit(engine, allocator);
|
||||
|
||||
var textures = try Textures.init(allocator, swapchain.engine);
|
||||
errdefer textures.deinit(swapchain.engine);
|
||||
_ = try materials.getOrLoadFilename(engine, &textures, "Bricks.json", allocator);
|
||||
_ = try materials.getOrLoadFilename(engine, &textures, "Dirt.json", allocator);
|
||||
_ = try materials.getOrLoadFilename(engine, &textures, "Gold.json", allocator);
|
||||
_ = try materials.getOrLoadFilename(engine, &textures, "Stone.json", allocator);
|
||||
|
||||
_ = try materials.getOrLoadId(swapchain.engine, &textures, &atoms, try atoms.getOrPutAtom("Bricks.json"));
|
||||
_ = try materials.getOrLoadId(swapchain.engine, &textures, &atoms, try atoms.getOrPutAtom("Dirt.json"));
|
||||
_ = try materials.getOrLoadId(swapchain.engine, &textures, &atoms, try atoms.getOrPutAtom("Gold.json"));
|
||||
_ = try materials.getOrLoadId(swapchain.engine, &textures, &atoms, try atoms.getOrPutAtom("Stone.json"));
|
||||
const sampler = try engine.createSampler(.{
|
||||
.mag_filter = .linear,
|
||||
.min_filter = .linear,
|
||||
.mipmap_mode = .linear,
|
||||
.address_mode_u = .repeat,
|
||||
.address_mode_v = .repeat,
|
||||
.address_mode_w = .repeat,
|
||||
.mip_lod_bias = 0,
|
||||
.anisotropy_enable = .false,
|
||||
.max_anisotropy = 0,
|
||||
.compare_enable = .false,
|
||||
.compare_op = .always,
|
||||
.min_lod = 0,
|
||||
.max_lod = vk.LOD_CLAMP_NONE,
|
||||
.border_color = .float_transparent_black,
|
||||
.unnormalized_coordinates = .false,
|
||||
});
|
||||
errdefer engine.destroySampler(sampler);
|
||||
|
||||
const device = swapchain.engine.device;
|
||||
const interface = &swapchain.engine.vk_allocator.interface;
|
||||
|
||||
const global_descriptor_set_layout_bindings = [_]vk.DescriptorSetLayoutBinding{
|
||||
const global_descriptor_set_layout = try engine.createDescriptorSetLayout(.{
|
||||
.bindings = &.{
|
||||
.{
|
||||
.binding = 0,
|
||||
.descriptor_type = .uniform_buffer,
|
||||
@@ -108,129 +204,139 @@ pub fn init(allocator: std.mem.Allocator, swapchain: *Swapchain) !Game {
|
||||
.descriptor_type = .sampler,
|
||||
.descriptor_count = 1,
|
||||
.stage_flags = .{ .vertex_bit = true, .fragment_bit = true },
|
||||
.immutable_samplers = &.{sampler},
|
||||
},
|
||||
.{
|
||||
.binding = 5,
|
||||
.descriptor_type = .sampled_image,
|
||||
.descriptor_count = 1024,
|
||||
.descriptor_count = max_textures,
|
||||
.stage_flags = .{ .vertex_bit = true, .fragment_bit = true },
|
||||
.flags = .{ .variable_descriptor_count_bit = true },
|
||||
},
|
||||
};
|
||||
|
||||
const global_descriptor_set_layout_bindings_flags = [_]vk.DescriptorBindingFlags{
|
||||
.{},
|
||||
.{},
|
||||
.{},
|
||||
.{},
|
||||
.{},
|
||||
.{ .variable_descriptor_count_bit = true },
|
||||
};
|
||||
|
||||
std.debug.assert(global_descriptor_set_layout_bindings.len == global_descriptor_set_layout_bindings_flags.len);
|
||||
|
||||
const global_descriptor_set_layout = try device.createDescriptorSetLayout(&.{
|
||||
.p_next = &vk.DescriptorSetLayoutBindingFlagsCreateInfo{
|
||||
.binding_count = global_descriptor_set_layout_bindings_flags.len,
|
||||
.p_binding_flags = &global_descriptor_set_layout_bindings_flags,
|
||||
},
|
||||
.binding_count = global_descriptor_set_layout_bindings.len,
|
||||
.p_bindings = &global_descriptor_set_layout_bindings,
|
||||
}, interface);
|
||||
errdefer device.destroyDescriptorSetLayout(global_descriptor_set_layout, interface);
|
||||
});
|
||||
errdefer engine.destroyDescriptorSetLayout(global_descriptor_set_layout);
|
||||
|
||||
const per_object_descriptor_set_layout_bindings = [_]vk.DescriptorSetLayoutBinding{
|
||||
const per_batch_descriptor_set_layout = try engine.createDescriptorSetLayout(.{
|
||||
.bindings = &.{
|
||||
.{
|
||||
.binding = 0,
|
||||
.descriptor_type = .uniform_buffer_dynamic,
|
||||
.descriptor_type = .storage_buffer,
|
||||
.descriptor_count = 1,
|
||||
.stage_flags = .{ .vertex_bit = true, .fragment_bit = true },
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
errdefer engine.destroyDescriptorSetLayout(per_batch_descriptor_set_layout);
|
||||
|
||||
const per_object_descriptor_set_layout = try device.createDescriptorSetLayout(&.{
|
||||
.binding_count = per_object_descriptor_set_layout_bindings.len,
|
||||
.p_bindings = &per_object_descriptor_set_layout_bindings,
|
||||
}, interface);
|
||||
errdefer device.destroyDescriptorSetLayout(per_object_descriptor_set_layout, interface);
|
||||
|
||||
const descriptor_set_layouts = [_]vk.DescriptorSetLayout{
|
||||
const pipeline_layout = try engine.createPipelineLayout(.{
|
||||
.set_layouts = &.{
|
||||
global_descriptor_set_layout,
|
||||
per_object_descriptor_set_layout,
|
||||
};
|
||||
|
||||
const pipeline_layout = try device.createPipelineLayout(&.{
|
||||
.set_layout_count = descriptor_set_layouts.len,
|
||||
.p_set_layouts = &descriptor_set_layouts,
|
||||
}, interface);
|
||||
errdefer device.destroyPipelineLayout(pipeline_layout, interface);
|
||||
|
||||
const vertex_shader = try device.createShaderModule(&.{
|
||||
.code_size = main_vert_spv.len,
|
||||
.p_code = @ptrCast(&main_vert_spv),
|
||||
}, interface);
|
||||
defer device.destroyShaderModule(vertex_shader, interface);
|
||||
|
||||
const fragment_shader = try device.createShaderModule(&.{
|
||||
.code_size = main_frag_spv.len,
|
||||
.p_code = @ptrCast(&main_frag_spv),
|
||||
}, interface);
|
||||
defer device.destroyShaderModule(fragment_shader, interface);
|
||||
|
||||
var vertex_buffer: VertexBuffer = try .init(swapchain.engine, Vertex, 4);
|
||||
errdefer vertex_buffer.deinit(swapchain.engine);
|
||||
try vertex_buffer.write(Vertex, swapchain.engine, &.{
|
||||
.{
|
||||
.positionOS = .{ -0.5, -0.5, 0 },
|
||||
.texCoord = .{ 0, 65535 },
|
||||
.normalOS = .{ 127, 0, 0 },
|
||||
.tangentOS = .{ 127, 0, 0, -127 },
|
||||
per_batch_descriptor_set_layout,
|
||||
},
|
||||
.{
|
||||
.positionOS = .{ 0.5, -0.5, 0 },
|
||||
.texCoord = .{ 65535, 65535 },
|
||||
.normalOS = .{ 127, 0, 0 },
|
||||
.tangentOS = .{ 127, 0, 0, -127 },
|
||||
},
|
||||
.{
|
||||
.positionOS = .{ -0.5, 0.5, 0 },
|
||||
.texCoord = .{ 0, 0 },
|
||||
.normalOS = .{ 127, 0, 0 },
|
||||
.tangentOS = .{ 127, 0, 0, -127 },
|
||||
},
|
||||
.{
|
||||
.positionOS = .{ 0.5, 0.5, 0 },
|
||||
.texCoord = .{ 65535, 0 },
|
||||
.normalOS = .{ 127, 0, 0 },
|
||||
.tangentOS = .{ 127, 0, 0, -127 },
|
||||
});
|
||||
errdefer engine.destroyPipelineLayout(pipeline_layout);
|
||||
|
||||
const vertex_shader = try engine.createShaderModule(.{ .code = &main_vert_spv });
|
||||
defer engine.destroyShaderModule(vertex_shader);
|
||||
|
||||
const fragment_shader = try engine.createShaderModule(.{ .code = &main_frag_spv });
|
||||
defer engine.destroyShaderModule(fragment_shader);
|
||||
|
||||
var vertex_buffer = try VertexBuffer.init(engine, .{
|
||||
.usage = .vertex,
|
||||
.target_queue = .graphics,
|
||||
.array_capacity = 4,
|
||||
});
|
||||
errdefer vertex_buffer.deinit(engine);
|
||||
try vertex_buffer.write(engine, .{
|
||||
.elements = &.{
|
||||
.init(
|
||||
.init(-0.5, -0.5, 0),
|
||||
.init(0, 1),
|
||||
.init(1, 0, 0),
|
||||
.init(1, 0, 0, -1),
|
||||
),
|
||||
.init(
|
||||
.init(0.5, -0.5, 0),
|
||||
.init(1, 1),
|
||||
.init(1, 0, 0),
|
||||
.init(1, 0, 0, -1),
|
||||
),
|
||||
.init(
|
||||
.init(-0.5, 0.5, 0),
|
||||
.init(0, 0),
|
||||
.init(1, 0, 0),
|
||||
.init(1, 0, 0, -1),
|
||||
),
|
||||
.init(
|
||||
.init(0.5, 0.5, 0),
|
||||
.init(1, 0),
|
||||
.init(1, 0, 0),
|
||||
.init(1, 0, 0, -1),
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
var index_buffer: IndexBuffer = try .init(swapchain.engine, 6);
|
||||
errdefer index_buffer.deinit(swapchain.engine);
|
||||
try index_buffer.write(swapchain.engine, &.{ 0, 1, 2, 2, 1, 3 });
|
||||
var index_buffer = try IndexBuffer.init(engine, .{
|
||||
.usage = .index,
|
||||
.target_queue = .graphics,
|
||||
.array_capacity = 6,
|
||||
});
|
||||
errdefer index_buffer.deinit(engine);
|
||||
try index_buffer.write(engine, .{
|
||||
.elements = &.{ 0, 1, 2, 2, 1, 3 },
|
||||
});
|
||||
|
||||
const pipeline_shader_stages = [_]vk.PipelineShaderStageCreateInfo{
|
||||
var global_uniforms = try GlobalUniformsBuffer.init(engine, .{
|
||||
.usage = .uniform,
|
||||
.target_queue = .graphics,
|
||||
});
|
||||
errdefer global_uniforms.deinit(engine);
|
||||
|
||||
var point_lights = try PointLightBuffer.init(engine, .{
|
||||
.usage = .storage,
|
||||
.target_queue = .graphics,
|
||||
.array_capacity = max_point_lights,
|
||||
});
|
||||
errdefer point_lights.deinit(engine);
|
||||
|
||||
var directional_lights = try DirectionalLightBuffer.init(engine, .{
|
||||
.usage = .storage,
|
||||
.target_queue = .graphics,
|
||||
.array_capacity = max_directional_lights,
|
||||
});
|
||||
errdefer directional_lights.deinit(engine);
|
||||
|
||||
var object_uniforms = try ObjectUniformsBuffer.init(engine, .{
|
||||
.usage = .storage,
|
||||
.target_queue = .graphics,
|
||||
.array_capacity = max_objects,
|
||||
});
|
||||
errdefer object_uniforms.deinit(engine);
|
||||
|
||||
const pipeline = try engine.createGraphicsPipeline(.{
|
||||
.stages = &.{
|
||||
.{
|
||||
.stage = .{ .vertex_bit = true },
|
||||
.module = vertex_shader,
|
||||
.p_name = "main",
|
||||
.name = "main",
|
||||
},
|
||||
.{
|
||||
.stage = .{ .fragment_bit = true },
|
||||
.module = fragment_shader,
|
||||
.p_name = "main",
|
||||
.name = "main",
|
||||
},
|
||||
};
|
||||
|
||||
const vertex_input_binding_descriptions = [_]vk.VertexInputBindingDescription{
|
||||
},
|
||||
.vertex_input_state = .{
|
||||
.vertex_binding_descriptions = &.{
|
||||
.{
|
||||
.binding = 0,
|
||||
.stride = @sizeOf(Vertex),
|
||||
.input_rate = .vertex,
|
||||
},
|
||||
};
|
||||
|
||||
const vertex_input_attribute_descriptions = [_]vk.VertexInputAttributeDescription{
|
||||
},
|
||||
.vertex_attribute_descriptions = &.{
|
||||
.{
|
||||
.location = 0,
|
||||
.binding = 0,
|
||||
@@ -255,9 +361,39 @@ pub fn init(allocator: std.mem.Allocator, swapchain: *Swapchain) !Game {
|
||||
.format = .r8g8b8a8_snorm,
|
||||
.offset = @offsetOf(Vertex, "tangentOS"),
|
||||
},
|
||||
};
|
||||
|
||||
const pipeline_color_blend_attachment_states = [_]vk.PipelineColorBlendAttachmentState{
|
||||
},
|
||||
},
|
||||
.input_assembly_state = .{
|
||||
.topology = .triangle_list,
|
||||
.primitive_restart_enable = .false,
|
||||
},
|
||||
.viewport_state = .{
|
||||
.viewports = &.{undefined}, // dynamic
|
||||
.scissors = &.{undefined}, // dynamic
|
||||
},
|
||||
.rasterization_state = .{
|
||||
.depth_clamp_enable = .false,
|
||||
.rasterizer_discard_enable = .false,
|
||||
.polygon_mode = .fill,
|
||||
.cull_mode = .{ .back_bit = true },
|
||||
.front_face = .counter_clockwise,
|
||||
.depth_bias_enable = .false,
|
||||
.depth_bias_constant_factor = 0,
|
||||
.depth_bias_clamp = 0,
|
||||
.depth_bias_slope_factor = 0,
|
||||
.line_width = 1,
|
||||
},
|
||||
.multisample_state = .{
|
||||
.rasterization_samples = .{ .@"1_bit" = true },
|
||||
.sample_shading_enable = .false,
|
||||
.min_sample_shading = 1,
|
||||
.alpha_to_coverage_enable = .false,
|
||||
.alpha_to_one_enable = .false,
|
||||
},
|
||||
.color_blend_state = .{
|
||||
.logic_op_enable = .false,
|
||||
.logic_op = .copy,
|
||||
.attachments = &.{
|
||||
.{
|
||||
.blend_enable = .false,
|
||||
.src_color_blend_factor = .one,
|
||||
@@ -273,100 +409,189 @@ pub fn init(allocator: std.mem.Allocator, swapchain: *Swapchain) !Game {
|
||||
.a_bit = true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const dynamic_states = [_]vk.DynamicState{ .viewport, .scissor };
|
||||
|
||||
var pipeline: vk.Pipeline = undefined;
|
||||
_ = try device.createGraphicsPipelines(.null_handle, 1, &.{
|
||||
.{
|
||||
.stage_count = pipeline_shader_stages.len,
|
||||
.p_stages = &pipeline_shader_stages,
|
||||
.p_vertex_input_state = &.{
|
||||
.vertex_binding_description_count = vertex_input_binding_descriptions.len,
|
||||
.p_vertex_binding_descriptions = &vertex_input_binding_descriptions,
|
||||
.vertex_attribute_description_count = vertex_input_attribute_descriptions.len,
|
||||
.p_vertex_attribute_descriptions = &vertex_input_attribute_descriptions,
|
||||
},
|
||||
.p_input_assembly_state = &.{
|
||||
.topology = .triangle_list,
|
||||
.primitive_restart_enable = .false,
|
||||
},
|
||||
.p_viewport_state = &.{
|
||||
.viewport_count = 1,
|
||||
.p_viewports = undefined,
|
||||
.scissor_count = 1,
|
||||
.p_scissors = undefined,
|
||||
},
|
||||
.p_rasterization_state = &.{
|
||||
.depth_clamp_enable = .false,
|
||||
.rasterizer_discard_enable = .false,
|
||||
.polygon_mode = .fill,
|
||||
.cull_mode = .{ .back_bit = true },
|
||||
.front_face = .counter_clockwise,
|
||||
.depth_bias_enable = .false,
|
||||
.depth_bias_constant_factor = 0,
|
||||
.depth_bias_clamp = 0,
|
||||
.depth_bias_slope_factor = 0,
|
||||
.line_width = 1,
|
||||
},
|
||||
.p_multisample_state = &.{
|
||||
.rasterization_samples = .{ .@"1_bit" = true },
|
||||
.sample_shading_enable = .false,
|
||||
.min_sample_shading = 1,
|
||||
.alpha_to_coverage_enable = .false,
|
||||
.alpha_to_one_enable = .false,
|
||||
},
|
||||
.p_depth_stencil_state = null,
|
||||
.p_color_blend_state = &.{
|
||||
.logic_op_enable = .false,
|
||||
.logic_op = .copy,
|
||||
.attachment_count = pipeline_color_blend_attachment_states.len,
|
||||
.p_attachments = &pipeline_color_blend_attachment_states,
|
||||
.blend_constants = .{ 0, 0, 0, 0 },
|
||||
},
|
||||
.p_dynamic_state = &.{
|
||||
.dynamic_state_count = dynamic_states.len,
|
||||
.p_dynamic_states = &dynamic_states,
|
||||
.dynamic_state = .{
|
||||
.dynamic_states = &.{ .viewport, .scissor },
|
||||
},
|
||||
.layout = pipeline_layout,
|
||||
.render_pass = swapchain.render_pass,
|
||||
.subpass = 0,
|
||||
.base_pipeline_index = -1,
|
||||
});
|
||||
errdefer engine.destroyPipeline(pipeline);
|
||||
|
||||
const descriptor_pool = try engine.createDescriptorPool(.{
|
||||
.max_sets = 2,
|
||||
.pool_sizes = &.{
|
||||
.{
|
||||
.type = .sampler,
|
||||
.descriptor_count = 1,
|
||||
},
|
||||
}, interface, @ptrCast(&pipeline));
|
||||
errdefer device.destroyPipeline(pipeline, interface);
|
||||
.{
|
||||
.type = .sampled_image,
|
||||
.descriptor_count = max_textures,
|
||||
},
|
||||
.{
|
||||
.type = .uniform_buffer,
|
||||
.descriptor_count = 1,
|
||||
},
|
||||
.{
|
||||
.type = .storage_buffer,
|
||||
.descriptor_count = 4,
|
||||
},
|
||||
},
|
||||
});
|
||||
errdefer engine.destroyDescriptorPool(descriptor_pool);
|
||||
|
||||
// [0] GLOBAL, [1] PER BATCH
|
||||
var descriptor_sets: [2]vk.DescriptorSet = undefined;
|
||||
try engine.allocateDescriptorSets(.{
|
||||
.descriptor_pool = descriptor_pool,
|
||||
.set_layouts = &.{ global_descriptor_set_layout, per_batch_descriptor_set_layout },
|
||||
.variable_descriptor_counts = &.{ @intCast(textures.textures.items.len), 0 },
|
||||
}, &descriptor_sets);
|
||||
|
||||
const descriptor_images = try allocator.alloc(vk.DescriptorImageInfo, textures.textures.items.len);
|
||||
for (textures.textures.items, descriptor_images) |texture, *info| {
|
||||
info.* = .{
|
||||
.sampler = .null_handle,
|
||||
.image_view = texture.image_view,
|
||||
.image_layout = .shader_read_only_optimal,
|
||||
};
|
||||
}
|
||||
defer allocator.free(descriptor_images);
|
||||
|
||||
try engine.updateDescriptorSets(.{
|
||||
.writes = &.{
|
||||
.{
|
||||
.dst_set = descriptor_sets[0],
|
||||
.dst_binding = 0,
|
||||
.dst_array_element = 0,
|
||||
.descriptor_type = .uniform_buffer,
|
||||
.descriptor_infos = .{
|
||||
.buffer = &.{
|
||||
.{
|
||||
.buffer = global_uniforms.buffer,
|
||||
.offset = 0,
|
||||
.range = vk.WHOLE_SIZE,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
.{
|
||||
.dst_set = descriptor_sets[0],
|
||||
.dst_binding = 1,
|
||||
.dst_array_element = 0,
|
||||
.descriptor_type = .storage_buffer,
|
||||
.descriptor_infos = .{
|
||||
.buffer = &.{
|
||||
.{
|
||||
.buffer = point_lights.buffer,
|
||||
.offset = 0,
|
||||
.range = vk.WHOLE_SIZE,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
.{
|
||||
.dst_set = descriptor_sets[0],
|
||||
.dst_binding = 2,
|
||||
.dst_array_element = 0,
|
||||
.descriptor_type = .storage_buffer,
|
||||
.descriptor_infos = .{
|
||||
.buffer = &.{
|
||||
.{
|
||||
.buffer = directional_lights.buffer,
|
||||
.offset = 0,
|
||||
.range = vk.WHOLE_SIZE,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
.{
|
||||
.dst_set = descriptor_sets[0],
|
||||
.dst_binding = 3,
|
||||
.dst_array_element = 0,
|
||||
.descriptor_type = .storage_buffer,
|
||||
.descriptor_infos = .{
|
||||
.buffer = &.{
|
||||
.{
|
||||
.buffer = materials.material_buffer.buffer,
|
||||
.offset = 0,
|
||||
.range = vk.WHOLE_SIZE,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
.{
|
||||
.dst_set = descriptor_sets[0],
|
||||
.dst_binding = 5,
|
||||
.dst_array_element = 0,
|
||||
.descriptor_type = .sampled_image,
|
||||
.descriptor_infos = .{ .image = descriptor_images },
|
||||
},
|
||||
.{
|
||||
.dst_set = descriptor_sets[1],
|
||||
.dst_binding = 0,
|
||||
.dst_array_element = 0,
|
||||
.descriptor_type = .storage_buffer,
|
||||
.descriptor_infos = .{
|
||||
.buffer = &.{
|
||||
.{
|
||||
.buffer = object_uniforms.buffer,
|
||||
.offset = 0,
|
||||
.range = vk.WHOLE_SIZE,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return .{
|
||||
.allocator = allocator,
|
||||
.engine = engine,
|
||||
.swapchain = swapchain,
|
||||
.atoms = atoms,
|
||||
.materials = materials,
|
||||
.textures = textures,
|
||||
.global_descriptor_set_layout = global_descriptor_set_layout,
|
||||
.per_object_descriptor_set_layout = per_object_descriptor_set_layout,
|
||||
.per_batch_descriptor_set_layout = per_batch_descriptor_set_layout,
|
||||
.descriptor_pool = descriptor_pool,
|
||||
.descriptor_sets = descriptor_sets,
|
||||
.pipeline_layout = pipeline_layout,
|
||||
.pipeline = pipeline,
|
||||
|
||||
.vertex_buffer = vertex_buffer,
|
||||
.index_buffer = index_buffer,
|
||||
|
||||
.global_uniforms = global_uniforms,
|
||||
.point_lights = point_lights,
|
||||
.directional_lights = directional_lights,
|
||||
.object_uniforms = object_uniforms,
|
||||
.sampler = sampler,
|
||||
|
||||
.materials = materials,
|
||||
.textures = textures,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Game) void {
|
||||
const device = self.swapchain.engine.device;
|
||||
const interface = &self.swapchain.engine.vk_allocator.interface;
|
||||
self.vertex_buffer.deinit(self.engine);
|
||||
self.index_buffer.deinit(self.engine);
|
||||
|
||||
self.vertex_buffer.deinit(self.swapchain.engine);
|
||||
self.index_buffer.deinit(self.swapchain.engine);
|
||||
self.global_uniforms.deinit(self.engine);
|
||||
self.point_lights.deinit(self.engine);
|
||||
self.directional_lights.deinit(self.engine);
|
||||
self.object_uniforms.deinit(self.engine);
|
||||
|
||||
device.destroyPipeline(self.pipeline, interface);
|
||||
device.destroyPipelineLayout(self.pipeline_layout, interface);
|
||||
device.destroyDescriptorSetLayout(self.per_object_descriptor_set_layout, interface);
|
||||
device.destroyDescriptorSetLayout(self.global_descriptor_set_layout, interface);
|
||||
self.engine.destroyDescriptorPool(self.descriptor_pool);
|
||||
self.engine.destroySampler(self.sampler);
|
||||
self.engine.destroyPipeline(self.pipeline);
|
||||
self.engine.destroyPipelineLayout(self.pipeline_layout);
|
||||
self.engine.destroyDescriptorSetLayout(self.per_batch_descriptor_set_layout);
|
||||
self.engine.destroyDescriptorSetLayout(self.global_descriptor_set_layout);
|
||||
|
||||
self.textures.deinit(self.swapchain.engine);
|
||||
self.materials.deinit(self.swapchain.engine);
|
||||
self.atoms.deinit();
|
||||
self.textures.deinit(self.engine, self.allocator);
|
||||
self.materials.deinit(self.engine, self.allocator);
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
@@ -378,8 +603,93 @@ pub fn update(self: *Game, dt: f32) void {
|
||||
|
||||
self.camera_position = Vector3.add(self.camera_position, camera_d.asVector3(0));
|
||||
|
||||
const framebuffer_size = Vector2.init(
|
||||
@floatFromInt(self.swapchain.extent.width),
|
||||
@floatFromInt(self.swapchain.extent.height),
|
||||
);
|
||||
|
||||
const camera_rotation = Quaternion.mulQuaternion(
|
||||
.initRotationXY(self.camera_yaw),
|
||||
.initRotationYZ(self.camera_pitch),
|
||||
);
|
||||
const camera_aspect_ratio = framebuffer_size.getX() / framebuffer_size.getY();
|
||||
const camera_yscale = 1.0 / @tan(camera_half_vertical_fov_rad);
|
||||
const camera_xscale = camera_yscale / camera_aspect_ratio;
|
||||
|
||||
const matrix_ws_to_vs = Matrix4x4.mulMatrix(
|
||||
Matrix4x4.initRotation(camera_rotation.conjugate()),
|
||||
Matrix4x4.initTranslation(self.camera_position.negate()),
|
||||
);
|
||||
|
||||
// zig fmt: off
|
||||
const matrix_vs_to_cs = Matrix4x4.init(
|
||||
camera_xscale, 0, 0, 0,
|
||||
0, 0, camera_near_plane, 1,
|
||||
0, camera_yscale, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
);
|
||||
// zig fmt: on
|
||||
|
||||
const ambient_light = Vector3.init(0.3, 0.3, 0.3);
|
||||
|
||||
self.global_uniforms.write(self.engine, .{
|
||||
.header = .{
|
||||
.matrixWStoVS = matrix_ws_to_vs.asArray(),
|
||||
.matrixVStoCS = matrix_vs_to_cs.asArray(),
|
||||
.ambientLight = ambient_light.asArray(),
|
||||
},
|
||||
}) catch |err| {
|
||||
std.log.err("Failed to write global uniforms: {s}", .{@errorName(err)});
|
||||
@panic("Frame update failed");
|
||||
};
|
||||
|
||||
const point_lights: []const PointLight = &.{
|
||||
.{
|
||||
.positionWS = .{ 0, 0, 1 },
|
||||
.color = .{ 10, 10, 10 },
|
||||
},
|
||||
.{
|
||||
.positionWS = .{ -10, 10, 1 },
|
||||
.color = .{ 5, 0, 0 },
|
||||
},
|
||||
.{
|
||||
.positionWS = .{ 10, 10, 1 },
|
||||
.color = .{ 0, 0, 5 },
|
||||
},
|
||||
.{
|
||||
.positionWS = .{ -10, -10, 1 },
|
||||
.color = .{ 0, 5, 0 },
|
||||
},
|
||||
.{
|
||||
.positionWS = .{ 10, -10, 1 },
|
||||
.color = .{ 5, 5, 0 },
|
||||
},
|
||||
};
|
||||
self.point_lights.write(self.engine, .{
|
||||
.header = @intCast(point_lights.len),
|
||||
.elements = point_lights,
|
||||
}) catch |err| {
|
||||
std.log.err("Failed to write point lights: {s}", .{@errorName(err)});
|
||||
@panic("Frame update failed");
|
||||
};
|
||||
|
||||
const directional_lights: []const DirectionalLight = &.{
|
||||
.{
|
||||
.directionWS = .{ 0, 0, -1 },
|
||||
.color = .{ 0.2, 0.2, 0.2 },
|
||||
},
|
||||
};
|
||||
self.directional_lights.write(self.engine, .{
|
||||
.header = @intCast(directional_lights.len),
|
||||
.elements = directional_lights,
|
||||
}) catch |err| {
|
||||
std.log.err("Failed to write directional lights: {s}", .{@errorName(err)});
|
||||
@panic("Frame update failed");
|
||||
};
|
||||
|
||||
self.render() catch |err| {
|
||||
std.log.err("Failed to render: {s}", .{@errorName(err)});
|
||||
@panic("Frame update failed");
|
||||
};
|
||||
}
|
||||
|
||||
@@ -387,7 +697,7 @@ pub fn onKeyDown(self: *Game, key_code: glfw.Key, mods: glfw.Mods) void {
|
||||
const no_mods = @as(c_int, @bitCast(mods)) == 0;
|
||||
|
||||
if (key_code == .escape and no_mods) {
|
||||
self.swapchain.engine.mode.surface.window.setShouldClose(true);
|
||||
self.engine.mode.surface.window.setShouldClose(true);
|
||||
}
|
||||
|
||||
if (key_code == .w) {
|
||||
@@ -434,21 +744,40 @@ pub fn onMouseMove(self: *Game, dx: f32, dy: f32) void {
|
||||
}
|
||||
|
||||
fn render(self: *Game) !void {
|
||||
const engine = self.swapchain.engine;
|
||||
const engine = self.engine;
|
||||
const extent = self.swapchain.extent;
|
||||
|
||||
const fence = try engine.device.createFence(&.{}, &engine.vk_allocator.interface);
|
||||
defer engine.device.destroyFence(fence, &engine.vk_allocator.interface);
|
||||
const object_count: u32 = blk: {
|
||||
var objects: std.ArrayList(ObjectUniforms) = try .initCapacity(self.engine.vk_allocator.allocator, 289);
|
||||
defer objects.deinit(self.engine.vk_allocator.allocator);
|
||||
|
||||
var it = Iterator3.init(.init(-8, -8, 0), .init(8, 8, 0), .one);
|
||||
var material: u16 = 0;
|
||||
while (it.next()) |pos| : (material = (material + 1) % @intFromEnum(self.materials.next_id)) {
|
||||
const matrix_os_to_ws = Matrix4x4.initTranslation(pos);
|
||||
const matrix_os_to_ws_normal = Matrix4x4.inverseTransposeAffine(matrix_os_to_ws);
|
||||
objects.appendAssumeCapacity(.{
|
||||
.matrixOStoWS = matrix_os_to_ws.asArray(),
|
||||
.matrixOStoWSNormal = matrix_os_to_ws_normal.asArray(),
|
||||
.material = @enumFromInt(material),
|
||||
});
|
||||
}
|
||||
try self.object_uniforms.write(self.engine, .{ .elements = objects.items });
|
||||
|
||||
break :blk @intCast(objects.items.len);
|
||||
};
|
||||
|
||||
const command_buffer = try engine.allocateGraphicsCommandBuffer();
|
||||
defer engine.freeGraphicsCommandBuffer(command_buffer);
|
||||
// NOTE Do not free command buffer yet
|
||||
|
||||
try command_buffer.beginCommandBuffer(&.{ .flags = .{ .one_time_submit_bit = true } });
|
||||
|
||||
const viewports = [_]vk.Viewport{
|
||||
.{
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = 0,
|
||||
.height = 0,
|
||||
.width = @floatFromInt(extent.width),
|
||||
.height = @floatFromInt(extent.height),
|
||||
.min_depth = 0,
|
||||
.max_depth = 1,
|
||||
},
|
||||
@@ -458,7 +787,7 @@ fn render(self: *Game) !void {
|
||||
const scissors = [_]vk.Rect2D{
|
||||
.{
|
||||
.offset = .{ .x = 0, .y = 0 },
|
||||
.extent = .{ .width = 0, .height = 0 },
|
||||
.extent = extent,
|
||||
},
|
||||
};
|
||||
command_buffer.setScissor(0, scissors.len, &scissors);
|
||||
@@ -473,7 +802,7 @@ fn render(self: *Game) !void {
|
||||
.framebuffer = self.swapchain.swapchain_images[0].framebuffer,
|
||||
.render_area = .{
|
||||
.offset = .{ .x = 0, .y = 0 },
|
||||
.extent = .{ .width = 0, .height = 0 },
|
||||
.extent = extent,
|
||||
},
|
||||
.clear_value_count = clear_values.len,
|
||||
.p_clear_values = &clear_values,
|
||||
@@ -484,10 +813,22 @@ fn render(self: *Game) !void {
|
||||
|
||||
command_buffer.bindVertexBuffers(0, 1, @ptrCast(&self.vertex_buffer.buffer), &.{0});
|
||||
command_buffer.bindIndexBuffer(self.index_buffer.buffer, 0, .uint16);
|
||||
command_buffer.drawIndexed(@intCast(self.index_buffer.index_count), 1, 0, 0, 0);
|
||||
|
||||
command_buffer.bindDescriptorSets(
|
||||
.graphics,
|
||||
self.pipeline_layout,
|
||||
0,
|
||||
self.descriptor_sets.len,
|
||||
&self.descriptor_sets,
|
||||
0,
|
||||
null,
|
||||
);
|
||||
|
||||
command_buffer.drawIndexed(@intCast(self.index_buffer.array_capacity), object_count, 0, 0, 0);
|
||||
}
|
||||
|
||||
try command_buffer.endCommandBuffer();
|
||||
try engine.submitGraphicsCommandBuffer(command_buffer, fence);
|
||||
_ = try engine.device.waitForFences(1, @ptrCast(&fence), .true, std.math.maxInt(u64));
|
||||
const res = try self.swapchain.present(self.engine, command_buffer.handle);
|
||||
|
||||
_ = res;
|
||||
}
|
||||
|
||||
@@ -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 Atoms = @import("Atoms.zig");
|
||||
const atoms = @import("../engine/atoms.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");
|
||||
|
||||
allocator: std.mem.Allocator,
|
||||
const MaterialBuffer = GenericBuffer(void, Material);
|
||||
|
||||
map: Map,
|
||||
storage_buffer: StorageBuffer,
|
||||
material_buffer: MaterialBuffer,
|
||||
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 {
|
||||
id: u32,
|
||||
pub const Key = struct { atom: atoms.Atom };
|
||||
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 {
|
||||
base_color: [3]f32,
|
||||
base_color_texture: Textures.Id,
|
||||
emissive: [3]f32,
|
||||
emissive_texture: Textures.Id,
|
||||
ior: f32,
|
||||
metallic: f32,
|
||||
normal_scale: f32,
|
||||
normal_texture: Textures.Id,
|
||||
occlusion_roughness_metallic_texture: Textures.Id,
|
||||
occlusion_texture_strength: 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;
|
||||
errdefer map.deinit(allocator);
|
||||
try map.ensureTotalCapacity(allocator, capacity);
|
||||
|
||||
var storage_buffer: StorageBuffer = try .init(engine, void, Material, initial_capacity);
|
||||
errdefer storage_buffer.deinit(engine);
|
||||
var material_buffer = try MaterialBuffer.init(engine, .{
|
||||
.usage = .storage,
|
||||
.target_queue = .graphics,
|
||||
.array_capacity = capacity,
|
||||
});
|
||||
errdefer material_buffer.deinit(engine);
|
||||
|
||||
return .{
|
||||
.allocator = allocator,
|
||||
.map = map,
|
||||
.storage_buffer = storage_buffer,
|
||||
.next_id = .{ .id = 0 },
|
||||
.material_buffer = material_buffer,
|
||||
.next_id = @enumFromInt(0),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Materials, engine: *Engine) void {
|
||||
self.storage_buffer.deinit(engine);
|
||||
self.map.deinit(self.allocator);
|
||||
pub fn deinit(self: *Materials, engine: *Engine, allocator: std.mem.Allocator) void {
|
||||
self.material_buffer.deinit(engine);
|
||||
self.map.deinit(allocator);
|
||||
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);
|
||||
}
|
||||
|
||||
pub fn getOrLoadId(self: *Materials, engine: *Engine, textures: *Textures, atoms: *Atoms, key: Atoms.Atom) !Id {
|
||||
const entry = try self.map.getOrPut(self.allocator, key);
|
||||
pub fn getFilename(self: *const Materials, filename: []const u8) ?Id {
|
||||
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) {
|
||||
return entry.value_ptr.*;
|
||||
} 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;
|
||||
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 {
|
||||
baseColor: [3]f32 = .{ 1, 1, 1 },
|
||||
baseColorTexture: ?[]const u8 = null,
|
||||
@@ -87,7 +123,6 @@ fn loadMaterial(self: *Materials, engine: *Engine, textures: *Textures, atoms: *
|
||||
roughness: f32 = 1,
|
||||
};
|
||||
|
||||
const filename = atoms.getString(key).?;
|
||||
std.log.debug("Loading material \"{s}\"...", .{filename});
|
||||
|
||||
const cwd = std.fs.cwd();
|
||||
@@ -102,10 +137,10 @@ fn loadMaterial(self: *Materials, engine: *Engine, textures: *Textures, atoms: *
|
||||
defer file.close();
|
||||
|
||||
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();
|
||||
|
||||
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",
|
||||
.ignore_unknown_fields = false,
|
||||
.allocate = .alloc_if_needed,
|
||||
@@ -116,65 +151,57 @@ fn loadMaterial(self: *Materials, engine: *Engine, textures: *Textures, atoms: *
|
||||
|
||||
const base_color_texture = blk: {
|
||||
if (material_json.baseColorTexture) |name| {
|
||||
const atom = try atoms.getOrPutAtom(name);
|
||||
break :blk try textures.getOrLoadId(engine, atoms, .{ .atom = atom, .usage = .base_color });
|
||||
break :blk try textures.getOrLoadFilename(engine, name, .base_color, temp_allocator);
|
||||
} else {
|
||||
break :blk textures.empty_base_color;
|
||||
break :blk .empty_base_color;
|
||||
}
|
||||
};
|
||||
|
||||
const emissive_texture = blk: {
|
||||
if (material_json.emissiveTexture) |name| {
|
||||
const atom = try atoms.getOrPutAtom(name);
|
||||
break :blk try textures.getOrLoadId(engine, atoms, .{ .atom = atom, .usage = .emissive });
|
||||
break :blk try textures.getOrLoadFilename(engine, name, .emissive, temp_allocator);
|
||||
} else {
|
||||
break :blk textures.empty_emissive;
|
||||
break :blk .empty_emissive;
|
||||
}
|
||||
};
|
||||
|
||||
const normal_texture = blk: {
|
||||
if (material_json.normalTexture) |name| {
|
||||
const atom = try atoms.getOrPutAtom(name);
|
||||
break :blk try textures.getOrLoadId(engine, atoms, .{ .atom = atom, .usage = .normal });
|
||||
break :blk try textures.getOrLoadFilename(engine, name, .normal, temp_allocator);
|
||||
} else {
|
||||
break :blk textures.empty_normal;
|
||||
break :blk .empty_normal;
|
||||
}
|
||||
};
|
||||
|
||||
const occlusion_roughness_metallic_texture = blk: {
|
||||
if (material_json.occlusionRoughnessMetallicTexture) |name| {
|
||||
const atom = try atoms.getOrPutAtom(name);
|
||||
break :blk try textures.getOrLoadId(engine, atoms, .{ .atom = atom, .usage = .occlusion_roughness_metallic });
|
||||
break :blk try textures.getOrLoadFilename(engine, name, .occlusion_roughness_metallic, temp_allocator);
|
||||
} else {
|
||||
break :blk textures.empty_occlusuion_roughness_metallic;
|
||||
break :blk .empty_occlusion_roughness_metallic;
|
||||
}
|
||||
};
|
||||
|
||||
const next_id = self.next_id;
|
||||
const offset: usize = next_id.id;
|
||||
|
||||
if (offset >= self.storage_buffer.array_capacity) {
|
||||
try self.storage_buffer.enlarge(void, Material, engine, self.storage_buffer.array_capacity * 2);
|
||||
}
|
||||
|
||||
const materials = [_]Material{
|
||||
try self.material_buffer.write(engine, .{
|
||||
.element_offset = @intFromEnum(self.next_id),
|
||||
.elements = &.{
|
||||
.{
|
||||
.base_color = material_json.baseColor,
|
||||
.base_color_texture = base_color_texture,
|
||||
.emissive = material_json.emissive,
|
||||
.emissive_texture = emissive_texture,
|
||||
.ior = material_json.ior,
|
||||
.metallic = material_json.metallic,
|
||||
.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,
|
||||
.base_color_texture = base_color_texture,
|
||||
.emissive_texture = emissive_texture,
|
||||
.normal_texture = normal_texture,
|
||||
.occlusion_roughness_metallic_texture = occlusion_roughness_metallic_texture,
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
try self.storage_buffer.writeOffset(void, Material, engine, {}, offset, &materials);
|
||||
self.next_id = .{ .id = next_id.id + 1 };
|
||||
const id = self.next_id;
|
||||
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 Atoms = @import("Atoms.zig");
|
||||
const atoms = @import("../engine/atoms.zig");
|
||||
const Engine = @import("../engine/Engine.zig");
|
||||
const Texture = @import("../engine/Texture.zig");
|
||||
|
||||
allocator: std.mem.Allocator,
|
||||
map: Map,
|
||||
array: Array,
|
||||
textures: Array,
|
||||
|
||||
empty_base_color: Id,
|
||||
empty_emissive: Id,
|
||||
empty_normal: Id,
|
||||
empty_occlusuion_roughness_metallic: Id,
|
||||
|
||||
pub const Id = extern struct {
|
||||
id: u32,
|
||||
};
|
||||
pub const capacity = std.math.maxInt(std.meta.Tag(Id));
|
||||
|
||||
pub const Key = struct {
|
||||
atom: Atoms.Atom,
|
||||
atom: atoms.Atom,
|
||||
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 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;
|
||||
errdefer map.deinit(allocator);
|
||||
try map.ensureTotalCapacity(allocator, capacity);
|
||||
|
||||
var array: Array = try .initCapacity(allocator, 4);
|
||||
var array: Array = try .initCapacity(allocator, capacity);
|
||||
errdefer {
|
||||
for (array.items) |*texture| {
|
||||
texture.deinit(engine);
|
||||
@@ -40,79 +45,116 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine) !Textures {
|
||||
array.deinit(allocator);
|
||||
}
|
||||
|
||||
const empty_base_color: Id = .{ .id = @intCast(array.items.len) };
|
||||
const empty_base_color_texture: Texture = try .init(engine, 1, 1, .base_color);
|
||||
array.appendAssumeCapacity(empty_base_color_texture);
|
||||
const empty_base_color_texture = try Texture.init(engine, .{
|
||||
.width = 1,
|
||||
.height = 1,
|
||||
.usage = .base_color,
|
||||
.target_queue = .graphics,
|
||||
});
|
||||
|
||||
const empty_emissive: Id = .{ .id = @intCast(array.items.len) };
|
||||
const empty_emissive_texture: Texture = try .init(engine, 1, 1, .emissive);
|
||||
array.appendAssumeCapacity(empty_emissive_texture);
|
||||
const empty_emissive_texture = try Texture.init(engine, .{
|
||||
.width = 1,
|
||||
.height = 1,
|
||||
.usage = .emissive,
|
||||
.target_queue = .graphics,
|
||||
});
|
||||
|
||||
const empty_normal: Id = .{ .id = @intCast(array.items.len) };
|
||||
const empty_normal_texture: Texture = try .init(engine, 1, 1, .normal);
|
||||
array.appendAssumeCapacity(empty_normal_texture);
|
||||
const empty_normal_texture = try Texture.init(engine, .{
|
||||
.width = 1,
|
||||
.height = 1,
|
||||
.usage = .normal,
|
||||
.target_queue = .graphics,
|
||||
});
|
||||
|
||||
const empty_occlusuion_roughness_metallic: Id = .{ .id = @intCast(array.items.len) };
|
||||
const empty_occlusuion_roughness_metallic_texture: Texture = try .init(engine, 1, 1, .occlusion_roughness_metallic);
|
||||
array.appendAssumeCapacity(empty_occlusuion_roughness_metallic_texture);
|
||||
const empty_occlusuion_roughness_metallic_texture = try Texture.init(engine, .{
|
||||
.width = 1,
|
||||
.height = 1,
|
||||
.usage = .occlusion_roughness_metallic,
|
||||
.target_queue = .graphics,
|
||||
});
|
||||
|
||||
try empty_base_color_texture.write([4]u8, engine, &.{.{ 255, 255, 255, 255 }});
|
||||
try empty_emissive_texture.write([4]f16, engine, &.{.{ 1.0, 1.0, 1.0, 1.0 }});
|
||||
try empty_normal_texture.write([4]i8, engine, &.{.{ 0, 0, 127, 127 }});
|
||||
try empty_occlusuion_roughness_metallic_texture.write([4]u8, engine, &.{.{ 255, 255, 255, 255 }});
|
||||
array.appendSliceAssumeCapacity(&.{
|
||||
empty_base_color_texture,
|
||||
empty_emissive_texture,
|
||||
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 .{
|
||||
.allocator = allocator,
|
||||
.map = map,
|
||||
.array = array,
|
||||
|
||||
.empty_base_color = empty_base_color,
|
||||
.empty_emissive = empty_emissive,
|
||||
.empty_normal = empty_normal,
|
||||
.empty_occlusuion_roughness_metallic = empty_occlusuion_roughness_metallic,
|
||||
.textures = array,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Textures, engine: *Engine) void {
|
||||
for (self.array.items) |*texture| {
|
||||
pub fn deinit(self: *Textures, engine: *Engine, allocator: std.mem.Allocator) void {
|
||||
for (self.textures.items) |*texture| {
|
||||
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 {
|
||||
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 {
|
||||
return self.map.get(key);
|
||||
}
|
||||
|
||||
pub fn getOrLoadId(self: *Textures, engine: *Engine, atoms: *Atoms, key: Key) !Id {
|
||||
const entry = try self.map.getOrPut(self.allocator, key);
|
||||
pub fn getOrLoadAtom(self: *Textures, engine: *Engine, atom: atoms.Atom, usage: Texture.Usage, temp_allocator: std.mem.Allocator) !Id {
|
||||
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);
|
||||
try self.array.ensureUnusedCapacity(self.allocator, 1);
|
||||
const texture = try self.loadTexture(engine, atoms, key);
|
||||
|
||||
const texture = try self.loadTexture(engine, atoms.getString(atom), usage, temp_allocator);
|
||||
const id = self.nextId();
|
||||
|
||||
entry.value_ptr.* = id;
|
||||
self.array.appendAssumeCapacity(texture);
|
||||
|
||||
self.textures.appendAssumeCapacity(texture);
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
fn loadTexture(self: *Textures, engine: *Engine, atoms: *Atoms, key: Key) !Texture {
|
||||
const filename = atoms.getString(key.atom).?;
|
||||
std.log.debug("Loading texture \"{s}\" as {s}...", .{ filename, @tagName(key.usage) });
|
||||
pub fn getOrLoadFilename(self: *Textures, engine: *Engine, filename: []const u8, usage: Texture.Usage, temp_allocator: std.mem.Allocator) !Id {
|
||||
const atom = try atoms.getOrPutAtom(filename);
|
||||
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();
|
||||
|
||||
@@ -122,22 +164,26 @@ fn loadTexture(self: *Textures, engine: *Engine, atoms: *Atoms, key: Key) !Textu
|
||||
const file = try dir.openFile(filename, .{});
|
||||
defer file.close();
|
||||
|
||||
const file_buf = try file.readToEndAlloc(self.allocator, std.math.maxInt(usize));
|
||||
defer self.allocator.free(file_buf);
|
||||
const file_buf = try file.readToEndAlloc(temp_allocator, std.math.maxInt(usize));
|
||||
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();
|
||||
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);
|
||||
|
||||
try texture.write(u8, engine, img.data);
|
||||
|
||||
try texture.writeRaw(engine, img.data);
|
||||
return texture;
|
||||
}
|
||||
|
||||
fn nextId(self: *const Textures) Id {
|
||||
const index = self.array.items.len;
|
||||
return .{ .id = @intCast(index) };
|
||||
const index = self.textures.items.len;
|
||||
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);
|
||||
}
|
||||
|
||||
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 = .{
|
||||
.p_next = &enabled_features_vulkan11,
|
||||
.descriptor_binding_partially_bound = .true,
|
||||
.descriptor_binding_variable_descriptor_count = .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,
|
||||
.enabled_extension_count = @intCast(enabled_extensions.items.len),
|
||||
.pp_enabled_extension_names = enabled_extensions.items.ptr,
|
||||
.p_enabled_features = &enabled_features_vulkan10,
|
||||
}, &vk_allocator.interface);
|
||||
};
|
||||
|
||||
@@ -561,3 +572,541 @@ fn findPresentationQueueFamily(queue_families_properties: []const vk.QueueFamily
|
||||
|
||||
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 Engine = @import("Engine.zig");
|
||||
const QSM = @import("QueueSharingMode.zig");
|
||||
const TargetQueue = @import("TargetQueue.zig").TargetQueue;
|
||||
|
||||
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 qsm = QSM.resolve(destination_queue_family, transfer_queue_family);
|
||||
const buffer = try engine.device.createBuffer(&.{
|
||||
.size = data.len,
|
||||
const buffer = try engine.createBuffer(.{
|
||||
.size = init_info.capacity,
|
||||
.usage = .{
|
||||
.transfer_src_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);
|
||||
.queue_family_indices = &.{ target_queue_family, transfer_queue_family },
|
||||
});
|
||||
errdefer engine.destroyBuffer(buffer);
|
||||
|
||||
const memory_requirements = engine.device.getBufferMemoryRequirements(buffer);
|
||||
const memory = try engine.allocate(
|
||||
const device_memory = try engine.allocate(
|
||||
memory_requirements,
|
||||
.{
|
||||
.host_visible_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);
|
||||
|
||||
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);
|
||||
try engine.device.bindBufferMemory(buffer, device_memory, 0);
|
||||
|
||||
return .{
|
||||
.buffer = buffer,
|
||||
.memory = memory,
|
||||
.device_memory = device_memory,
|
||||
.capacity = init_info.capacity,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *StagingBuffer, engine: *Engine) void {
|
||||
engine.device.freeMemory(self.memory, &engine.vk_allocator.interface);
|
||||
engine.device.destroyBuffer(self.buffer, &engine.vk_allocator.interface);
|
||||
engine.freeMemory(self.device_memory);
|
||||
engine.destroyBuffer(self.buffer);
|
||||
|
||||
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 QSM = @import("QueueSharingMode.zig");
|
||||
|
||||
engine: *Engine,
|
||||
params: Params,
|
||||
render_pass: vk.RenderPass,
|
||||
|
||||
extent: vk.Extent2D = .{ .width = 0, .height = 0 },
|
||||
swapchain: vk.SwapchainKHR = .null_handle,
|
||||
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 {
|
||||
const params: Params = try .init(engine);
|
||||
@@ -20,7 +28,7 @@ pub fn init(engine: *Engine) !Swapchain {
|
||||
.{
|
||||
.format = params.surface_format.format,
|
||||
.samples = .{ .@"1_bit" = true },
|
||||
.load_op = .dont_care,
|
||||
.load_op = .clear,
|
||||
.store_op = .store,
|
||||
.stencil_load_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);
|
||||
|
||||
var swapchain: Swapchain = .{
|
||||
.engine = engine,
|
||||
.params = params,
|
||||
.render_pass = render_pass,
|
||||
};
|
||||
|
||||
try recreate(&swapchain);
|
||||
try recreate(&swapchain, engine);
|
||||
return swapchain;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Swapchain) void {
|
||||
const allocator = self.engine.vk_allocator.allocator;
|
||||
pub fn deinit(self: *Swapchain, engine: *Engine) void {
|
||||
const allocator = engine.vk_allocator.allocator;
|
||||
|
||||
for (self.swapchain_images) |swapchain_image| {
|
||||
swapchain_image.deinit(self.engine);
|
||||
swapchain_image.deinit(engine);
|
||||
}
|
||||
allocator.free(self.swapchain_images);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
pub fn recreate(self: *Swapchain) !void {
|
||||
const mode = &self.engine.mode.surface;
|
||||
const allocator = self.engine.vk_allocator.allocator;
|
||||
pub fn recreate(self: *Swapchain, engine: *Engine) !void {
|
||||
const mode = &engine.mode.surface;
|
||||
const allocator = engine.vk_allocator.allocator;
|
||||
|
||||
const old_swapchain = self.swapchain;
|
||||
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 ------------------------------------------------
|
||||
|
||||
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 new_swapchain = try self.engine.device.createSwapchainKHR(&.{
|
||||
const qsm = QSM.resolve(engine.graphics_queue.allocation.family, mode.presentation_queue.allocation.family);
|
||||
const new_swapchain = try engine.device.createSwapchainKHR(&.{
|
||||
.surface = mode.surface,
|
||||
.min_image_count = self.params.image_count,
|
||||
.image_format = self.params.surface_format.format,
|
||||
@@ -110,8 +120,8 @@ pub fn recreate(self: *Swapchain) !void {
|
||||
.present_mode = .fifo_khr,
|
||||
.clipped = .true,
|
||||
.old_swapchain = old_swapchain,
|
||||
}, &self.engine.vk_allocator.interface);
|
||||
errdefer self.engine.device.destroySwapchainKHR(new_swapchain, &self.engine.vk_allocator.interface);
|
||||
}, &engine.vk_allocator.interface);
|
||||
errdefer engine.device.destroySwapchainKHR(new_swapchain, &engine.vk_allocator.interface);
|
||||
|
||||
// --- DESTROY OLD SWAPCHAIN AND ITS IMAGES --------------------------------
|
||||
|
||||
@@ -120,29 +130,36 @@ pub fn recreate(self: *Swapchain) !void {
|
||||
// null and deinit the old swapchain and images.
|
||||
|
||||
for (old_swapchain_images) |swapchain_image| {
|
||||
swapchain_image.deinit(self.engine);
|
||||
swapchain_image.deinit(engine);
|
||||
}
|
||||
allocator.free(self.swapchain_images);
|
||||
self.swapchain_images = &.{};
|
||||
|
||||
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.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 -----------------------------------------
|
||||
|
||||
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);
|
||||
|
||||
var swapchain_images: std.ArrayList(SwapchainImage) = try .initCapacity(allocator, images.len);
|
||||
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| {
|
||||
swapchain_images.appendAssumeCapacity(try SwapchainImage.init(self.engine, .{
|
||||
swapchain_images.appendAssumeCapacity(try SwapchainImage.init(engine, .{
|
||||
.image = image,
|
||||
.format = self.params.surface_format.format,
|
||||
.render_pass = self.render_pass,
|
||||
@@ -154,20 +171,105 @@ pub fn recreate(self: *Swapchain) !void {
|
||||
};
|
||||
errdefer {
|
||||
for (new_swapchain_images) |swapchain_image| {
|
||||
swapchain_image.deinit(self.engine);
|
||||
swapchain_image.deinit(engine);
|
||||
}
|
||||
allocator.free(new_swapchain_images);
|
||||
}
|
||||
|
||||
// --- COMMIT --------------------------------------------------------------
|
||||
// --- ACQUIRE NEXT IMAGE --------------------------------------------------
|
||||
|
||||
self.swapchain = new_swapchain;
|
||||
self.swapchain_images = new_swapchain_images;
|
||||
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;
|
||||
}
|
||||
|
||||
fn getCurrentExtent(self: *Engine) !vk.Extent2D {
|
||||
const mode = &self.mode.surface;
|
||||
const surface_capabilities = try self.instance.getPhysicalDeviceSurfaceCapabilitiesKHR(self.physical_device, mode.surface);
|
||||
std.mem.swap(vk.Semaphore, &new_swapchain_images[res.image_index].semaphore_image_acquired, &semaphore_image_acquired);
|
||||
|
||||
// --- COMMIT --------------------------------------------------------------
|
||||
|
||||
self.extent = extent;
|
||||
self.swapchain = new_swapchain;
|
||||
self.swapchain_images = new_swapchain_images;
|
||||
self.image_index = res.image_index;
|
||||
self.semaphore_image_acquired = semaphore_image_acquired;
|
||||
|
||||
std.log.debug("Swapchain recreated.", .{});
|
||||
}
|
||||
|
||||
pub fn present(self: *Swapchain, engine: *Engine, command_buffer: vk.CommandBuffer) !PresentResult {
|
||||
const device = engine.device;
|
||||
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)) {
|
||||
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),
|
||||
);
|
||||
|
||||
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 .{
|
||||
.surface_format = surface_format,
|
||||
.image_count = image_count,
|
||||
@@ -237,6 +341,7 @@ const SwapchainImage = struct {
|
||||
semaphore_render_finished: vk.Semaphore,
|
||||
fence: vk.Fence,
|
||||
framebuffer: vk.Framebuffer,
|
||||
command_buffer: vk.CommandBuffer,
|
||||
|
||||
const InitProps = struct {
|
||||
image: vk.Image,
|
||||
@@ -246,16 +351,10 @@ const SwapchainImage = struct {
|
||||
};
|
||||
|
||||
fn init(engine: *Engine, props: InitProps) !SwapchainImage {
|
||||
const image_view = try engine.device.createImageView(&.{
|
||||
const image_view = try engine.createImageView(.{
|
||||
.image = props.image,
|
||||
.view_type = .@"2d",
|
||||
.format = props.format,
|
||||
.components = .{
|
||||
.r = .identity,
|
||||
.g = .identity,
|
||||
.b = .identity,
|
||||
.a = .identity,
|
||||
},
|
||||
.subresource_range = .{
|
||||
.aspect_mask = .{ .color_bit = true },
|
||||
.base_mip_level = 0,
|
||||
@@ -263,31 +362,28 @@ const SwapchainImage = struct {
|
||||
.base_array_layer = 0,
|
||||
.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);
|
||||
errdefer engine.device.destroySemaphore(semaphore_image_acquired, &engine.vk_allocator.interface);
|
||||
const semaphore_image_acquired = try engine.createSemaphore();
|
||||
errdefer engine.destroySemaphore(semaphore_image_acquired);
|
||||
|
||||
const semaphore_render_finished = try engine.device.createSemaphore(&.{}, &engine.vk_allocator.interface);
|
||||
errdefer engine.device.destroySemaphore(semaphore_render_finished, &engine.vk_allocator.interface);
|
||||
const semaphore_render_finished = try engine.createSemaphore();
|
||||
errdefer engine.destroySemaphore(semaphore_render_finished);
|
||||
|
||||
const fence = try engine.device.createFence(&.{
|
||||
const fence = try engine.createFence(.{
|
||||
.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.device.createFramebuffer(&.{
|
||||
const framebuffer = try engine.createFramebuffer(.{
|
||||
.render_pass = props.render_pass,
|
||||
.attachment_count = attachments.len,
|
||||
.p_attachments = &attachments,
|
||||
.attachments = &.{image_view},
|
||||
.width = props.extent.width,
|
||||
.height = props.extent.height,
|
||||
.layers = 1,
|
||||
}, &engine.vk_allocator.interface);
|
||||
errdefer engine.device.destroyFramebuffer(framebuffer, &engine.vk_allocator.interface);
|
||||
});
|
||||
errdefer engine.destroyFramebuffer(framebuffer);
|
||||
|
||||
return .{
|
||||
.image = props.image,
|
||||
@@ -296,20 +392,16 @@ const SwapchainImage = struct {
|
||||
.semaphore_render_finished = semaphore_render_finished,
|
||||
.fence = fence,
|
||||
.framebuffer = framebuffer,
|
||||
.command_buffer = .null_handle,
|
||||
};
|
||||
}
|
||||
|
||||
fn deinit(self: SwapchainImage, engine: *Engine) void {
|
||||
self.waitForFence(engine) catch return;
|
||||
engine.device.destroyFramebuffer(self.framebuffer, &engine.vk_allocator.interface);
|
||||
engine.device.destroyFence(self.fence, &engine.vk_allocator.interface);
|
||||
engine.device.destroySemaphore(self.semaphore_render_finished, &engine.vk_allocator.interface);
|
||||
engine.device.destroySemaphore(self.semaphore_image_acquired, &engine.vk_allocator.interface);
|
||||
engine.device.destroyImageView(self.image_view, &engine.vk_allocator.interface);
|
||||
}
|
||||
|
||||
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));
|
||||
engine.waitForFence(self.fence) catch {};
|
||||
engine.destroyFramebuffer(self.framebuffer);
|
||||
engine.destroyFence(self.fence);
|
||||
engine.destroySemaphore(self.semaphore_render_finished);
|
||||
engine.destroySemaphore(self.semaphore_image_acquired);
|
||||
engine.destroyImageView(self.image_view);
|
||||
}
|
||||
};
|
||||
|
||||
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 StagingBuffer = @import("StagingBuffer.zig");
|
||||
const QSM = @import("QueueSharingMode.zig");
|
||||
const TargetQueue = @import("TargetQueue.zig").TargetQueue;
|
||||
|
||||
pub const Usage = enum {
|
||||
base_color,
|
||||
@@ -13,7 +13,7 @@ pub const Usage = enum {
|
||||
occlusion_roughness_metallic,
|
||||
emissive,
|
||||
|
||||
pub fn format(self: Usage) vk.Format {
|
||||
pub fn vkFormat(self: Usage) vk.Format {
|
||||
return switch (self) {
|
||||
.base_color => .r8g8b8a8_srgb,
|
||||
.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) {
|
||||
.base_color => 4,
|
||||
.normal => 4,
|
||||
@@ -34,47 +34,58 @@ pub const Usage = enum {
|
||||
pub fn SampleType(comptime self: Usage) type {
|
||||
return switch (self) {
|
||||
.base_color => u8,
|
||||
.normal => u8,
|
||||
.normal => i8,
|
||||
.occlusion_roughness_metallic => u8,
|
||||
.emissive => f16,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn sampleSize(self: Usage) u32 {
|
||||
pub fn bytesPerSample(self: Usage) u32 {
|
||||
return switch (self) {
|
||||
inline else => |x| @sizeOf(SampleType(x)),
|
||||
};
|
||||
}
|
||||
|
||||
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) {
|
||||
inline else => |x| @sizeOf(TexelType(x)),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const InitInfo = struct {
|
||||
width: u32,
|
||||
height: u32,
|
||||
usage: Usage,
|
||||
target_queue: TargetQueue,
|
||||
};
|
||||
|
||||
image: vk.Image,
|
||||
image_view: vk.ImageView,
|
||||
memory: vk.DeviceMemory,
|
||||
device_memory: vk.DeviceMemory,
|
||||
target_queue: TargetQueue,
|
||||
|
||||
width: u32,
|
||||
height: u32,
|
||||
usage: Usage,
|
||||
|
||||
pub fn init(engine: *Engine, width: u32, height: u32, usage: Usage) !Texture {
|
||||
const format: vk.Format = usage.format();
|
||||
pub fn init(engine: *Engine, init_info: InitInfo) !Texture {
|
||||
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.device.createImage(&.{
|
||||
const image = try engine.createImage(.{
|
||||
.image_type = .@"2d",
|
||||
.format = format,
|
||||
.format = init_info.usage.vkFormat(),
|
||||
.extent = .{
|
||||
.width = width,
|
||||
.height = height,
|
||||
.width = init_info.width,
|
||||
.height = init_info.height,
|
||||
.depth = 1,
|
||||
},
|
||||
.mip_levels = 1,
|
||||
@@ -85,29 +96,21 @@ pub fn init(engine: *Engine, width: u32, height: u32, usage: Usage) !Texture {
|
||||
.transfer_dst_bit = true,
|
||||
.sampled_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,
|
||||
.queue_family_indices = &.{ target_queue_family, transfer_queue_family },
|
||||
.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 = try engine.allocate(memory_requirements, .{ .device_local_bit = true });
|
||||
errdefer engine.device.freeMemory(memory, &engine.vk_allocator.interface);
|
||||
const device_memory = try engine.allocate(memory_requirements, .{ .device_local_bit = true });
|
||||
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,
|
||||
.view_type = .@"2d",
|
||||
.format = format,
|
||||
.components = .{
|
||||
.r = .identity,
|
||||
.g = .identity,
|
||||
.b = .identity,
|
||||
.a = .identity,
|
||||
},
|
||||
.format = init_info.usage.vkFormat(),
|
||||
.subresource_range = .{
|
||||
.aspect_mask = .{ .color_bit = true },
|
||||
.base_mip_level = 0,
|
||||
@@ -115,49 +118,72 @@ pub fn init(engine: *Engine, width: u32, height: u32, usage: Usage) !Texture {
|
||||
.base_array_layer = 0,
|
||||
.layer_count = 1,
|
||||
},
|
||||
}, &engine.vk_allocator.interface);
|
||||
errdefer engine.device.destroyImageView(image_view, &engine.vk_allocator.interface);
|
||||
});
|
||||
errdefer engine.destroyImageView(image_view);
|
||||
|
||||
return .{
|
||||
.image = image,
|
||||
.image_view = image_view,
|
||||
.memory = memory,
|
||||
.width = width,
|
||||
.height = height,
|
||||
.usage = usage,
|
||||
.device_memory = device_memory,
|
||||
.target_queue = init_info.target_queue,
|
||||
.width = init_info.width,
|
||||
.height = init_info.height,
|
||||
.usage = init_info.usage,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Texture, engine: *Engine) void {
|
||||
engine.device.destroyImageView(self.image_view, &engine.vk_allocator.interface);
|
||||
engine.device.freeMemory(self.memory, &engine.vk_allocator.interface);
|
||||
engine.device.destroyImage(self.image, &engine.vk_allocator.interface);
|
||||
engine.destroyImageView(self.image_view);
|
||||
engine.freeMemory(self.device_memory);
|
||||
engine.destroyImage(self.image);
|
||||
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
pub fn write(self: Texture, comptime T: type, engine: *Engine, data: []const T) !void {
|
||||
const bytes_per_texel = self.usage.texelSize();
|
||||
const bytes_per_row = self.width * bytes_per_texel;
|
||||
const byte_length = @as(usize, self.height) * bytes_per_row;
|
||||
pub fn writeTexels(self: Texture, comptime TexelType: type, engine: *Engine, texels: []const TexelType) !void {
|
||||
const texel_count = try std.math.mul(u32, self.width, self.height);
|
||||
std.debug.assert(texels.len == texel_count);
|
||||
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);
|
||||
defer engine.device.destroyFence(fence, &engine.vk_allocator.interface);
|
||||
pub fn writeSamples(self: Texture, comptime SampleType: type, engine: *Engine, samples: []const SampleType) !void {
|
||||
const texel_count = try std.math.mul(u32, self.width, self.height);
|
||||
const sample_count = try std.math.mul(u32, texel_count, self.usage.samplesPerTexel());
|
||||
std.debug.assert(samples.len == sample_count);
|
||||
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);
|
||||
|
||||
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();
|
||||
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 } });
|
||||
|
||||
const pre_copy_barriers = [_]vk.ImageMemoryBarrier{
|
||||
const transfer_transition_barriers = [_]vk.ImageMemoryBarrier{
|
||||
.{
|
||||
.src_access_mask = .{},
|
||||
.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,
|
||||
0,
|
||||
null,
|
||||
pre_copy_barriers.len,
|
||||
&pre_copy_barriers,
|
||||
transfer_transition_barriers.len,
|
||||
&transfer_transition_barriers,
|
||||
);
|
||||
|
||||
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 engine.submitTransferCommandBuffer(transfer_command_buffer, fence);
|
||||
|
||||
_ = try engine.device.waitForFences(1, @ptrCast(&fence), .true, std.math.maxInt(u64));
|
||||
try engine.device.resetFences(1, @ptrCast(&fence));
|
||||
const semaphore = try engine.createSemaphore();
|
||||
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 } });
|
||||
|
||||
const post_copy_barriers = [_]vk.ImageMemoryBarrier{
|
||||
const graphics_transition_barriers = [_]vk.ImageMemoryBarrier{
|
||||
.{
|
||||
.src_access_mask = .{ .transfer_write_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,
|
||||
0,
|
||||
null,
|
||||
post_copy_barriers.len,
|
||||
&post_copy_barriers,
|
||||
graphics_transition_barriers.len,
|
||||
&graphics_transition_barriers,
|
||||
);
|
||||
|
||||
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 atoms = @import("engine/atoms.zig");
|
||||
const Engine = @import("engine/Engine.zig");
|
||||
const Swapchain = @import("engine/Swapchain.zig");
|
||||
const Game = @import("Game.zig");
|
||||
@@ -25,6 +26,9 @@ pub fn main() !void {
|
||||
allocator = gpa.allocator();
|
||||
temp_allocator = fba.threadSafeAllocator();
|
||||
|
||||
atoms.init(allocator);
|
||||
defer atoms.deinit();
|
||||
|
||||
stbi.init(allocator);
|
||||
defer stbi.deinit();
|
||||
|
||||
@@ -52,9 +56,9 @@ pub fn main() !void {
|
||||
defer engine.deinit();
|
||||
|
||||
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();
|
||||
|
||||
var t1 = glfw.getTime();
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
const std = @import("std");
|
||||
|
||||
const Vector3 = @import("Vector3.zig").Vector3;
|
||||
const Vector4 = @import("Vector3.zig").Vector4;
|
||||
|
||||
@@ -28,6 +30,11 @@ pub const Vector2 = extern struct {
|
||||
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 {
|
||||
const z_vector: @Vector(3, f32) = .{ undefined, undefined, z };
|
||||
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 Vector2 = @import("Vector2.zig").Vector2;
|
||||
const Vector4 = @import("Vector4.zig").Vector4;
|
||||
@@ -30,6 +32,11 @@ pub const Vector3 = extern struct {
|
||||
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 {
|
||||
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 Vector3 = @import("Vector3.zig").Vector3;
|
||||
|
||||
@@ -30,6 +32,11 @@ pub const Vector4 = extern struct {
|
||||
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 {
|
||||
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