Begin new Asset Pipeline

This commit is contained in:
2025-11-21 13:02:32 +01:00
parent bbafc55f6f
commit 63a8eee18c
34 changed files with 1190 additions and 586 deletions

View File

@@ -0,0 +1,51 @@
const IndexBuffer = @This();
const std = @import("std");
const vk = @import("vulkan");
const Engine = @import("Engine.zig");
const StagingBuffer = @import("StagingBuffer.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 buffer = try engine.device.createBuffer(&.{
.size = size,
.usage = .{
.transfer_dst_bit = true,
.index_buffer_bit = true,
},
.sharing_mode = .exclusive,
}, null);
errdefer engine.device.destroyBuffer(buffer);
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);
self.* = undefined;
}
pub fn write(self: IndexBuffer, engine: *Engine, indices: []const u16) !void {
std.debug.assert(indices.len == self.index_count);
const staging_buffer: StagingBuffer = .init(engine, std.mem.sliceAsBytes(indices), engine.graphics_queue.allocation.family);
defer staging_buffer.deinit(engine);
}

View File

@@ -0,0 +1,54 @@
const StagingBuffer = @This();
const std = @import("std");
const vk = @import("vulkan");
const Engine = @import("Engine.zig");
buffer: vk.Buffer,
memory: vk.DeviceMemory,
pub fn init(engine: *Engine, data: []const u8, destination_queue_family: u32) !StagingBuffer {
const transfer_queue_family = engine.transfer_queue.allocation.family;
const single_queue_family = transfer_queue_family == destination_queue_family;
const queue_family_indices: []const u32 = if (single_queue_family) &.{} else &.{ transfer_queue_family, destination_queue_family };
const buffer = try engine.device.createBuffer(&.{
.size = data.len,
.usage = .{
.transfer_src_bit = true,
},
.sharing_mode = if (single_queue_family) .exclusive else .concurrent,
.p_queue_family_indices = queue_family_indices.ptr,
}, null);
errdefer engine.device.destroyBuffer(buffer);
const memory_requirements = engine.device.getBufferMemoryRequirements(buffer);
const memory = try engine.allocate(
memory_requirements,
.{
.host_visible_bit = true,
.host_coherent_bit = true,
},
);
errdefer engine.device.freeMemory(memory, &engine.vk_allocator.interface);
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);
return .{
.buffer = buffer,
.memory = memory,
};
}
pub fn deinit(self: *StagingBuffer, engine: *Engine) void {
engine.device.freeMemory(self.memory, &engine.vk_allocator.interface);
engine.device.destroyBuffer(self.buffer);
self.* = undefined;
}

View File

@@ -0,0 +1,74 @@
const StorageBuffer = @This();
const std = @import("std");
const vk = @import("vulkan");
const Engine = @import("Engine.zig");
const StagingBuffer = @import("StagingBuffer.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(array_offset, array_capacity_in_bytes) catch return error.OutOfMemory;
const buffer = try engine.device.createBuffer(&.{
.size = size,
.usage = .{
.transfer_dst_bit = true,
.storage_buffer_bit = true,
},
.sharing_mode = .exclusive,
}, null);
errdefer engine.device.destroyBuffer(buffer);
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);
self.* = undefined;
}
pub fn write(self: StorageBuffer, comptime PrefixType: type, comptime ElementType: type, engine: *Engine, prefix: PrefixType, elements: []const ElementType) !void {
std.debug.assert(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 * elements.len;
const size = self.array_offset + array_size_in_bytes;
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));
const staging_buffer: StagingBuffer = .init(engine, data, engine.graphics_queue.allocation.family);
defer staging_buffer.deinit(engine);
}

139
src/engine/Texture.zig Normal file
View File

@@ -0,0 +1,139 @@
const Texture = @This();
const std = @import("std");
const vk = @import("vulkan");
const Engine = @import("Engine.zig");
pub const Usage = enum {
base_color,
normal,
occlusion_roughness_metallic,
emissive,
pub fn format(self: Usage) vk.Format {
return switch (self) {
.base_color => .r8g8b8_srgb,
.normal => .r8g8b8_snorm,
.occlusion_roughness_metallic => .r8g8b8_unorm,
.emissive => .r16g16b16_sfloat,
};
}
pub fn sampleCount(self: Usage) usize {
return switch (self) {
.base_color => 3,
.normal => 3,
.occlusion_roughness_metallic => 3,
.emissive => 3,
};
}
pub fn SampleType(comptime self: Usage) type {
return switch (self) {
.base_color => u8,
.normal => u8,
.occlusion_roughness_metallic => u8,
.emissive => f16,
};
}
pub fn sampleSize(self: Usage) usize {
return switch (self) {
inline else => |x| @sizeOf(SampleType(x)),
};
}
pub fn TexelType(comptime self: Usage) type {
return [self.sampleCount()]SampleType(self);
}
pub fn texelSize(self: Usage) usize {
return switch (self) {
inline else => |x| @sizeOf(TexelType(x)),
};
}
};
image: vk.Image,
image_view: vk.ImageView,
memory: vk.DeviceMemory,
width: u32,
height: u32,
usage: Usage,
pub fn init(engine: *Engine, width: u32, height: u32, usage: Usage) !Texture {
const format: vk.Format = usage.format();
const image = try engine.device.createImage(&.{
.image_type = .@"2d",
.format = format,
.extent = .{
.width = width,
.height = height,
.depth = 1,
},
.mip_levels = 1,
.array_layers = 1,
.samples = .{ .@"1_bit" = true },
.tiling = .optimal,
.usage = .{
.transfer_src_bit = true,
.sampled_bit = true,
},
.sharing_mode = .exclusive,
.initial_layout = .undefined,
}, &engine.vk_allocator.interface);
errdefer engine.device.destroyImage(image, &engine.vk_allocator.interface);
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);
try engine.device.bindImageMemory(image, memory, 0);
const image_view = try engine.device.createImageView(&.{
.image = image,
.view_type = .@"2d",
.format = format,
.components = .{
.r = .identity,
.g = .identity,
.b = .identity,
.a = .identity,
},
.subresource_range = .{
.aspect_mask = .{ .color_bit = true },
.base_mip_level = 0,
.level_count = 1,
.base_array_level = 0,
.layer_count = 1,
},
}, &engine.vk_allocator.interface);
errdefer engine.device.destroyImageView(image_view, &engine.vk_allocator.interface);
return .{
.image = image,
.image_view = image_view,
.memory = memory,
.width = width,
.height = height,
};
}
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);
self.* = undefined;
}
pub fn write(self: Texture, comptime T: type, data: []const T) void {
const bytes_per_texel = self.format.texelSize();
const bytes_per_row = self.width * bytes_per_texel;
const byte_length = self.height * bytes_per_row;
std.debug.assert(data.len * @sizeOf(T) == byte_length);
}

View File

@@ -0,0 +1,55 @@
const VertexBuffer = @This();
const std = @import("std");
const vk = @import("vulkan");
const Engine = @import("Engine.zig");
const StagingBuffer = @import("StagingBuffer.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 buffer = try engine.device.createBuffer(&.{
.size = size,
.usage = .{
.transfer_dst_bit = true,
.vertex_buffer_bit = true,
},
.sharing_mode = .exclusive,
}, null);
errdefer engine.device.destroyBuffer(buffer);
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);
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 staging_buffer: StagingBuffer = .init(engine, std.mem.sliceAsBytes(vertices), engine.graphics_queue.allocation.family);
defer staging_buffer.deinit(engine);
}