diff --git a/src/rendering/Device.zig b/src/rendering/Device.zig index 10a4703..34a4292 100644 --- a/src/rendering/Device.zig +++ b/src/rendering/Device.zig @@ -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 { diff --git a/src/rendering/GraphicsPipeline.zig b/src/rendering/GraphicsPipeline.zig index 2be8909..6248bb6 100644 --- a/src/rendering/GraphicsPipeline.zig +++ b/src/rendering/GraphicsPipeline.zig @@ -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, }; } diff --git a/src/rendering/Mesh.zig b/src/rendering/Mesh.zig index 28d18c1..f5d58b6 100644 --- a/src/rendering/Mesh.zig +++ b/src/rendering/Mesh.zig @@ -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); diff --git a/src/rendering/Renderer.zig b/src/rendering/Renderer.zig index aefcde1..569160a 100644 --- a/src/rendering/Renderer.zig +++ b/src/rendering/Renderer.zig @@ -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); diff --git a/src/rendering/vulkan.zig b/src/rendering/vulkan.zig index 6e0b12f..1387de3 100644 --- a/src/rendering/vulkan.zig +++ b/src/rendering/vulkan.zig @@ -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, };