Implemented mesh batching at startup
This commit is contained in:
parent
1ed403dc9e
commit
214317e0bf
5 changed files with 181 additions and 114 deletions
|
|
@ -1,5 +1,6 @@
|
|||
const std = @import("std");
|
||||
const vk = @import("vulkan.zig");
|
||||
const Mesh = @import("Mesh.zig");
|
||||
const Texture = vk.Texture;
|
||||
const c = vk.c;
|
||||
const frames_in_flight = vk.frames_in_flight;
|
||||
|
|
@ -162,9 +163,9 @@ pub fn transitionImageLayout(self: Self, image: c.VkImage, format: c.VkFormat, o
|
|||
|
||||
|
||||
|
||||
pub fn draw(self: Self, indices: u32, frame: usize) void {
|
||||
pub fn draw(self: Self, indices: u32, frame: usize, mesh: Mesh) void {
|
||||
std.debug.assert(frame < frames_in_flight);
|
||||
c.vkCmdDrawIndexed(self.command_buffers[frame], indices, 1, 0, 0, 0);
|
||||
c.vkCmdDrawIndexed(self.command_buffers[frame], indices, 1, mesh.index_buffer, mesh.vertex_buffer, 0);
|
||||
}
|
||||
|
||||
pub fn findMemoryType(self: Self, filter: u32, properties: c.VkMemoryPropertyFlags) error{NoSuitableMemory}!u32 {
|
||||
|
|
|
|||
|
|
@ -5,9 +5,13 @@ const vk = @import("vulkan.zig");
|
|||
const Texture = vk.Texture;
|
||||
const c = vk.c;
|
||||
const math = @import("math");
|
||||
const gltf = @import("gltf.zig");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
layout: c.VkPipelineLayout,
|
||||
handle: c.VkPipeline,
|
||||
vertex_buffer: vk.Buffer,
|
||||
index_buffer: vk.Buffer,
|
||||
texture_set_layout: c.VkDescriptorSetLayout,
|
||||
descriptor_pool: c.VkDescriptorPool,
|
||||
descriptor_set: c.VkDescriptorSet,
|
||||
|
|
@ -25,7 +29,164 @@ light_pos: [*]f32,
|
|||
|
||||
const Self = @This();
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator, device: vk.Device, swapchain: vk.Swapchain, render_pass: vk.RenderPass, vertex_shader: c.VkShaderModule, fragment_shader: c.VkShaderModule) !Self {
|
||||
pub const Builder = struct {
|
||||
current_vertex: i32 = 0,
|
||||
current_index: u32 = 0,
|
||||
vertex_buffers: std.ArrayList(vk.Buffer),
|
||||
index_buffers: std.ArrayList(vk.Buffer),
|
||||
device: vk.Device,
|
||||
allocator: Allocator,
|
||||
|
||||
pub fn init(allocator: Allocator, device: vk.Device) Builder {
|
||||
return .{
|
||||
.vertex_buffers = std.ArrayList(vk.Buffer).init(allocator),
|
||||
.index_buffers = std.ArrayList(vk.Buffer).init(allocator),
|
||||
.device = device,
|
||||
.allocator = allocator,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn addMesh(self: *Builder, path: []const u8) !Mesh {
|
||||
const gltf_data = try gltf.parseFile(self.allocator, path);
|
||||
|
||||
const vertex_buffer = try createVertexBuffer(self.allocator, self.device, gltf_data);
|
||||
const index_buffer = try createIndexBuffer(self.allocator, self.device, gltf_data);
|
||||
const vertex_cursor = self.current_vertex;
|
||||
const index_cursor = self.current_index;
|
||||
self.current_vertex += @intCast(vertex_buffer.size);
|
||||
self.current_index += @intCast(index_buffer.size);
|
||||
try self.vertex_buffers.append(vertex_buffer);
|
||||
try self.index_buffers.append(index_buffer);
|
||||
|
||||
return .{
|
||||
.vertex_buffer = vertex_cursor,
|
||||
.index_buffer = index_cursor,
|
||||
.index_count = @intCast(index_buffer.size / @sizeOf(u16)),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn build(self: *Builder, swapchain: vk.Swapchain, render_pass: vk.RenderPass, vertex_shader: c.VkShaderModule, fragment_shader: c.VkShaderModule) !Self {
|
||||
const vertex_buffer, const index_buffer = try self.createBuffers();
|
||||
return Self.init(self.allocator, self.device, swapchain, render_pass, vertex_shader, fragment_shader, vertex_buffer, index_buffer);
|
||||
}
|
||||
|
||||
pub fn createBuffers(self: *Builder) !struct { vk.Buffer, vk.Buffer } {
|
||||
const vertex_buffer = try self.device.initBuffer(vk.BufferUsage{ .vertex_buffer = true, .transfer_dst = true }, vk.BufferFlags{ .device_local = true }, @intCast(self.current_vertex));
|
||||
|
||||
var vertex_cursor = @as(usize, 0);
|
||||
for (self.vertex_buffers.items) |buffer| {
|
||||
try buffer.copyTo(self.device, vertex_buffer, vertex_cursor);
|
||||
vertex_cursor += buffer.size;
|
||||
buffer.deinit(self.device.handle);
|
||||
}
|
||||
|
||||
const index_buffer = try self.device.initBuffer(vk.BufferUsage{ .index_buffer = true, .transfer_dst = true }, vk.BufferFlags{ .device_local = true }, self.current_index);
|
||||
|
||||
var index_cursor = @as(usize, 0);
|
||||
for (self.index_buffers.items) |buffer| {
|
||||
try buffer.copyTo(self.device, index_buffer, index_cursor);
|
||||
index_cursor += buffer.size;
|
||||
buffer.deinit(self.device.handle);
|
||||
}
|
||||
|
||||
self.vertex_buffers.deinit();
|
||||
self.index_buffers.deinit();
|
||||
|
||||
return .{
|
||||
vertex_buffer,
|
||||
index_buffer,
|
||||
};
|
||||
}
|
||||
|
||||
fn createVertexBuffer(allocator: Allocator, device: vk.Device, gltf_data: anytype) !vk.Buffer {
|
||||
const vertices = gltf_data.vertices;
|
||||
const normals = gltf_data.normals;
|
||||
const uvs = gltf_data.uvs;
|
||||
defer allocator.free(uvs);
|
||||
defer allocator.free(normals);
|
||||
defer allocator.free(vertices);
|
||||
|
||||
const final_array = try allocator.alloc([8]f32, vertices.len);
|
||||
defer allocator.free(final_array);
|
||||
|
||||
for (vertices, normals, uvs, final_array) |vertex, normal, uv, *final| {
|
||||
final[0] = vertex[0];
|
||||
final[1] = vertex[1];
|
||||
final[2] = vertex[2];
|
||||
|
||||
final[3] = normal[0];
|
||||
final[4] = normal[1];
|
||||
final[5] = normal[2];
|
||||
|
||||
final[6] = uv[0];
|
||||
final[7] = uv[1];
|
||||
}
|
||||
|
||||
var data: [*c]?*anyopaque = null;
|
||||
|
||||
const buffer = try device.initBuffer(vk.BufferUsage{ .transfer_src = true }, vk.BufferFlags{ .host_visible = true, .host_coherent = true }, @sizeOf([8]f32) * vertices.len);
|
||||
|
||||
try vk.mapError(vk.c.vkMapMemory(
|
||||
device.handle,
|
||||
buffer.memory,
|
||||
0,
|
||||
buffer.size,
|
||||
0,
|
||||
@ptrCast(&data),
|
||||
));
|
||||
|
||||
if (data) |ptr| {
|
||||
const gpu_vertices: [*]Mesh.Vertex = @ptrCast(@alignCast(ptr));
|
||||
|
||||
@memcpy(gpu_vertices, @as([]Mesh.Vertex, @ptrCast(final_array[0..])));
|
||||
}
|
||||
|
||||
vk.c.vkUnmapMemory(device.handle, buffer.memory);
|
||||
|
||||
const vertex_buffer = try device.initBuffer(vk.BufferUsage{ .vertex_buffer = true, .transfer_dst = true, .transfer_src = true }, vk.BufferFlags{ .device_local = true }, @sizeOf(Mesh.Vertex) * vertices.len);
|
||||
|
||||
try buffer.copyTo(device, vertex_buffer, 0);
|
||||
buffer.deinit(device.handle);
|
||||
|
||||
return vertex_buffer;
|
||||
}
|
||||
|
||||
pub fn createIndexBuffer(allocator: Allocator, device: anytype, gltf_data: anytype) !vk.Buffer {
|
||||
const indices = gltf_data.indices;
|
||||
defer allocator.free(indices);
|
||||
|
||||
var data: [*c]?*anyopaque = null;
|
||||
|
||||
const buffer = try device.initBuffer(vk.BufferUsage{ .transfer_src = true }, vk.BufferFlags{ .host_visible = true, .host_coherent = true }, @sizeOf(u16) * indices.len);
|
||||
|
||||
try vk.mapError(vk.c.vkMapMemory(
|
||||
device.handle,
|
||||
buffer.memory,
|
||||
0,
|
||||
buffer.size,
|
||||
0,
|
||||
@ptrCast(&data),
|
||||
));
|
||||
|
||||
if (data) |ptr| {
|
||||
const gpu_indices: [*]u16 = @ptrCast(@alignCast(ptr));
|
||||
|
||||
@memcpy(gpu_indices, indices[0..]);
|
||||
}
|
||||
|
||||
vk.c.vkUnmapMemory(device.handle, buffer.memory);
|
||||
|
||||
const index_buffer = try device.initBuffer(vk.BufferUsage{ .index_buffer = true, .transfer_dst = true, .transfer_src = true }, vk.BufferFlags{ .device_local = true }, @sizeOf(u16) * indices.len);
|
||||
|
||||
try buffer.copyTo(device, index_buffer, 0);
|
||||
buffer.deinit(device.handle);
|
||||
|
||||
return index_buffer;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
pub fn init(allocator: Allocator, device: vk.Device, swapchain: vk.Swapchain, render_pass: vk.RenderPass, vertex_shader: c.VkShaderModule, fragment_shader: c.VkShaderModule, vertex_buffer: vk.Buffer, index_buffer: vk.Buffer) !Self {
|
||||
const vertex_shader_stage_info: c.VkPipelineShaderStageCreateInfo = .{
|
||||
.sType = c.VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
.stage = c.VK_SHADER_STAGE_VERTEX_BIT,
|
||||
|
|
@ -439,6 +600,8 @@ pub fn init(allocator: std.mem.Allocator, device: vk.Device, swapchain: vk.Swapc
|
|||
.diffuse_sampler = try vk.Sampler.init(device),
|
||||
.textures = std.ArrayList(c.VkDescriptorSet).init(allocator),
|
||||
.light_pos = light_pos,
|
||||
.vertex_buffer = vertex_buffer,
|
||||
.index_buffer = index_buffer,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@ const c = vk.c;
|
|||
|
||||
const Mesh = @This();
|
||||
|
||||
vertex_buffer: i32,
|
||||
index_buffer: u32,
|
||||
index_count: u32,
|
||||
|
||||
pub const Vertex = struct {
|
||||
position: [3]f32,
|
||||
normal: [3]f32,
|
||||
|
|
@ -55,102 +59,6 @@ pub const Vertex = struct {
|
|||
}
|
||||
};
|
||||
|
||||
vertex_buffer: vk.Buffer,
|
||||
index_buffer: vk.Buffer,
|
||||
|
||||
pub fn createVertexBuffer(allocator: Allocator, device: anytype) !vk.Buffer {
|
||||
const gltf_data = try gltf.parseFile(allocator, "assets/models/cube.glb");
|
||||
|
||||
const vertices = gltf_data.vertices;
|
||||
const normals = gltf_data.normals;
|
||||
const uvs = gltf_data.uvs;
|
||||
defer allocator.free(uvs);
|
||||
defer allocator.free(normals);
|
||||
defer allocator.free(vertices);
|
||||
defer allocator.free(gltf_data.indices);
|
||||
|
||||
const final_array = try allocator.alloc([8]f32, vertices.len);
|
||||
defer allocator.free(final_array);
|
||||
|
||||
for (vertices, normals, uvs, final_array) |vertex, normal, uv, *final| {
|
||||
final[0] = vertex[0];
|
||||
final[1] = vertex[1];
|
||||
final[2] = vertex[2];
|
||||
|
||||
final[3] = normal[0];
|
||||
final[4] = normal[1];
|
||||
final[5] = normal[2];
|
||||
|
||||
final[6] = uv[0];
|
||||
final[7] = uv[1];
|
||||
}
|
||||
|
||||
var data: [*c]?*anyopaque = null;
|
||||
|
||||
const buffer = try device.initBuffer(vk.BufferUsage{ .transfer_src = true }, vk.BufferFlags{ .host_visible = true, .host_coherent = true }, @sizeOf([8]f32) * vertices.len);
|
||||
|
||||
try vk.mapError(vk.c.vkMapMemory(
|
||||
device.handle,
|
||||
buffer.memory,
|
||||
0,
|
||||
buffer.size,
|
||||
0,
|
||||
@ptrCast(&data),
|
||||
));
|
||||
|
||||
if (data) |ptr| {
|
||||
const gpu_vertices: [*]Vertex = @ptrCast(@alignCast(ptr));
|
||||
|
||||
@memcpy(gpu_vertices, @as([]Vertex, @ptrCast(final_array[0..])));
|
||||
}
|
||||
|
||||
vk.c.vkUnmapMemory(device.handle, buffer.memory);
|
||||
|
||||
const vertex_buffer = try device.initBuffer(vk.BufferUsage{ .vertex_buffer = true, .transfer_dst = true }, vk.BufferFlags{ .device_local = true }, @sizeOf(Vertex) * vertices.len);
|
||||
|
||||
try buffer.copyTo(device, vertex_buffer);
|
||||
buffer.deinit(device.handle);
|
||||
|
||||
return vertex_buffer;
|
||||
}
|
||||
|
||||
pub fn createIndexBuffer(allocator: Allocator, device: anytype) !vk.Buffer {
|
||||
const gltf_data = try gltf.parseFile(allocator, "assets/models/cube.glb");
|
||||
const indices = gltf_data.indices;
|
||||
defer allocator.free(indices);
|
||||
defer allocator.free(gltf_data.vertices);
|
||||
defer allocator.free(gltf_data.normals);
|
||||
defer allocator.free(gltf_data.uvs);
|
||||
|
||||
var data: [*c]?*anyopaque = null;
|
||||
|
||||
const buffer = try device.initBuffer(vk.BufferUsage{ .transfer_src = true }, vk.BufferFlags{ .host_visible = true, .host_coherent = true }, @sizeOf(u16) * indices.len);
|
||||
|
||||
try vk.mapError(vk.c.vkMapMemory(
|
||||
device.handle,
|
||||
buffer.memory,
|
||||
0,
|
||||
buffer.size,
|
||||
0,
|
||||
@ptrCast(&data),
|
||||
));
|
||||
|
||||
if (data) |ptr| {
|
||||
const gpu_indices: [*]u16 = @ptrCast(@alignCast(ptr));
|
||||
|
||||
@memcpy(gpu_indices, indices[0..]);
|
||||
}
|
||||
|
||||
vk.c.vkUnmapMemory(device.handle, buffer.memory);
|
||||
|
||||
const index_buffer = try device.initBuffer(vk.BufferUsage{ .index_buffer = true, .transfer_dst = true }, vk.BufferFlags{ .device_local = true }, @sizeOf(u16) * indices.len);
|
||||
|
||||
try buffer.copyTo(device, index_buffer);
|
||||
buffer.deinit(device.handle);
|
||||
|
||||
return index_buffer;
|
||||
}
|
||||
|
||||
pub fn init(allocator: Allocator, device: anytype) !Mesh {
|
||||
const vertex_buffer = try Mesh.createVertexBuffer(allocator, device);
|
||||
const index_buffer = try Mesh.createIndexBuffer(allocator, device);
|
||||
|
|
|
|||
|
|
@ -17,8 +17,7 @@ render_pass: vk.RenderPass,
|
|||
swapchain: vk.Swapchain,
|
||||
graphics_pipeline: vk.GraphicsPipeline,
|
||||
current_frame: u32,
|
||||
vertex_buffer: vk.Buffer,
|
||||
index_buffer: vk.Buffer,
|
||||
mesh: Mesh,
|
||||
transform: math.Transform,
|
||||
previous_time: std.time.Instant,
|
||||
|
||||
|
|
@ -37,7 +36,9 @@ pub fn init(allocator: Allocator, instance_handle: vk.c.VkInstance, surface_hand
|
|||
|
||||
const swapchain = try vk.Swapchain.init(allocator, surface, device, physical_device, render_pass);
|
||||
|
||||
var graphics_pipeline = try vk.GraphicsPipeline.init(allocator, device, swapchain, render_pass, vertex_shader, fragment_shader);
|
||||
var pipeline_builder = vk.GraphicsPipeline.Builder.init(allocator, device);
|
||||
const mesh = try pipeline_builder.addMesh("assets/models/cube.glb");
|
||||
var graphics_pipeline = try pipeline_builder.build(swapchain, render_pass, vertex_shader, fragment_shader);
|
||||
|
||||
// TODO: I think the renderer shouldn't have to interact with buffers. I think the API should change to
|
||||
// something along the lines of
|
||||
|
|
@ -46,8 +47,6 @@ pub fn init(allocator: Allocator, instance_handle: vk.c.VkInstance, surface_hand
|
|||
// renderer.render(some_other_thing);
|
||||
// ...
|
||||
// renderer.submit()
|
||||
const triangle = try Mesh.init(allocator, device);
|
||||
|
||||
const texture = try Texture.init("assets/textures/container.png", device);
|
||||
const diffuse = try Texture.init("assets/textures/container_specular.png", device);
|
||||
|
||||
|
|
@ -66,18 +65,14 @@ pub fn init(allocator: Allocator, instance_handle: vk.c.VkInstance, surface_hand
|
|||
.swapchain = swapchain,
|
||||
.graphics_pipeline = graphics_pipeline,
|
||||
.current_frame = 0,
|
||||
// TODO: Why are we storing the buffer and not the Mesh?
|
||||
.vertex_buffer = triangle.vertex_buffer,
|
||||
.index_buffer = triangle.index_buffer,
|
||||
.transform = math.Transform.init(.{0.0, 0.0, 0.0}, .{1.0, 1.0, 1.0}, .{0.0, 0.0, 0.0}),
|
||||
.previous_time = try std.time.Instant.now(),
|
||||
.mesh = mesh,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: Renderer) void {
|
||||
self.device.waitIdle();
|
||||
self.index_buffer.deinit(self.device.handle);
|
||||
self.vertex_buffer.deinit(self.device.handle);
|
||||
self.graphics_pipeline.deinit(self.device);
|
||||
self.swapchain.deinit(self.device);
|
||||
self.render_pass.deinit(self.device);
|
||||
|
|
@ -113,10 +108,10 @@ pub fn render(pool: *ecs.Pool) anyerror!void {
|
|||
try renderer.device.beginCommand(renderer.current_frame);
|
||||
renderer.render_pass.begin(renderer.swapchain, renderer.device, image, renderer.current_frame);
|
||||
renderer.graphics_pipeline.bind(renderer.device, renderer.current_frame);
|
||||
renderer.device.bindVertexBuffer(renderer.vertex_buffer, renderer.current_frame);
|
||||
renderer.device.bindIndexBuffer(renderer.index_buffer, renderer.current_frame);
|
||||
renderer.device.bindVertexBuffer(renderer.graphics_pipeline.vertex_buffer, renderer.current_frame);
|
||||
renderer.device.bindIndexBuffer(renderer.graphics_pipeline.index_buffer, renderer.current_frame);
|
||||
renderer.device.bindDescriptorSets(renderer.graphics_pipeline, renderer.current_frame, 0);
|
||||
renderer.device.draw(@intCast(renderer.index_buffer.size / @sizeOf(u16)), renderer.current_frame);
|
||||
renderer.device.draw(renderer.mesh.index_count, renderer.current_frame, renderer.mesh);
|
||||
renderer.render_pass.end(renderer.device, renderer.current_frame);
|
||||
try renderer.device.endCommand(renderer.current_frame);
|
||||
|
||||
|
|
|
|||
|
|
@ -83,12 +83,12 @@ pub const Buffer = struct {
|
|||
memory: c.VkDeviceMemory,
|
||||
size: usize,
|
||||
|
||||
pub fn copyTo(self: Buffer, device: anytype, dest: Buffer) !void {
|
||||
pub fn copyTo(self: Buffer, device: anytype, dest: Buffer, offset: usize) !void {
|
||||
const command_buffer = try device.beginSingleTimeCommands();
|
||||
|
||||
const copy_region: c.VkBufferCopy = .{
|
||||
.srcOffset = 0,
|
||||
.dstOffset = 0,
|
||||
.dstOffset = offset,
|
||||
.size = self.size,
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue