const std = @import("std"); const builtin = @import("builtin"); const vk = @import("vk"); const c = @import("c.zig"); pub fn uploadData( comptime T: type, pdev: vk.PhysicalDevice, vki: Instance.Wrapper, dev: vk.Device, vkd: Device.Wrapper, queue: vk.Queue, pool: vk.CommandPool, buffer: vk.Buffer, source: []const T, ) !void { // if (@typeInfo(T) == .Struct and @typeInfo(T).Struct.layout == .auto) @compileError("Requires defined T layout"); const size = @sizeOf(T) * source.len; const staging_buffer = try vkd.createBuffer(dev, &.{ .size = size, .usage = .{ .transfer_src_bit = true }, .sharing_mode = .exclusive, }, null); defer vkd.destroyBuffer(dev, staging_buffer, null); const vally = VkAllocator.init(pdev, vki); const mem_reqs = vkd.getBufferMemoryRequirements(dev, staging_buffer); const staging_memory = try vally.alloc(dev, vkd, mem_reqs, .{ .host_visible_bit = true, .host_coherent_bit = true }); // const staging_memory = try allocate(pdev, vki, dev, vkd, mem_reqs, .{ // .host_visible_bit = true, // .host_coherent_bit = true, // }); defer vkd.freeMemory(dev, staging_memory, null); try vkd.bindBufferMemory(dev, staging_buffer, staging_memory, 0); { const data = try vkd.mapMemory(dev, staging_memory, 0, vk.WHOLE_SIZE, .{}); defer vkd.unmapMemory(dev, staging_memory); const dest: [*]T = @ptrCast(@alignCast(data)); @memcpy(dest, source); } try copyBuffer(dev, queue, pool, buffer, staging_buffer, size, vkd); } pub fn copyBuffer( dev: vk.Device, queue: vk.Queue, pool: vk.CommandPool, dst: vk.Buffer, src: vk.Buffer, size: vk.DeviceSize, vkd: Device.Wrapper, ) !void { var cmdbuf: vk.CommandBuffer = undefined; try vkd.allocateCommandBuffers(dev, &.{ .command_pool = pool, .level = .primary, .command_buffer_count = 1, }, @ptrCast(&cmdbuf)); defer vkd.freeCommandBuffers(dev, pool, 1, @ptrCast(&cmdbuf)); try vkd.beginCommandBuffer(cmdbuf, &.{ .flags = .{ .one_time_submit_bit = true }, }); const region = vk.BufferCopy{ .src_offset = 0, .dst_offset = 0, .size = size, }; vkd.cmdCopyBuffer(cmdbuf, src, dst, 1, @ptrCast(®ion)); try vkd.endCommandBuffer(cmdbuf); const si = vk.SubmitInfo{ .command_buffer_count = 1, .p_command_buffers = @ptrCast(&cmdbuf), .p_wait_dst_stage_mask = undefined, }; // creating and submitting a queue for every copy operation seems a bad idea for "streamed" data // gonna want a way to send a copy operation WITH SYNCHRONIZATION PRIMITIVES on a particular queue // see https://stackoverflow.com/a/62183243 // // this may be a misunderstanding on how submission works... try vkd.queueSubmit(queue, 1, @ptrCast(&si), .null_handle); try vkd.queueWaitIdle(queue); } pub const VkAllocator = struct { memory_types: [vk.MAX_MEMORY_TYPES]vk.MemoryType, memory_type_count: u32, pub fn init( pdev: vk.PhysicalDevice, vki: Instance.Wrapper, ) VkAllocator { const props = vki.getPhysicalDeviceMemoryProperties(pdev); return VkAllocator{ .memory_types = props.memory_types, .memory_type_count = props.memory_type_count, }; } pub fn alloc( self: VkAllocator, dev: vk.Device, vkd: Device.Wrapper, reqs: vk.MemoryRequirements, flags: vk.MemoryPropertyFlags, ) !vk.DeviceMemory { const memory_type_bits = reqs.memory_type_bits; for (self.memory_types[0..self.memory_type_count], 0..) |mem_type, idx| { if (memory_type_bits & (@as(u32, 1) << @truncate(idx)) != 0 and mem_type.property_flags.contains(flags)) { return try vkd.allocateMemory(dev, &.{ .allocation_size = reqs.size, .memory_type_index = @intCast(idx), }, null); } } return error.NoSuitableMemoryType; } };