Cleanup GenericBuffer

This commit is contained in:
2026-09-22 16:42:30 +02:00
parent e8674a6bcd
commit 2fb4e38209
8 changed files with 155 additions and 250 deletions

View File

@@ -16,8 +16,6 @@ const Game = @import("Game.zig");
const GenericBuffer = @import("engine/GenericBuffer.zig").GenericBuffer;
const Materials = @import("engine/Materials.zig");
const ObjectUniformsBuffer = GenericBuffer(void, shaders.ObjectUniforms);
const chunk_size = c.vx_per_ck;
const initial_capacity = 256;
@@ -28,7 +26,7 @@ var next_chunk_id: std.atomic.Value(u64) = .init(0);
blocks: [chunk_size][chunk_size][chunk_size]Blocks.Id,
origin: vm.Vector3,
descriptor_set: vk.DescriptorSet,
object_buffer: ObjectUniformsBuffer,
object_buffer: shaders.ObjectUniformsBuffer,
object_count: u32,
/// For debug purposes, a unique "name" per chunk instance.
@@ -51,10 +49,10 @@ pub fn init(init_info: InitInfo) !Chunk {
});
errdefer engine.freeDescriptorSet(init_info.descriptor_pool, descriptor_set);
var object_buffer: ObjectUniformsBuffer = try .init(.{
var object_buffer: shaders.ObjectUniformsBuffer = try .init(.{
.usage = .storage,
.target_queue = .graphics,
.array_capacity = initial_capacity,
.capacity = initial_capacity,
});
errdefer object_buffer.deinit();
@@ -136,12 +134,12 @@ pub fn refresh(self: *Chunk, blocks: *const Blocks, neighbors: Neighbors) !void
}
const object_count: u32 = @intCast(uniforms.items.len);
if (self.object_buffer.array_capacity < object_count) {
if (self.object_buffer.capacity < object_count) {
const desired_capacity = std.math.ceilPowerOfTwoAssert(u32, object_count);
const new_object_buffer: ObjectUniformsBuffer = try .init(.{
const new_object_buffer: shaders.ObjectUniformsBuffer = try .init(.{
.usage = .storage,
.target_queue = .graphics,
.array_capacity = desired_capacity,
.capacity = desired_capacity,
});
try engine.updateDescriptorSets(.{
@@ -170,7 +168,7 @@ pub fn refresh(self: *Chunk, blocks: *const Blocks, neighbors: Neighbors) !void
engine.setObjectName(new_object_buffer.device_memory, "DM Chunk[{d}]", .{self.chunk_id});
}
try self.object_buffer.write(.{ .elements = uniforms.items });
try self.object_buffer.writeSlice(uniforms.items);
self.object_count = object_count;
}

View File

@@ -174,49 +174,45 @@ pub fn init() !Game {
var vertex_buffer = try shaders.VertexBuffer.init(.{
.usage = .vertex,
.target_queue = .graphics,
.array_capacity = 4,
.capacity = 4,
.name = "QuadVB",
});
errdefer vertex_buffer.deinit();
try vertex_buffer.write(.{
.elements = &.{
.init(
.init(0, 0, 0),
.init(0, 1),
.init(0, 0, 1),
.init(1, 0, 0, -1),
),
.init(
.init(1, 0, 0),
.init(1, 1),
.init(0, 0, 1),
.init(1, 0, 0, -1),
),
.init(
.init(0, 1, 0),
.init(0, 0),
.init(0, 0, 1),
.init(1, 0, 0, -1),
),
.init(
.init(1, 1, 0),
.init(1, 0),
.init(0, 0, 1),
.init(1, 0, 0, -1),
),
},
try vertex_buffer.writeSlice(&.{
.init(
.init(0, 0, 0),
.init(0, 1),
.init(0, 0, 1),
.init(1, 0, 0, -1),
),
.init(
.init(1, 0, 0),
.init(1, 1),
.init(0, 0, 1),
.init(1, 0, 0, -1),
),
.init(
.init(0, 1, 0),
.init(0, 0),
.init(0, 0, 1),
.init(1, 0, 0, -1),
),
.init(
.init(1, 1, 0),
.init(1, 0),
.init(0, 0, 1),
.init(1, 0, 0, -1),
),
});
var index_buffer = try shaders.IndexBuffer.init(.{
.usage = .index,
.target_queue = .graphics,
.array_capacity = 6,
.capacity = 6,
.name = "QuadIB",
});
errdefer index_buffer.deinit();
try index_buffer.write(.{
.elements = &.{ 0, 1, 2, 2, 1, 3 },
});
try index_buffer.writeSlice(&.{ 0, 1, 2, 2, 1, 3 });
var global_uniforms = try shaders.GlobalUniformsBuffer.init(.{
.usage = .uniform,
@@ -255,7 +251,7 @@ pub fn init() !Game {
var point_lights = try shaders.PointLightBuffer.init(.{
.usage = .storage,
.target_queue = .graphics,
.array_capacity = max_point_lights,
.capacity = max_point_lights,
.name = "PointLights",
});
errdefer point_lights.deinit();
@@ -263,7 +259,7 @@ pub fn init() !Game {
var directional_lights = try shaders.DirectionalLightBuffer.init(.{
.usage = .storage,
.target_queue = .graphics,
.array_capacity = max_directional_lights,
.capacity = max_directional_lights,
.name = "DirectionalLights",
});
errdefer directional_lights.deinit();
@@ -620,8 +616,8 @@ pub fn init() !Game {
});
}
try point_lights.write(.{ .elements = &point_lights_data });
try directional_lights.write(.{ .elements = &directional_lights_data });
try point_lights.writeSlice(&point_lights_data);
try directional_lights.writeSlice(&directional_lights_data);
var skybox = try Skybox.load("skybox.hdr", 512, global_uniforms.buffer);
errdefer skybox.deinit();

View File

@@ -39,9 +39,9 @@ codepoint_glyph_id: std.hash_map.AutoHashMapUnmanaged(u21, u16),
kerning_em: std.hash_map.AutoHashMapUnmanaged([2]u16, f32),
cap_height_em: f32,
glyph_data: GenericBuffer(void, shaders.GlyphData),
control_points: GenericBuffer(void, [2]f16),
band_data: GenericBuffer(void, u32),
glyph_data: GenericBuffer(shaders.GlyphData),
control_points: GenericBuffer([2]f16),
band_data: GenericBuffer(u32),
const BandHelper = struct {
allocator: std.mem.Allocator,
@@ -394,39 +394,39 @@ pub fn init(ttf_buffer: []const u8, init_info: InitInfo) !Font {
// std.debug.print("Kerning table size: {d}\n", .{kerning_em.size});
var glyph_data: GenericBuffer(void, shaders.GlyphData) = try .init(.{
var glyph_data: GenericBuffer(shaders.GlyphData) = try .init(.{
.usage = .storage,
.target_queue = .graphics,
.array_capacity = @intCast(gpu_glyph_data.items.len),
.capacity = @intCast(gpu_glyph_data.items.len),
.name = "Glyph data",
});
errdefer glyph_data.deinit();
// std.debug.print("Glyph data size: {d} [B]\n", .{gpu_glyph_data.items.len * @sizeOf(shaders.GlyphData)});
var control_points: GenericBuffer(void, [2]f16) = try .init(.{
var control_points: GenericBuffer([2]f16) = try .init(.{
.usage = .storage,
.target_queue = .graphics,
.array_capacity = @intCast(control_points_array.items.len),
.capacity = @intCast(control_points_array.items.len),
.name = "Control points",
});
errdefer control_points.deinit();
// std.debug.print("Control points size: {d} [B]\n", .{control_points_array.items.len * @sizeOf([2]f16)});
var band_data: GenericBuffer(void, u32) = try .init(.{
var band_data: GenericBuffer(u32) = try .init(.{
.usage = .storage,
.target_queue = .graphics,
.array_capacity = @intCast(band_data_array.items.len),
.capacity = @intCast(band_data_array.items.len),
.name = "Band data",
});
errdefer band_data.deinit();
// std.debug.print("Band data size: {d} [B]\n", .{band_data_array.items.len * @sizeOf(u32)});
try glyph_data.write(.{ .elements = gpu_glyph_data.items });
try control_points.write(.{ .elements = control_points_array.items });
try band_data.write(.{ .elements = band_data_array.items });
try glyph_data.writeSlice(gpu_glyph_data.items);
try control_points.writeSlice(control_points_array.items);
try band_data.writeSlice(band_data_array.items);
return .{
.cpu_glyph_data = cpu_glyph_data.toOwnedSliceAssert(),

View File

@@ -29,34 +29,25 @@ pub const Usage = enum {
pub const InitInfo = struct {
usage: Usage,
target_queue: TargetQueue,
array_capacity: u32 = 0,
capacity: u32 = 1,
name: ?[]const u8 = null,
};
pub fn GenericBuffer(comptime Header: type, comptime Element: type) type {
pub fn GenericBuffer(comptime T: type) type {
return struct {
const Self = @This();
buffer: vk.Buffer,
device_memory: vk.DeviceMemory,
target_queue: TargetQueue,
array_capacity: u32,
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 = &.{},
};
const element_size: u32 = @sizeOf(T);
pub fn init(init_info: InitInfo) !Self {
const engine = ctx.engine;
const array_size = try std.math.mul(u32, init_info.array_capacity, element_size);
const size = try std.math.add(u32, array_offset, array_size);
const size = try std.math.mul(u32, init_info.capacity, element_size);
const target_queue_family = switch (init_info.target_queue) {
.graphics => engine.graphics_queue.allocation.family,
@@ -71,14 +62,14 @@ pub fn GenericBuffer(comptime Header: type, comptime Element: type) type {
});
errdefer engine.destroyBuffer(buffer);
if (init_info.name) |name| {
engine.setObjectName(buffer, "B {s} [{s}, {s}]", .{ name, @typeName(Header), @typeName(Element) });
engine.setObjectName(buffer, "B {s} [{s}]", .{ name, @typeName(T) });
}
const memory_requirements = engine.getBufferMemoryRequirements(buffer);
const device_memory = try engine.allocate(memory_requirements, .{ .device_local_bit = true });
errdefer engine.freeMemory(device_memory);
if (init_info.name) |name| {
engine.setObjectName(device_memory, "DM {s} [{s}, {s}]", .{ name, @typeName(Header), @typeName(Element) });
engine.setObjectName(device_memory, "DM {s} [{s}]", .{ name, @typeName(T) });
}
try engine.bindBufferMemory(buffer, device_memory, 0);
@@ -87,7 +78,7 @@ pub fn GenericBuffer(comptime Header: type, comptime Element: type) type {
.buffer = buffer,
.device_memory = device_memory,
.target_queue = init_info.target_queue,
.array_capacity = init_info.array_capacity,
.capacity = init_info.capacity,
};
}
@@ -102,51 +93,26 @@ pub fn GenericBuffer(comptime Header: type, comptime Element: type) type {
self.* = undefined;
}
pub fn write(self: Self, write_info: WriteInfo) !void {
pub fn write(self: Self, value: T) !void {
return self.writeSliceAt(0, &.{value});
}
pub fn writeAt(self: Self, index: u32, value: T) !void {
return self.writeSliceAt(index, &.{value});
}
pub fn writeSlice(self: Self, values: []const T) !void {
return self.writeSliceAt(0, values);
}
pub fn writeSliceAt(self: Self, first_index: u32, values: []const T) !void {
const engine = ctx.engine;
const element_count = std.math.cast(u32, write_info.elements.len) orelse return error.Overflow;
std.debug.assert(write_info.element_offset + element_count <= self.array_capacity);
const element_count = std.math.cast(u32, values.len) orelse return error.Overflow;
std.debug.assert(first_index + element_count <= self.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(&regions_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);
}
const write_size = element_count * element_size;
if (write_size == 0) return;
var staging_buffer: StagingBuffer = try .init(.{
.target_queue = self.target_queue,
@@ -155,17 +121,21 @@ pub fn GenericBuffer(comptime Header: type, comptime Element: type) type {
defer staging_buffer.deinit();
const staging_memory = try staging_buffer.map();
if (write_info.header) |header| {
@memcpy(staging_memory[0..header_size], std.mem.asBytes(&header));
}
@memcpy(staging_memory[array_write_offset..write_size], std.mem.sliceAsBytes(write_info.elements));
@memcpy(staging_memory, std.mem.sliceAsBytes(values));
staging_buffer.unmap();
var command_buffer: CommandBuffer = try .init(.transfer);
defer command_buffer.deinit();
try command_buffer.beginCommandBuffer();
command_buffer.copyBuffer(staging_buffer.buffer, self.buffer, regions.items);
const regions = [_]vk.BufferCopy{
.{
.src_offset = 0,
.dst_offset = first_index * element_size,
.size = write_size,
},
};
command_buffer.copyBuffer(staging_buffer.buffer, self.buffer, &regions);
try command_buffer.endCommandBuffer();
const fence = try engine.createFence(.{});
@@ -174,48 +144,5 @@ pub fn GenericBuffer(comptime Header: type, comptime Element: type) type {
try command_buffer.submit(.{ .fence = fence });
try engine.waitForFence(fence);
}
pub fn writeRaw(self: Self, data_offset: u32, data: []const u8) !void {
const engine = ctx.engine;
const array_size = self.array_capacity * element_size;
const size = array_offset + array_size;
const data_size = std.math.cast(u32, data.len) orelse return error.Overflow;
std.debug.assert(data_offset + data_size <= 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);
var command_buffer = try CommandBuffer.init(engine, .transfer);
defer command_buffer.deinit(engine);
try command_buffer.beginCommandBuffer(.{});
command_buffer.copyBuffer(
staging_buffer.buffer,
self.buffer,
&.{
.{
.src_offset = 0,
.dst_offset = data_offset,
.size = data_size,
},
},
);
try command_buffer.endCommandBuffer();
const fence = try engine.createFence(.{});
defer engine.destroyFence(fence);
try command_buffer.submit(engine, .{ .fence = fence });
try engine.waitForFence(fence);
}
};
}

View File

@@ -48,9 +48,9 @@ pub const Batch = struct {
instance_count: u32,
};
box_gpu_buffer: GenericBuffer(void, Draw.Box),
text_gpu_buffer: GenericBuffer(void, Draw.Text),
image_gpu_buffer: GenericBuffer(void, Draw.Image),
box_gpu_buffer: GenericBuffer(Draw.Box),
text_gpu_buffer: GenericBuffer(Draw.Text),
image_gpu_buffer: GenericBuffer(Draw.Image),
box_cpu_buffer: std.ArrayList(Draw.Box),
text_cpu_buffer: std.ArrayList(Draw.Text),
@@ -91,26 +91,26 @@ pub fn init() !*Gui {
const gui = try allocator_persistent.create(Gui);
var box_gpu_buffer: GenericBuffer(void, Draw.Box) = try .init(.{
var box_gpu_buffer: GenericBuffer(Draw.Box) = try .init(.{
.usage = .storage,
.target_queue = .graphics,
.array_capacity = max_box_draws,
.capacity = max_box_draws,
.name = "GUI box draws",
});
errdefer box_gpu_buffer.deinit();
var text_gpu_buffer: GenericBuffer(void, Draw.Text) = try .init(.{
var text_gpu_buffer: GenericBuffer(Draw.Text) = try .init(.{
.usage = .storage,
.target_queue = .graphics,
.array_capacity = max_text_draws,
.capacity = max_text_draws,
.name = "GUI text draws",
});
errdefer text_gpu_buffer.deinit();
var image_gpu_buffer: GenericBuffer(void, Draw.Image) = try .init(.{
var image_gpu_buffer: GenericBuffer(Draw.Image) = try .init(.{
.usage = .storage,
.target_queue = .graphics,
.array_capacity = max_image_draws,
.capacity = max_image_draws,
.name = "GUI image draws",
});
errdefer image_gpu_buffer.deinit();
@@ -123,13 +123,11 @@ pub fn init() !*Gui {
var index_buffer = try shaders.IndexBuffer.init(.{
.usage = .index,
.target_queue = .graphics,
.array_capacity = 6,
.capacity = 6,
.name = "QuadIB",
});
errdefer index_buffer.deinit();
try index_buffer.write(.{
.elements = &.{ 0, 1, 2, 2, 1, 3 },
});
try index_buffer.writeSlice(&.{ 0, 1, 2, 2, 1, 3 });
const sampler = try engine.createSampler(.{
.mag_filter = .linear,
@@ -842,9 +840,9 @@ fn extendOrAddBatch(self: *Gui, draw_type: std.meta.DeclEnum(Draw), instance: u3
pub fn draw(self: *const Gui, command_buffer: CommandBuffer) !void {
command_buffer.bindIndexBuffer(self.index_buffer.buffer, 0, .uint16);
try self.box_gpu_buffer.write(.{ .elements = self.box_cpu_buffer.items });
try self.text_gpu_buffer.write(.{ .elements = self.text_cpu_buffer.items });
try self.image_gpu_buffer.write(.{ .elements = self.image_cpu_buffer.items });
try self.box_gpu_buffer.writeSlice(self.box_cpu_buffer.items);
try self.text_gpu_buffer.writeSlice(self.text_cpu_buffer.items);
try self.image_gpu_buffer.writeSlice(self.image_cpu_buffer.items);
for (self.batches.items) |batch| {
switch (batch.draw_type) {

View File

@@ -90,7 +90,7 @@ pub fn init() !*Materials {
materials.material_buffer = try .init(.{
.usage = .storage,
.target_queue = .graphics,
.array_capacity = max_materials,
.capacity = max_materials,
.name = "Materials",
});
errdefer materials.material_buffer.deinit();
@@ -114,11 +114,7 @@ pub fn init() !*Materials {
};
materials.array.appendSliceAssumeCapacity(&material_data);
try materials.material_buffer.write(.{
.element_offset = Id.empty.toInt(),
.elements = &material_data,
});
try materials.material_buffer.writeSlice(&material_data);
std.debug.assert(materials.array.items.len == @typeInfo(Id).@"enum".fields.len);
return materials;
@@ -314,26 +310,20 @@ fn loadMaterial(self: *Materials, filename: []const u8, index: u32) !shaders.Mat
.allocate = .alloc_if_needed,
});
const material_data = [_]shaders.Material{
.{
.base_color = .initArray(material_json.baseColor),
.emissive = .initArray(material_json.emissive),
.ior = material_json.ior,
.metallic = material_json.metallic,
.normal_scale = material_json.normalScale,
.occlusion_texture_strength = material_json.occlusionTextureStrength,
.roughness = material_json.roughness,
.base_color_texture = try textures.getOrLoad(material_json.baseColorTexture, .base_color),
.emissive_texture = try textures.getOrLoad(material_json.emissiveTexture, .emissive),
.normal_texture = try textures.getOrLoad(material_json.normalTexture, .normal),
.occlusion_roughness_metallic_texture = try textures.getOrLoad(material_json.occlusionRoughnessMetallicTexture, .occlusion_roughness_metallic),
},
const material_data: shaders.Material = .{
.base_color = .initArray(material_json.baseColor),
.emissive = .initArray(material_json.emissive),
.ior = material_json.ior,
.metallic = material_json.metallic,
.normal_scale = material_json.normalScale,
.occlusion_texture_strength = material_json.occlusionTextureStrength,
.roughness = material_json.roughness,
.base_color_texture = try textures.getOrLoad(material_json.baseColorTexture, .base_color),
.emissive_texture = try textures.getOrLoad(material_json.emissiveTexture, .emissive),
.normal_texture = try textures.getOrLoad(material_json.normalTexture, .normal),
.occlusion_roughness_metallic_texture = try textures.getOrLoad(material_json.occlusionRoughnessMetallicTexture, .occlusion_roughness_metallic),
};
try self.material_buffer.write(.{
.element_offset = index,
.elements = &material_data,
});
return material_data[0];
try self.material_buffer.writeAt(index, material_data);
return material_data;
}

View File

@@ -18,7 +18,7 @@ image: vk.Image,
image_view: vk.ImageView,
device_memory: vk.DeviceMemory,
vertex_buffer: GenericBuffer(void, vm.Vector3),
vertex_buffer: GenericBuffer(vm.Vector3),
index_buffer: shaders.IndexBuffer,
sampler: vk.Sampler,
@@ -495,10 +495,10 @@ pub fn load(
// --- SKYBOX PIPELINE -----------------------------------------------------
var vertex_buffer = try GenericBuffer(void, vm.Vector3).init(.{
var vertex_buffer = try GenericBuffer(vm.Vector3).init(.{
.usage = .vertex,
.target_queue = .graphics,
.array_capacity = 8,
.capacity = 8,
});
errdefer vertex_buffer.deinit();
@@ -510,47 +510,43 @@ pub fn load(
// ┃╱ ┃╱ │╱
// 0━━━━1 O────X
try vertex_buffer.write(.{
.elements = &.{
.init(-1, -1, -1),
.init(1, -1, -1),
.init(-1, 1, -1),
.init(1, 1, -1),
.init(-1, -1, 1),
.init(1, -1, 1),
.init(-1, 1, 1),
.init(1, 1, 1),
},
try vertex_buffer.writeSlice(&.{
.init(-1, -1, -1),
.init(1, -1, -1),
.init(-1, 1, -1),
.init(1, 1, -1),
.init(-1, -1, 1),
.init(1, -1, 1),
.init(-1, 1, 1),
.init(1, 1, 1),
});
var index_buffer = try GenericBuffer(void, u16).init(.{
var index_buffer: shaders.IndexBuffer = try .init(.{
.usage = .index,
.target_queue = .graphics,
.array_capacity = 36,
.capacity = 36,
});
errdefer index_buffer.deinit();
try index_buffer.write(.{
.elements = &.{
// Positive X
3, 1, 7,
7, 1, 5,
// Negative X
0, 2, 4,
4, 2, 6,
// Positive Y
2, 3, 6,
6, 3, 7,
// Negative Y
1, 0, 5,
5, 0, 4,
// Positive Z
6, 7, 4,
4, 7, 5,
// Negative Z
0, 1, 2,
2, 1, 3,
},
try index_buffer.writeSlice(&.{
// Positive X
3, 1, 7,
7, 1, 5,
// Negative X
0, 2, 4,
4, 2, 6,
// Positive Y
2, 3, 6,
6, 3, 7,
// Negative Y
1, 0, 5,
5, 0, 4,
// Positive Z
6, 7, 4,
4, 7, 5,
// Negative Z
0, 1, 2,
2, 1, 3,
});
const descriptor_set_layout = try engine.createDescriptorSetLayout(.{

View File

@@ -5,13 +5,13 @@ const Materials = @import("engine/Materials.zig");
const GenericBuffer = @import("engine/GenericBuffer.zig").GenericBuffer;
const Textures = @import("engine/Textures.zig");
pub const VertexBuffer = GenericBuffer(void, Vertex);
pub const IndexBuffer = GenericBuffer(void, Index);
pub const GlobalUniformsBuffer = GenericBuffer(GlobalUniforms, void);
pub const PointLightBuffer = GenericBuffer(void, PointLight);
pub const DirectionalLightBuffer = GenericBuffer(void, DirectionalLight);
pub const MaterialBuffer = GenericBuffer(void, Material);
pub const ObjectUniformsBuffer = GenericBuffer(void, ObjectUniforms);
pub const VertexBuffer = GenericBuffer(Vertex);
pub const IndexBuffer = GenericBuffer(Index);
pub const GlobalUniformsBuffer = GenericBuffer(GlobalUniforms);
pub const PointLightBuffer = GenericBuffer(PointLight);
pub const DirectionalLightBuffer = GenericBuffer(DirectionalLight);
pub const MaterialBuffer = GenericBuffer(Material);
pub const ObjectUniformsBuffer = GenericBuffer(ObjectUniforms);
pub const Vertex = extern struct {
positionOS: vm.Vector3,