From 698c9493c89fb0c41eb7a10186c6befc7ad01d79 Mon Sep 17 00:00:00 2001 From: Irene Knapp Date: Thu, 6 Aug 2026 17:10:19 -0700 Subject: further splitting of graphics submodules now this is starting to feel like halfway decent organization Force-Push: yes Change-Id: I45cc4afb45e1528705680eb4192c1e9ba72f53fb --- src/graphics/util.rs | 173 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 src/graphics/util.rs (limited to 'src/graphics/util.rs') diff --git a/src/graphics/util.rs b/src/graphics/util.rs new file mode 100644 index 0000000..2debc4e --- /dev/null +++ b/src/graphics/util.rs @@ -0,0 +1,173 @@ +#![allow(unsafe_code)] +use crate::error::*; + +use std::mem::size_of; +use std::ptr::copy_nonoverlapping; + +use vulkanalia::{ Device, Instance }; +use vulkanalia::vk::{ self, Handle, HasBuilder, InstanceV1_0, DeviceV1_0 }; + + +pub fn stage_in_buffer(instance: &Instance, + physical_device: &vk::PhysicalDevice, + device: &Device, contents: &[T]) + -> Result<(vk::Buffer, vk::DeviceMemory, usize)> +{ + let size = size_of::() * contents.len(); + + let staging_usage = vk::BufferUsageFlags::TRANSFER_SRC; + let staging_memory_flags = vk::MemoryPropertyFlags::HOST_COHERENT + | vk::MemoryPropertyFlags::HOST_VISIBLE; + let (staging_buffer, staging_memory) + = allocate_buffer(instance, physical_device, device, + size as vk::DeviceSize, staging_usage, + staging_memory_flags)?; + + let host_memory = unsafe { + device.map_memory(staging_memory, 0, size as vk::DeviceSize, + vk::MemoryMapFlags::empty()) + }?; + + unsafe { + copy_nonoverlapping(contents.as_ptr(), host_memory.cast(), contents.len()) + }; + + unsafe { device.unmap_memory(staging_memory) }; + + Ok((staging_buffer, staging_memory, size)) +} + + +#[allow(unsafe_code)] +pub fn allocate_buffer(instance: &Instance, + physical_device: &vk::PhysicalDevice, device: &Device, + size: vk::DeviceSize, usage: vk::BufferUsageFlags, + memory_flags: vk::MemoryPropertyFlags) + -> Result<(vk::Buffer, vk::DeviceMemory)> +{ + let buffer_info = vk::BufferCreateInfo::builder() + .size(size) + .usage(usage) + .sharing_mode(vk::SharingMode::EXCLUSIVE); + + let buffer = unsafe { device.create_buffer(&buffer_info, None) }?; + + // The requirements are mostly what you'd think: size and alignment. The + // bits field is something special; see pick_memory_type() for the + // explanation. Despite the simplicity of this data, Vulkan wants to be the + // one to tell us about it, and we let it. + let requirements = unsafe { device.get_buffer_memory_requirements(buffer) }; + + let type_index = pick_memory_type(instance, physical_device, + &memory_flags, &requirements)?; + + let memory_info = vk::MemoryAllocateInfo::builder() + .allocation_size(requirements.size) + .memory_type_index(type_index); + + let device_memory = unsafe { device.allocate_memory(&memory_info, None) }?; + + unsafe { device.bind_buffer_memory(buffer, device_memory, 0) }?; + + Ok((buffer, device_memory)) +} + + +#[allow(unsafe_code)] +pub fn copy_buffer(device: &Device, queue: &vk::Queue, + command_pool: &vk::CommandPool, source: &vk::Buffer, + destination: &vk::Buffer, size: vk::DeviceSize) + -> Result<()> +{ + let command_buffer = begin_transient_commands(device, command_pool)?; + + let copy_info = vk::BufferCopy::builder().size(size); + unsafe { + device.cmd_copy_buffer(command_buffer, *source, *destination, + &[copy_info]) + }; + + end_transient_commands(command_buffer, device, queue, command_pool)?; + + Ok(()) +} + + +#[allow(unsafe_code)] +pub fn pick_memory_type(instance: &Instance, + physical_device: &vk::PhysicalDevice, + properties: &vk::MemoryPropertyFlags, + requirements: &vk::MemoryRequirements) + -> Result +{ + let memory_map = unsafe { + instance.get_physical_device_memory_properties(*physical_device) + }; + + // So. The memory_type_bits field is a map of which indices are suitable, + // based on the buffer our caller passed to + // get_buffer_memory_requirements(). Yes, that means there's a hard cap on + // how many memory types there can be, based on the size of the bitfield. + for index in 0 .. memory_map.memory_type_count { + if requirements.memory_type_bits & (1 << index) == 0 { + continue; + } + + let memory_type = memory_map.memory_types[index as usize]; + + if memory_type.property_flags.contains(*properties) { + return Ok(index); + } + } + + Err(Error { + message: "The system has no suitable memory for a buffer.".to_string() + }) +} + + +#[allow(unsafe_code)] +pub fn begin_transient_commands(device: &Device, + command_pool: &vk::CommandPool) + -> Result +{ + let command_buffer_allocation_info + = vk::CommandBufferAllocateInfo::builder() + .command_pool(*command_pool) + .level(vk::CommandBufferLevel::PRIMARY) + .command_buffer_count(1); + let command_buffer = unsafe { + device.allocate_command_buffers(&command_buffer_allocation_info) + }?[0]; + + let command_buffer_begin_info = vk::CommandBufferBeginInfo::builder() + .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT); + + unsafe { + device.begin_command_buffer(command_buffer, &command_buffer_begin_info) + }?; + + Ok(command_buffer) +} + + +#[allow(unsafe_code)] +pub fn end_transient_commands(command_buffer: vk::CommandBuffer, + device: &Device, queue: &vk::Queue, + command_pool: &vk::CommandPool) + -> Result<()> +{ + unsafe { device.end_command_buffer(command_buffer) }?; + + let command_buffers = [command_buffer]; + let submit_info = vk::SubmitInfo::builder() + .command_buffers(&command_buffers); + unsafe { device.queue_submit(*queue, &[submit_info], vk::Fence::null()) }?; + + unsafe { device.queue_wait_idle(*queue) }?; + + unsafe { device.free_command_buffers(*command_pool, &command_buffers) }; + + Ok(()) +} + -- cgit 1.4.1