const std = @import("std"); const vk = @import("vulkan"); const Instance = @import("vulkan_renderer.zig").Instance; const Device = @import("vulkan_renderer.zig").Device; const CommandBuffer = @import("vulkan_renderer.zig").CommandBuffer; pub const device_extensions = [_][*:0]const u8{vk.extensions.khr_swapchain.name}; pub const Vector3 = @Vector(3, f32); // Vertex data representation pub const Vertex = struct { // Vertex position (x, y, z) pos: Vector3, col: Vector3, }; pub const QueueFamilyIndices = struct { graphics_family: ?u32 = null, presentation_family: ?u32 = null, pub fn isValid(self: QueueFamilyIndices) bool { return self.graphics_family != null and self.presentation_family != null; } }; pub const SwapchainDetails = struct { surface_capabilities: vk.SurfaceCapabilitiesKHR, formats: []vk.SurfaceFormatKHR, presentation_modes: []vk.PresentModeKHR, }; pub const SwapchainImage = struct { image: vk.Image, image_view: vk.ImageView, }; fn findMemoryTypeIndex(pdev: vk.PhysicalDevice, instance: Instance, allowed_types: u32, properties: vk.MemoryPropertyFlags) u32 { // Get properties of physical device memory const memory_properties = instance.getPhysicalDeviceMemoryProperties(pdev); const mem_type_count = memory_properties.memory_type_count; for (memory_properties.memory_types[0..mem_type_count], 0..mem_type_count) |mem_type, i| { // Index of memory type must match corresponding bit in allowed_types if (allowed_types & (@as(u32, 1) << @truncate(i)) != 0 and mem_type.property_flags.contains(properties)) { // Return the index of the valid memory type return @truncate(i); } } unreachable; } pub fn createBuffer( pdev: vk.PhysicalDevice, instance: Instance, device: Device, buffer_size: vk.DeviceSize, buffer_usage: vk.BufferUsageFlags, buffer_properties: vk.MemoryPropertyFlags, buffer: *vk.Buffer, buffer_memory: *vk.DeviceMemory, ) !void { // Create vertex buffer // Information to create buffer (doesn't include assigning memory) const buffer_create_info: vk.BufferCreateInfo = .{ .size = buffer_size, // Size of buffer (size of 1 vertex * number of vertices) .usage = buffer_usage, // Multiple types of buffer possible .sharing_mode = .exclusive, // Similar to swapchain images, can share vertex buffers }; buffer.* = try device.createBuffer(&buffer_create_info, null); // Get buffer memory requirements const mem_requirements = device.getBufferMemoryRequirements(buffer.*); // Allocate memory to buffer const allocate_info: vk.MemoryAllocateInfo = .{ .allocation_size = mem_requirements.size, .memory_type_index = findMemoryTypeIndex( pdev, instance, mem_requirements.memory_type_bits, // Index of memory type of physical device that has required bit flags // Host visible: CPU can interact with memory // Host coherent: Allows placement of data straight into buffer after mapping (otherwise would have to specify manually) buffer_properties, ), }; // Allocate memory to vkDeviceMemory buffer_memory.* = try device.allocateMemory(&allocate_info, null); // Allocate memory to given vertex buffer try device.bindBufferMemory(buffer.*, buffer_memory.*, 0); } pub fn copyBuffer( device: Device, transfer_queue: vk.Queue, transfer_command_pool: vk.CommandPool, src_buffer: vk.Buffer, dst_buffer: vk.Buffer, buffer_size: vk.DeviceSize, allocator: std.mem.Allocator, ) !void { // Command buffer to hold transfer commands const transfer_command_buffer_handle = try allocator.create(vk.CommandBuffer); defer allocator.destroy(transfer_command_buffer_handle); // Free temporary buffer back to pool defer device.freeCommandBuffers(transfer_command_pool, 1, @ptrCast(transfer_command_buffer_handle)); // Command buffer details const alloc_info: vk.CommandBufferAllocateInfo = .{ .command_pool = transfer_command_pool, .level = .primary, .command_buffer_count = 1, }; // Allocate command buffer from pool try device.allocateCommandBuffers(&alloc_info, @ptrCast(transfer_command_buffer_handle)); const transfer_command_buffer = CommandBuffer.init(transfer_command_buffer_handle.*, device.wrapper); // Information to begin the command buffer record const begin_info: vk.CommandBufferBeginInfo = .{ .flags = .{ .one_time_submit_bit = true }, // We're only using the command buffer once, so set to one time submit }; // Begin recording transfer commands try transfer_command_buffer.beginCommandBuffer(&begin_info); // Region of data to copy from and to const buffer_copy_region: vk.BufferCopy = .{ .src_offset = 0, .dst_offset = 0, .size = buffer_size, }; // Command to copy src buffer to dst buffer transfer_command_buffer.copyBuffer(src_buffer, dst_buffer, 1, @ptrCast(&buffer_copy_region)); // End commands try transfer_command_buffer.endCommandBuffer(); // Queue submission information const submit_info: vk.SubmitInfo = .{ .command_buffer_count = 1, .p_command_buffers = @ptrCast(&transfer_command_buffer.handle), }; // Submit transfer command to transfer queue and wait until it finishes try device.queueSubmit(transfer_queue, 1, @ptrCast(&submit_info), .null_handle); try device.queueWaitIdle(transfer_queue); }