diff --git a/assets/shaders/shader.vert b/assets/shaders/shader.vert index dcb0d65..b2d181e 100644 --- a/assets/shaders/shader.vert +++ b/assets/shaders/shader.vert @@ -1,5 +1,11 @@ #version 450 +struct Transform { + mat4 translation; + mat4 scale; + mat4 rotation; +}; + layout(location = 0) in vec3 vertPos; layout(location = 1) in vec3 normal; layout(location = 2) in vec2 uv; @@ -12,10 +18,8 @@ layout (binding = 1) uniform ViewUniform { mat4 view; } view; -layout (binding = 4) uniform TransformUniform { - mat4 translation; - mat4 scale; - mat4 rotation; +layout (binding = 4) readonly buffer TransformUniform { + Transform transforms[]; } transform; layout(location = 2) out vec3 Normal; @@ -23,7 +27,7 @@ layout(location = 3) out vec3 FragPos; layout(location = 4) out vec2 TexCoords; void main() { - mat4 transformation = transform.translation * transform.scale * transform.rotation; + mat4 transformation = transform.transforms[0].translation * transform.transforms[0].scale * transform.transforms[0].rotation; vec4 out_vec = proj.proj * view.view * transformation * vec4(vertPos, 1.0); FragPos = vec3(transformation * vec4(vertPos, 1.0)); diff --git a/src/rendering/GraphicsPipeline.zig b/src/rendering/GraphicsPipeline.zig index 1dd41c5..c3a33f1 100644 --- a/src/rendering/GraphicsPipeline.zig +++ b/src/rendering/GraphicsPipeline.zig @@ -23,7 +23,7 @@ descriptor_set_layout: c.VkDescriptorSetLayout, projection_buffer: vk.Buffer, view_buffer: vk.Buffer, view_memory: [*c]u8, -transform_memory: [*c]u8, +transform_buffer: vk.DynamicBuffer(math.Transform), view_pos_memory: [*c]u8, diffuse_sampler: vk.Sampler, specular_sampler: vk.Sampler, @@ -303,7 +303,7 @@ pub fn init(allocator: Allocator, device: vk.Device, swapchain: vk.Swapchain, re const transform_binding = c.VkDescriptorSetLayoutBinding{ .binding = 4, - .descriptorType = c.VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .descriptorType = c.VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, .descriptorCount = 1, .stageFlags = c.VK_SHADER_STAGE_VERTEX_BIT, }; @@ -417,12 +417,17 @@ pub fn init(allocator: Allocator, device: vk.Device, swapchain: vk.Swapchain, re .descriptorCount = 2, }; - const sizes = [_]c.VkDescriptorPoolSize {size, sampler_size}; + const transforms_size = c.VkDescriptorPoolSize{ + .type = c.VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = 1, + }; + + const sizes = [_]c.VkDescriptorPoolSize {size, sampler_size, transforms_size}; const descriptor_pool_info = c.VkDescriptorPoolCreateInfo{ .sType = c.VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, .maxSets = 2, - .poolSizeCount = 2, + .poolSizeCount = 3, .pPoolSizes = sizes[0..].ptr, }; @@ -505,36 +510,7 @@ pub fn init(allocator: Allocator, device: vk.Device, swapchain: vk.Swapchain, re c.vkUpdateDescriptorSets(device.handle, 1, &write_view_descriptor_set, 0, null); - const transform_buffer = try device.initBuffer(vk.BufferUsage{ .uniform_buffer = true, .transfer_dst = true }, vk.BufferFlags{ .device_local = true }, @sizeOf(math.Transform) - @sizeOf(math.Quaternion)); - - var transform_data: [*c]u8 = undefined; - - try vk.mapError(c.vkMapMemory( - device.handle, - transform_buffer.memory, - 0, - transform_buffer.size, - 0, - @ptrCast(&transform_data), - )); - - const transform_descriptor_buffer_info = c.VkDescriptorBufferInfo{ - .buffer = transform_buffer.handle, - .offset = 0, - .range = transform_buffer.size, - }; - - const write_transform_descriptor_set = c.VkWriteDescriptorSet{ - .sType = c.VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .dstSet = descriptor_set, - .dstBinding = 4, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = c.VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - .pBufferInfo = &transform_descriptor_buffer_info, - }; - - c.vkUpdateDescriptorSets(device.handle, 1, &write_transform_descriptor_set, 0, null); + const transform_buffer = try vk.DynamicBuffer(math.Transform).init(allocator, device, vk.BufferUsage{ .storage_buffer = true, .transfer_dst = true }, vk.BufferFlags{ .device_local = true }, descriptor_set, 4); const directional_light_buffer = try device.initBuffer(vk.BufferUsage{ .uniform_buffer = true, .transfer_dst = true }, vk.BufferFlags{ .device_local = true }, @sizeOf(lights.DirectionalLight)); @@ -643,7 +619,7 @@ pub fn init(allocator: Allocator, device: vk.Device, swapchain: vk.Swapchain, re .view_buffer = view_buffer, .view_memory = view_data, .view_pos_memory = view_pos_data, - .transform_memory = transform_data, + .transform_buffer = transform_buffer, .diffuse_sampler = try vk.Sampler.init(device), .specular_sampler = try vk.Sampler.init(device), .textures = std.ArrayList(c.VkDescriptorSet).init(allocator), diff --git a/src/rendering/Renderer.zig b/src/rendering/Renderer.zig index d2c721c..4a57998 100644 --- a/src/rendering/Renderer.zig +++ b/src/rendering/Renderer.zig @@ -118,8 +118,8 @@ pub fn render(pool: *ecs.Pool) anyerror!void { renderer.transform.rotate(math.rad(15) * delta_time, .{0.0, 1.0, 0.0}); - const transform_memory = renderer.graphics_pipeline.transform_memory; - @memcpy(transform_memory[0..(@sizeOf(math.Transform)-@sizeOf(math.Quaternion))], std.mem.asBytes(&renderer.transform)[0..(@sizeOf(math.Transform)-@sizeOf(math.Quaternion))]); + const transform_memory = renderer.graphics_pipeline.transform_buffer.mapped_memory; + transform_memory[0] = renderer.transform; try renderer.device.waitFence(renderer.current_frame); const image = try renderer.swapchain.nextImage(renderer.device, renderer.current_frame); diff --git a/src/rendering/dynamic_buffer.zig b/src/rendering/dynamic_buffer.zig new file mode 100644 index 0000000..205cad5 --- /dev/null +++ b/src/rendering/dynamic_buffer.zig @@ -0,0 +1,183 @@ +const std = @import("std"); +const vk = @import("vulkan.zig"); +const c = vk.c; +const Allocator = std.mem.Allocator; +const rendering = @import("rendering.zig"); + +pub fn DynamicBuffer(comptime T: type) type { + return struct { + device: vk.Device, + usage: vk.BufferUsage, + flags: vk.BufferFlags, + handle: c.VkBuffer, + memory: c.VkDeviceMemory, + size: usize, + len: usize, + element_size: usize, + free_indices: std.ArrayList(usize), + allocator: std.mem.Allocator, + mapped_memory: []T, + descriptor_set: c.VkDescriptorSet, + binding: u32, + + const Self = @This(); + + pub fn init(allocator: std.mem.Allocator, device: vk.Device, usage: vk.BufferUsage, flags: vk.BufferFlags, descriptor_set: c.VkDescriptorSet, binding: u32) !Self { + const size = @sizeOf(T) * 10; + const family_indices: []const u32 = &.{device.graphics_family}; + + const create_info: c.VkBufferCreateInfo = .{ + .sType = c.VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .size = size, + .sharingMode = c.VK_SHARING_MODE_EXCLUSIVE, + .usage = @bitCast(usage), + .queueFamilyIndexCount = 1, + .pQueueFamilyIndices = family_indices.ptr, + }; + + var buffer: c.VkBuffer = undefined; + try vk.mapError(c.vkCreateBuffer(device.handle, &create_info, null, &buffer)); + + var memory_requirements: c.VkMemoryRequirements = undefined; + c.vkGetBufferMemoryRequirements(device.handle, buffer, &memory_requirements); + + const alloc_info: c.VkMemoryAllocateInfo = .{ + .sType = c.VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .allocationSize = memory_requirements.size, + .memoryTypeIndex = device.pick_memory_type(memory_requirements.memoryTypeBits, @bitCast(flags)), + }; + + var device_memory: c.VkDeviceMemory = undefined; + + try vk.mapError(c.vkAllocateMemory(device.handle, &alloc_info, null, &device_memory)); + + try vk.mapError(c.vkBindBufferMemory(device.handle, buffer, device_memory, 0)); + + var mapped_data: [*c]u8 = undefined; + + try vk.mapError(c.vkMapMemory( + device.handle, + device_memory, + 0, + size, + 0, + @ptrCast(&mapped_data), + )); + + const mapped_memory: []T = @as([*]T, @ptrCast(@alignCast(mapped_data)))[0..10]; + + const descriptor_buffer_info = c.VkDescriptorBufferInfo{ + .buffer = buffer, + .offset = 0, + .range = size, + }; + + const write_descriptor_set = c.VkWriteDescriptorSet{ + .sType = c.VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = descriptor_set, + .dstBinding = binding, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = c.VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .pBufferInfo = &descriptor_buffer_info, + }; + + c.vkUpdateDescriptorSets(device.handle, 1, &write_descriptor_set, 0, null); + + return .{ + .handle = buffer, + .size = size, + .memory = device_memory, + .device = device, + .element_size = @sizeOf(T), + .usage = usage, + .flags = flags, + .allocator = allocator, + .free_indices = std.ArrayList(usize).init(allocator), + .mapped_memory = mapped_memory, + .descriptor_set = descriptor_set, + .binding = binding, + .len = 0, + }; + } + + pub fn elementOffset(self: Self, index: usize) usize { + return self.element_size * index; + } + + pub fn items(self: Self) []T { + return self.mapped_memory[0..self.len-1]; + } + + pub fn remove(self: *Self, index: usize) void { + self.free_indices.append(index); + @memset(@as([*]u8, @ptrCast(@alignCast(self.mapped_memory[index..].ptr)))[0..self.element_size], 0); + } + + pub fn append(self: *Self, element: T) !void { + if (self.free_indices.pop()) |index| { + self.mapped_memory[index] = element; + return; + } + + if (self.size + self.element_size >= self.size) self.grow(); + + self.mapped_memory[self.len] = element; + self.len += 1; + } + + pub fn grow(self: *Self) !void { + const new_size = self.size + (self.size / 2); + const new = try Self.init(self.allocator, self.device, self.usage, self.flags, new_size); + try self.copyTo(new, 0); + + c.vkDestroyBuffer(self.device.handle, self.handle, null); + c.vkUnmapMemory(self.device.handle, self.memory); + c.vkFreeMemory(self.device.handle, self.memory, null); + + self.size = new.size; + self.handle = new.handle; + self.memory = new.memory; + self.mapped_memory = new.mapped_memory; + + const descriptor_buffer_info = c.VkDescriptorBufferInfo{ + .buffer = self.handle, + .offset = 0, + .range = self.size, + }; + + const write_descriptor_set = c.VkWriteDescriptorSet{ + .sType = c.VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = self.descriptor_set, + .dstBinding = self.binding, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = c.VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .pBufferInfo = &descriptor_buffer_info, + }; + + c.vkUpdateDescriptorSets(self.device.handle, 1, &write_descriptor_set, 0, null); + } + + pub fn copyTo(self: Self, dest: Self, offset: usize) !void { + const command_buffer = try self.device.beginSingleTimeCommands(); + + const copy_region: c.VkBufferCopy = .{ + .srcOffset = 0, + .dstOffset = offset, + .size = self.size, + }; + + c.vkCmdCopyBuffer(command_buffer, self.handle, dest.handle, 1, ©_region); + + try self.device.endSingleTimeCommands(command_buffer); + } + + pub fn deinit(self: Self) void { + self.free_indices.deinit(); + c.vkDestroyBuffer(self.device.handle, self.handle, null); + c.vkUnmapMemory(self.device.handle, self.memory); + c.vkFreeMemory(self.device.handle, self.memory, null); + } + }; +} diff --git a/src/rendering/vulkan.zig b/src/rendering/vulkan.zig index 1387de3..aa02978 100644 --- a/src/rendering/vulkan.zig +++ b/src/rendering/vulkan.zig @@ -3,6 +3,7 @@ pub const GraphicsPipeline = @import("GraphicsPipeline.zig"); pub const Device = @import("Device.zig"); pub const Swapchain = @import("Swapchain.zig"); pub const PhysicalDevice = @import("PhysicalDevice.zig"); +pub const DynamicBuffer = @import("dynamic_buffer.zig").DynamicBuffer; const std = @import("std"); pub const c = @cImport({