From 009e74061078a6c573aee8dd67424f2d599e4622 Mon Sep 17 00:00:00 2001 From: chyyran Date: Thu, 9 Feb 2023 18:11:34 -0500 Subject: [PATCH] vk: use gpu-allocator for memory allocations --- Cargo.lock | 59 ++++++++ README.md | 2 +- librashader-runtime-vk/Cargo.toml | 3 +- librashader-runtime-vk/src/draw_quad.rs | 22 +-- librashader-runtime-vk/src/error.rs | 5 + librashader-runtime-vk/src/filter_chain.rs | 36 +++-- librashader-runtime-vk/src/filter_pass.rs | 2 +- .../src/hello_triangle/mod.rs | 6 +- .../src/hello_triangle/swapchain.rs | 33 +---- .../src/hello_triangle/vulkan_base.rs | 17 ++- librashader-runtime-vk/src/lib.rs | 2 +- librashader-runtime-vk/src/luts.rs | 29 ++-- .../src/{vulkan_primitives.rs => memory.rs} | 140 +++++++++--------- librashader-runtime-vk/src/texture.rs | 29 ++-- librashader-runtime-vk/src/ubo_ring.rs | 70 --------- librashader-runtime-vk/src/util.rs | 20 +++ 16 files changed, 228 insertions(+), 247 deletions(-) rename librashader-runtime-vk/src/{vulkan_primitives.rs => memory.rs} (57%) delete mode 100644 librashader-runtime-vk/src/ubo_ring.rs diff --git a/Cargo.lock b/Cargo.lock index 5ca5af3..ed22f39 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" @@ -64,6 +73,21 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "bit-set" version = "0.5.3" @@ -564,6 +588,12 @@ dependencies = [ "weezl", ] +[[package]] +name = "gimli" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "221996f774192f0f718773def8201c4ae31f02616a54ccfc2d358bb0e5cefdec" + [[package]] name = "gl" version = "0.14.0" @@ -625,6 +655,18 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "gpu-allocator" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce95f9e2e11c2c6fadfce42b5af60005db06576f231f5c92550fdded43c423e8" +dependencies = [ + "ash", + "backtrace", + "log", + "thiserror", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -930,6 +972,7 @@ dependencies = [ "ash-window", "bytemuck", "glfw 0.49.1", + "gpu-allocator", "librashader-common 0.1.0-rc.2", "librashader-preprocess", "librashader-presets 0.1.0-rc.2", @@ -937,6 +980,7 @@ dependencies = [ "librashader-runtime", "librashader-spirv-cross", "num", + "parking_lot", "raw-window-handle 0.5.0", "rayon", "rustc-hash", @@ -1290,6 +1334,15 @@ dependencies = [ "malloc_buf", ] +[[package]] +name = "object" +version = "0.30.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.17.0" @@ -1483,6 +1536,12 @@ dependencies = [ "spirv", ] +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + [[package]] name = "rustc-hash" version = "1.1.0" diff --git a/README.md b/README.md index 19fdc52..0737118 100644 --- a/README.md +++ b/README.md @@ -121,7 +121,7 @@ Please report an issue if you run into a shader that works in RetroArch, but not * The Vulkan runtime uses [`VK_KHR_dynamic_rendering`](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_dynamic_rendering.html) by default. This extension must be enabled at device creation. Explicit render passes can be used by configuring filter chain options, but may have reduced performance compared to dynamic rendering. - * UBOs use multiple discontiguous buffers. This may be improved in the future by switching to VMA rather than manually handling allocations. + * Allocations within the runtime are done through [gpu-allocator](https://github.com/Traverse-Research/gpu-allocator) rather than handled manually. * Direct3D 11 * Framebuffer copies are done via `ID3D11DeviceContext::CopySubresourceRegion` rather than a CPU conversion + copy. * Direct3D 12 diff --git a/librashader-runtime-vk/Cargo.toml b/librashader-runtime-vk/Cargo.toml index ac47014..8caab2e 100644 --- a/librashader-runtime-vk/Cargo.toml +++ b/librashader-runtime-vk/Cargo.toml @@ -24,7 +24,8 @@ rustc-hash = "1.1.0" bytemuck = "1.12.3" thiserror = "1.0.37" ash = { version = "0.37.1+1.3.235", features = ["linked", "debug"] } - +gpu-allocator = { version = "0.22.0", default-features = false, features = ["vulkan"] } +parking_lot = "0.12.1" rayon = "1.6.1" [dev-dependencies] diff --git a/librashader-runtime-vk/src/draw_quad.rs b/librashader-runtime-vk/src/draw_quad.rs index 2859e41..0658306 100644 --- a/librashader-runtime-vk/src/draw_quad.rs +++ b/librashader-runtime-vk/src/draw_quad.rs @@ -1,7 +1,9 @@ use crate::error; -use crate::vulkan_primitives::VulkanBuffer; +use crate::memory::VulkanBuffer; use ash::vk; +use gpu_allocator::vulkan::Allocator; use librashader_runtime::quad::QuadType; +use parking_lot::RwLock; use std::sync::Arc; #[rustfmt::skip] @@ -30,25 +32,23 @@ pub struct DrawQuad { impl DrawQuad { pub fn new( device: &Arc, - mem_props: &vk::PhysicalDeviceMemoryProperties, + allocator: &Arc>, ) -> error::Result { let mut buffer = VulkanBuffer::new( device, - mem_props, + allocator, vk::BufferUsageFlags::VERTEX_BUFFER, 2 * std::mem::size_of::<[f32; 16]>(), )?; { - let mut map = buffer.map()?; - unsafe { - map.copy_from(0, bytemuck::cast_slice(VBO_OFFSCREEN)); - map.copy_from( - std::mem::size_of::<[f32; 16]>(), - bytemuck::cast_slice(VBO_DEFAULT_FINAL), - ); - } + let slice = buffer.as_mut_slice()?; + slice[0..std::mem::size_of::<[f32; 16]>()] + .copy_from_slice(bytemuck::cast_slice(VBO_OFFSCREEN)); + slice[std::mem::size_of::<[f32; 16]>()..] + .copy_from_slice(bytemuck::cast_slice(VBO_DEFAULT_FINAL)); } + Ok(DrawQuad { buffer, device: device.clone(), diff --git a/librashader-runtime-vk/src/error.rs b/librashader-runtime-vk/src/error.rs index 4633ac0..d718c7f 100644 --- a/librashader-runtime-vk/src/error.rs +++ b/librashader-runtime-vk/src/error.rs @@ -1,4 +1,5 @@ //! Vulkan shader runtime errors. +use gpu_allocator::AllocationError; use librashader_preprocess::PreprocessError; use librashader_presets::ParsePresetError; use librashader_reflect::error::{ShaderCompileError, ShaderReflectError}; @@ -24,6 +25,10 @@ pub enum FilterChainError { VulkanResult(#[from] ash::vk::Result), #[error("could not find a valid vulkan memory type")] VulkanMemoryError(u32), + #[error("could not allocate gpu memory")] + AllocationError(#[from] AllocationError), + #[error("allocation is already freed")] + AllocationDoesNotExist, } /// Result type for Vulkan filter chains. diff --git a/librashader-runtime-vk/src/filter_chain.rs b/librashader-runtime-vk/src/filter_chain.rs index fd8df63..135252d 100644 --- a/librashader-runtime-vk/src/filter_chain.rs +++ b/librashader-runtime-vk/src/filter_chain.rs @@ -4,15 +4,16 @@ use crate::filter_pass::FilterPass; use crate::framebuffer::OutputImage; use crate::graphics_pipeline::VulkanGraphicsPipeline; use crate::luts::LutTexture; +use crate::memory::RawVulkanBuffer; use crate::options::{FilterChainOptionsVulkan, FrameOptionsVulkan}; use crate::queue_selection::get_graphics_queue; use crate::samplers::SamplerSet; use crate::texture::{InputImage, OwnedImage, OwnedImageLayout, VulkanImage}; -use crate::vulkan_primitives::RawVulkanBuffer; use crate::{error, util}; use ash::vk; use librashader_common::{ImageFormat, Size, Viewport}; +use gpu_allocator::vulkan::Allocator; use librashader_presets::{ShaderPreset, TextureConfig}; use librashader_reflect::back::targets::SPIRV; use librashader_reflect::back::{CompileReflectShader, CompileShader}; @@ -24,6 +25,7 @@ use librashader_runtime::binding::BindingUtil; use librashader_runtime::image::{Image, UVDirection}; use librashader_runtime::quad::QuadType; use librashader_runtime::uniforms::UniformStorage; +use parking_lot::RwLock; use rustc_hash::FxHashMap; use std::collections::VecDeque; use std::path::Path; @@ -36,9 +38,10 @@ use rayon::prelude::*; /// A Vulkan device and metadata that is required by the shader runtime. pub struct VulkanObjects { pub(crate) device: Arc, - pub(crate) memory_properties: vk::PhysicalDeviceMemoryProperties, + pub(crate) alloc: Arc>, queue: vk::Queue, pipeline_cache: vk::PipelineCache, + // pub(crate) memory_properties: vk::PhysicalDeviceMemoryProperties, } type ShaderPassMeta = @@ -75,26 +78,28 @@ impl TryFrom for VulkanObjects { device.create_pipeline_cache(&vk::PipelineCacheCreateInfo::default(), None)?; let queue = get_graphics_queue(&instance, &device, vulkan.physical_device); - let memory_properties = - instance.get_physical_device_memory_properties(vulkan.physical_device); + + // let memory_properties = + // instance.get_physical_device_memory_properties(vulkan.physical_device); + + let alloc = util::create_allocator(device.clone(), instance, vulkan.physical_device)?; Ok(VulkanObjects { device: Arc::new(device), + alloc, queue, pipeline_cache, - memory_properties, + // memory_properties, // debug, }) } } } -impl TryFrom<(vk::PhysicalDevice, ash::Instance, Arc)> for VulkanObjects { +impl TryFrom<(vk::PhysicalDevice, ash::Instance, ash::Device)> for VulkanObjects { type Error = FilterChainError; - fn try_from( - value: (vk::PhysicalDevice, ash::Instance, Arc), - ) -> error::Result { + fn try_from(value: (vk::PhysicalDevice, ash::Instance, ash::Device)) -> error::Result { unsafe { let device = value.2; @@ -103,13 +108,16 @@ impl TryFrom<(vk::PhysicalDevice, ash::Instance, Arc)> for VulkanOb let queue = get_graphics_queue(&value.1, &device, value.0); - let memory_properties = value.1.get_physical_device_memory_properties(value.0); + // let memory_properties = value.1.get_physical_device_memory_properties(value.0); + + let alloc = util::create_allocator(device.clone(), value.1, value.0)?; Ok(VulkanObjects { - device, + alloc, + device: Arc::new(device), queue, pipeline_cache, - memory_properties, + // memory_properties, // debug: value.3, }) } @@ -288,7 +296,7 @@ impl FilterChainVulkan { .map(|param| (param.name, param.value)) .collect(), }, - draw_quad: DrawQuad::new(&device.device, &device.memory_properties)?, + draw_quad: DrawQuad::new(&device.device, &device.alloc)?, device: device.device.clone(), output_inputs: output_textures.into_boxed_slice(), feedback_inputs: feedback_textures.into_boxed_slice(), @@ -324,7 +332,7 @@ impl FilterChainVulkan { let uniform_storage = UniformStorage::new_with_ubo_storage( RawVulkanBuffer::new( &vulkan.device, - &vulkan.memory_properties, + &vulkan.alloc, vk::BufferUsageFlags::UNIFORM_BUFFER, ubo_size, )?, diff --git a/librashader-runtime-vk/src/filter_pass.rs b/librashader-runtime-vk/src/filter_pass.rs index a17a275..5b1a76f 100644 --- a/librashader-runtime-vk/src/filter_pass.rs +++ b/librashader-runtime-vk/src/filter_pass.rs @@ -1,9 +1,9 @@ use crate::filter_chain::FilterCommon; use crate::framebuffer::OutputImage; use crate::graphics_pipeline::VulkanGraphicsPipeline; +use crate::memory::RawVulkanBuffer; use crate::samplers::SamplerSet; use crate::texture::InputImage; -use crate::vulkan_primitives::RawVulkanBuffer; use crate::{error, VulkanImage}; use ash::vk; use librashader_common::{ImageFormat, Size, Viewport}; diff --git a/librashader-runtime-vk/src/hello_triangle/mod.rs b/librashader-runtime-vk/src/hello_triangle/mod.rs index b6479f5..7eb8edc 100644 --- a/librashader-runtime-vk/src/hello_triangle/mod.rs +++ b/librashader-runtime-vk/src/hello_triangle/mod.rs @@ -22,6 +22,7 @@ use ash::vk; use librashader_common::Viewport; +use crate::options::FrameOptionsVulkan; use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop, EventLoopBuilder}; use winit::platform::windows::EventLoopBuilderExtWindows; @@ -256,7 +257,10 @@ impl VulkanWindow { }, cmd, frame, - None, + Some(&FrameOptionsVulkan { + clear_history: frame == 0, + frame_direction: 0, + }), ) .unwrap(); diff --git a/librashader-runtime-vk/src/hello_triangle/swapchain.rs b/librashader-runtime-vk/src/hello_triangle/swapchain.rs index d52351b..0977c3e 100644 --- a/librashader-runtime-vk/src/hello_triangle/swapchain.rs +++ b/librashader-runtime-vk/src/hello_triangle/swapchain.rs @@ -1,7 +1,6 @@ use crate::hello_triangle::surface::VulkanSurface; use crate::hello_triangle::vulkan_base::VulkanBase; -use crate::util::find_vulkan_memory_type; -use crate::vulkan_primitives::VulkanImageMemory; +use crate::memory::VulkanImageMemory; use ash::prelude::VkResult; use ash::vk; use ash::vk::Extent3D; @@ -95,33 +94,9 @@ impl VulkanSwapchain { let image = base.device.create_image(&create_info, None)?; let mem_reqs = unsafe { base.device.get_image_memory_requirements(image) }; - // base.debug - // .loader - // .set_debug_utils_object_name( - // base.device.handle(), - // &vk::DebugUtilsObjectNameInfoEXT::builder() - // .object_handle(image.as_raw()) - // .object_name(CStr::from_bytes_with_nul_unchecked(b"RenderImage\0")) - // .object_type(vk::ObjectType::IMAGE) - // .build(), - // ) - // .expect("could not set object name"); - - let alloc_info = vk::MemoryAllocateInfo::builder() - .allocation_size(mem_reqs.size) - .memory_type_index( - find_vulkan_memory_type( - &base.mem_props, - mem_reqs.memory_type_bits, - vk::MemoryPropertyFlags::DEVICE_LOCAL, - ) - .unwrap(), - ) - .build(); - - // todo: optimize by reusing existing memory. - let memory = VulkanImageMemory::new(&base.device, &alloc_info).unwrap(); - memory.bind(&image).unwrap(); + let memory = + VulkanImageMemory::new(&base.device, &base.allocator, mem_reqs, &image) + .unwrap(); render_images.push((image, memory)) } diff --git a/librashader-runtime-vk/src/hello_triangle/vulkan_base.rs b/librashader-runtime-vk/src/hello_triangle/vulkan_base.rs index 9835b30..2baca1a 100644 --- a/librashader-runtime-vk/src/hello_triangle/vulkan_base.rs +++ b/librashader-runtime-vk/src/hello_triangle/vulkan_base.rs @@ -5,7 +5,10 @@ use crate::filter_chain::VulkanObjects; use crate::hello_triangle::physicaldevice::{find_queue_family, pick_physical_device}; +use crate::util; use ash::prelude::VkResult; +use gpu_allocator::vulkan::{Allocator, AllocatorCreateDesc}; +use parking_lot::RwLock; use std::ffi::CStr; use std::sync::Arc; @@ -20,6 +23,7 @@ pub struct VulkanBase { // pub debug: VulkanDebug, pub physical_device: vk::PhysicalDevice, pub mem_props: vk::PhysicalDeviceMemoryProperties, + pub allocator: Arc>, } impl VulkanBase { @@ -61,6 +65,10 @@ impl VulkanBase { let mem_props = unsafe { instance.get_physical_device_memory_properties(physical_device) }; dbg!("got memprops"); + let alloc = + util::create_allocator(device.clone(), instance.clone(), physical_device.clone()) + .expect("could not create allocator"); + Ok(VulkanBase { entry, instance, @@ -69,6 +77,7 @@ impl VulkanBase { physical_device, mem_props, // debug, + allocator: alloc, }) } @@ -161,10 +170,8 @@ impl TryFrom<&VulkanBase> for VulkanObjects { type Error = FilterChainError; fn try_from(value: &VulkanBase) -> Result { - VulkanObjects::try_from(( - value.physical_device, - value.instance.clone(), - value.device.clone(), - )) + let device = (*value.device).clone(); + + VulkanObjects::try_from((value.physical_device, value.instance.clone(), device)) } } diff --git a/librashader-runtime-vk/src/lib.rs b/librashader-runtime-vk/src/lib.rs index c93ce27..3ca5968 100644 --- a/librashader-runtime-vk/src/lib.rs +++ b/librashader-runtime-vk/src/lib.rs @@ -15,12 +15,12 @@ mod graphics_pipeline; #[cfg(test)] mod hello_triangle; mod luts; +mod memory; mod parameters; mod queue_selection; mod samplers; mod texture; mod util; -mod vulkan_primitives; pub use filter_chain::FilterChainVulkan; pub use filter_chain::VulkanInstance; diff --git a/librashader-runtime-vk/src/luts.rs b/librashader-runtime-vk/src/luts.rs index fd6cfa2..7201f15 100644 --- a/librashader-runtime-vk/src/luts.rs +++ b/librashader-runtime-vk/src/luts.rs @@ -1,6 +1,6 @@ use crate::filter_chain::VulkanObjects; +use crate::memory::{VulkanBuffer, VulkanImageMemory}; use crate::texture::{InputImage, VulkanImage}; -use crate::vulkan_primitives::{VulkanBuffer, VulkanImageMemory}; use crate::{error, util}; use ash::vk; use librashader_presets::TextureConfig; @@ -46,21 +46,14 @@ impl LutTexture { let memory = unsafe { let mem_reqs = vulkan.device.get_image_memory_requirements(texture); - let mem_type = util::find_vulkan_memory_type( - &vulkan.memory_properties, - mem_reqs.memory_type_bits, - vk::MemoryPropertyFlags::DEVICE_LOCAL, - )?; - VulkanImageMemory::new( - &vulkan.device, - &vk::MemoryAllocateInfo::builder() - .memory_type_index(mem_type) - .allocation_size(mem_reqs.size), - )? + // let mem_type = util::find_vulkan_memory_type( + // &vulkan.memory_properties, + // mem_reqs.memory_type_bits, + // vk::MemoryPropertyFlags::DEVICE_LOCAL, + // )?; + VulkanImageMemory::new(&vulkan.device, &vulkan.alloc, mem_reqs, &texture)? }; - memory.bind(&texture)?; - let image_subresource = vk::ImageSubresourceRange::builder() .level_count(image_info.mip_levels) .layer_count(1) @@ -86,14 +79,12 @@ impl LutTexture { let mut staging = VulkanBuffer::new( &vulkan.device, - &vulkan.memory_properties, + &vulkan.alloc, vk::BufferUsageFlags::TRANSFER_SRC, image.bytes.len(), )?; - unsafe { - let mut handle = staging.map()?; - handle.copy_from(0, &image.bytes) - } + + staging.as_mut_slice()?.copy_from_slice(&image.bytes); unsafe { util::vulkan_image_layout_transition_levels( diff --git a/librashader-runtime-vk/src/vulkan_primitives.rs b/librashader-runtime-vk/src/memory.rs similarity index 57% rename from librashader-runtime-vk/src/vulkan_primitives.rs rename to librashader-runtime-vk/src/memory.rs index 35a9b10..67f7141 100644 --- a/librashader-runtime-vk/src/vulkan_primitives.rs +++ b/librashader-runtime-vk/src/memory.rs @@ -1,6 +1,11 @@ -use crate::{error, util}; +use crate::error; +use crate::error::FilterChainError; use ash::vk; +use gpu_allocator::vulkan::{Allocation, AllocationCreateDesc, AllocationScheme, Allocator}; +use gpu_allocator::MemoryLocation; use librashader_runtime::uniforms::UniformStorageAccess; +use parking_lot::RwLock; + use std::ffi::c_void; use std::mem::ManuallyDrop; use std::ops::{Deref, DerefMut}; @@ -8,32 +13,46 @@ use std::ptr::NonNull; use std::sync::Arc; pub struct VulkanImageMemory { - pub handle: vk::DeviceMemory, + allocation: Option, + allocator: Arc>, device: Arc, } impl VulkanImageMemory { pub fn new( device: &Arc, - alloc: &vk::MemoryAllocateInfo, + allocator: &Arc>, + requirements: vk::MemoryRequirements, + image: &vk::Image, ) -> error::Result { + let allocation = allocator.write().allocate(&AllocationCreateDesc { + name: "imagemem", + requirements, + location: MemoryLocation::GpuOnly, + linear: false, + allocation_scheme: AllocationScheme::DedicatedImage(*image), + })?; + unsafe { + device.bind_image_memory(*image, allocation.memory(), 0)?; Ok(VulkanImageMemory { - handle: device.allocate_memory(alloc, None)?, - device: device.clone(), + allocation: Some(allocation), + allocator: Arc::clone(allocator), + device: Arc::clone(device), }) } } - - pub fn bind(&self, image: &vk::Image) -> error::Result<()> { - unsafe { Ok(self.device.bind_image_memory(*image, self.handle, 0)?) } - } } impl Drop for VulkanImageMemory { fn drop(&mut self) { unsafe { - self.device.free_memory(self.handle, None); + let allocation = self.allocation.take(); + if let Some(allocation) = allocation { + if let Err(e) = self.allocator.write().free(allocation) { + println!("librashader-runtime-vk: [warn] failed to deallocate image buffer {e}") + } + } } } } @@ -41,19 +60,15 @@ impl Drop for VulkanImageMemory { pub struct VulkanBuffer { pub handle: vk::Buffer, device: Arc, - memory: vk::DeviceMemory, + memory: Option, + allocator: Arc>, size: vk::DeviceSize, } -pub struct VulkanBufferMapHandle<'a> { - buffer: &'a mut VulkanBuffer, - ptr: *mut c_void, -} - impl VulkanBuffer { pub fn new( device: &Arc, - mem_props: &vk::PhysicalDeviceMemoryProperties, + allocator: &Arc>, usage: vk::BufferUsageFlags, size: usize, ) -> error::Result { @@ -66,45 +81,48 @@ impl VulkanBuffer { let buffer = device.create_buffer(&buffer_info, None)?; let memory_reqs = device.get_buffer_memory_requirements(buffer); - let alloc_info = vk::MemoryAllocateInfo::builder() - .allocation_size(memory_reqs.size) - .memory_type_index(util::find_vulkan_memory_type( - mem_props, - memory_reqs.memory_type_bits, - vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT, - )?) - .build(); - let alloc = device.allocate_memory(&alloc_info, None)?; - device.bind_buffer_memory(buffer, alloc, 0)?; + let alloc = allocator.write().allocate(&AllocationCreateDesc { + name: "buffer", + requirements: memory_reqs, + location: MemoryLocation::CpuToGpu, + linear: true, + allocation_scheme: AllocationScheme::DedicatedBuffer(buffer), + })?; + + // let alloc = device.allocate_memory(&alloc_info, None)?; + device.bind_buffer_memory(buffer, alloc.memory(), 0)?; Ok(VulkanBuffer { handle: buffer, - memory: alloc, + memory: Some(alloc), + allocator: Arc::clone(allocator), size: size as vk::DeviceSize, device: device.clone(), }) } } - pub fn map(&mut self) -> error::Result { - let dst = unsafe { - self.device - .map_memory(self.memory, 0, self.size, vk::MemoryMapFlags::empty())? + pub fn as_mut_slice(&mut self) -> error::Result<&mut [u8]> { + let Some(allocation) = self.memory.as_mut() else { + return Err(FilterChainError::AllocationDoesNotExist) }; - - Ok(VulkanBufferMapHandle { - buffer: self, - ptr: dst, - }) + let Some(allocation) = allocation.mapped_slice_mut() else { + return Err(FilterChainError::AllocationDoesNotExist) + }; + Ok(allocation) } } impl Drop for VulkanBuffer { fn drop(&mut self) { unsafe { - if self.memory != vk::DeviceMemory::null() { - self.device.free_memory(self.memory, None); + if let Some(allocation) = self.memory.take() { + if let Err(e) = self.allocator.write().free(allocation) { + println!( + "librashader-runtime-vk: [warn] failed to deallocate buffer memory {e}" + ) + } } if self.handle != vk::Buffer::null() { @@ -114,26 +132,6 @@ impl Drop for VulkanBuffer { } } -impl<'a> VulkanBufferMapHandle<'a> { - pub unsafe fn copy_from(&mut self, offset: usize, src: &[u8]) { - let mut align = ash::util::Align::new( - self.ptr - .map_addr(|original| original.wrapping_add(offset)) - .cast(), - std::mem::align_of::() as u64, - self.buffer.size, - ); - - align.copy_from_slice(src); - } -} - -impl<'a> Drop for VulkanBufferMapHandle<'a> { - fn drop(&mut self) { - unsafe { self.buffer.device.unmap_memory(self.buffer.memory) } - } -} - /// SAFETY: Creating the pointer should be safe in multithreaded contexts. /// /// Mutation is guarded by DerefMut @@ -146,18 +144,17 @@ pub struct RawVulkanBuffer { impl RawVulkanBuffer { pub fn new( device: &Arc, - mem_props: &vk::PhysicalDeviceMemoryProperties, + allocator: &Arc>, usage: vk::BufferUsageFlags, size: usize, ) -> error::Result { - let buffer = ManuallyDrop::new(VulkanBuffer::new(device, mem_props, usage, size)?); - let ptr = unsafe { - NonNull::new_unchecked(device.map_memory( - buffer.memory, - 0, - buffer.size, - vk::MemoryMapFlags::empty(), - )?) + let buffer = ManuallyDrop::new(VulkanBuffer::new(device, allocator, usage, size)?); + + let Some(ptr) = buffer.memory + .as_ref() + .map(|m| m.mapped_ptr()) + .flatten() else { + return Err(FilterChainError::AllocationDoesNotExist) }; Ok(RawVulkanBuffer { buffer, ptr }) @@ -193,12 +190,7 @@ impl RawVulkanBuffer { impl Drop for RawVulkanBuffer { fn drop(&mut self) { unsafe { - self.buffer.device.unmap_memory(self.buffer.memory); - self.ptr = NonNull::dangling(); - if self.buffer.memory != vk::DeviceMemory::null() { - self.buffer.device.free_memory(self.buffer.memory, None); - } - + ManuallyDrop::drop(&mut self.buffer); if self.buffer.handle != vk::Buffer::null() { self.buffer.device.destroy_buffer(self.buffer.handle, None); } diff --git a/librashader-runtime-vk/src/texture.rs b/librashader-runtime-vk/src/texture.rs index e3dd7bd..b43c4b0 100644 --- a/librashader-runtime-vk/src/texture.rs +++ b/librashader-runtime-vk/src/texture.rs @@ -1,8 +1,9 @@ use crate::filter_chain::VulkanObjects; -use crate::util::find_vulkan_memory_type; -use crate::vulkan_primitives::VulkanImageMemory; +use crate::memory::VulkanImageMemory; use crate::{error, util}; use ash::vk; +use gpu_allocator::vulkan::Allocator; +use parking_lot::RwLock; use std::sync::Arc; use crate::error::FilterChainError; @@ -12,7 +13,7 @@ use librashader_runtime::scaling::{MipmapSize, ScaleFramebuffer, ViewportSize}; pub struct OwnedImage { pub device: Arc, - pub mem_props: vk::PhysicalDeviceMemoryProperties, + pub allocator: Arc>, pub image_view: vk::ImageView, pub image: VulkanImage, pub memory: VulkanImageMemory, @@ -32,7 +33,7 @@ pub struct OwnedImageLayout { impl OwnedImage { fn new_internal( device: Arc, - mem_props: vk::PhysicalDeviceMemoryProperties, + alloc: &Arc>, size: Size, mut format: ImageFormat, max_miplevels: u32, @@ -63,19 +64,7 @@ impl OwnedImage { let image = unsafe { device.create_image(&image_create_info, None)? }; let mem_reqs = unsafe { device.get_image_memory_requirements(image) }; - let alloc_info = vk::MemoryAllocateInfo::builder() - .allocation_size(mem_reqs.size) - .memory_type_index(find_vulkan_memory_type( - &mem_props, - mem_reqs.memory_type_bits, - vk::MemoryPropertyFlags::DEVICE_LOCAL, - )?) - .build(); - - // todo: optimize by reusing existing memory. - let memory = VulkanImageMemory::new(&device, &alloc_info)?; - memory.bind(&image)?; - + let memory = VulkanImageMemory::new(&device, alloc, mem_reqs, &image)?; let image_subresource = vk::ImageSubresourceRange::builder() .base_mip_level(0) .base_array_layer(0) @@ -103,7 +92,7 @@ impl OwnedImage { Ok(OwnedImage { device, - mem_props, + allocator: Arc::clone(alloc), image_view, image: VulkanImage { image, @@ -124,7 +113,7 @@ impl OwnedImage { ) -> error::Result { Self::new_internal( vulkan.device.clone(), - vulkan.memory_properties, + &vulkan.alloc, size, format, max_miplevels, @@ -150,7 +139,7 @@ impl OwnedImage { let new = OwnedImage::new_internal( self.device.clone(), - self.mem_props, + &self.allocator, size, if format == ImageFormat::Unknown { ImageFormat::R8G8B8A8Unorm diff --git a/librashader-runtime-vk/src/ubo_ring.rs b/librashader-runtime-vk/src/ubo_ring.rs deleted file mode 100644 index ab8b412..0000000 --- a/librashader-runtime-vk/src/ubo_ring.rs +++ /dev/null @@ -1,70 +0,0 @@ -use crate::error; -use crate::vulkan_primitives::VulkanBuffer; -use ash::vk; -use librashader_runtime::ringbuffer::{BoxRingBuffer, RingBuffer}; -use librashader_runtime::uniforms::UniformStorageAccess; -use std::sync::Arc; - -pub struct VkUboRing { - ring: BoxRingBuffer, - device: Arc, -} - -impl VkUboRing { - pub fn new( - device: &Arc, - mem_props: &vk::PhysicalDeviceMemoryProperties, - ring_size: usize, - buffer_size: usize, - ) -> error::Result { - let mut ring = Vec::new(); - for _ in 0..ring_size { - ring.push(VulkanBuffer::new( - device, - mem_props, - vk::BufferUsageFlags::UNIFORM_BUFFER, - buffer_size, - )?); - } - - Ok(VkUboRing { - ring: BoxRingBuffer::from_vec(ring), - device: device.clone(), - }) - } - - pub fn bind_to_descriptor_set( - &mut self, - descriptor_set: vk::DescriptorSet, - binding: u32, - storage: &impl UniformStorageAccess, - ) -> error::Result<()> { - // todo: write directly to allocated buffer. - unsafe { - let buffer = self.ring.current_mut(); - let mut map = buffer.map()?; - map.copy_from(0, storage.ubo_slice()) - } - - let buffer = self.ring.current(); - unsafe { - let buffer_info = [vk::DescriptorBufferInfo::builder() - .buffer(buffer.handle) - .offset(0) - .range(storage.ubo_slice().len() as vk::DeviceSize) - .build()]; - - let write_info = [vk::WriteDescriptorSet::builder() - .descriptor_type(vk::DescriptorType::UNIFORM_BUFFER) - .dst_set(descriptor_set) - .dst_binding(binding) - .dst_array_element(0) - .buffer_info(&buffer_info) - .build()]; - - self.device.update_descriptor_sets(&write_info, &[]) - } - self.ring.next(); - Ok(()) - } -} diff --git a/librashader-runtime-vk/src/util.rs b/librashader-runtime-vk/src/util.rs index 2d7ccbe..07cccfc 100644 --- a/librashader-runtime-vk/src/util.rs +++ b/librashader-runtime-vk/src/util.rs @@ -1,4 +1,8 @@ use ash::vk; +use gpu_allocator::vulkan::{Allocator, AllocatorCreateDesc}; +use gpu_allocator::AllocatorDebugSettings; +use parking_lot::RwLock; +use std::sync::Arc; use crate::error; use crate::error::FilterChainError; @@ -17,6 +21,7 @@ pub fn binding_stage_to_vulkan_stage(stage_mask: BindingStage) -> vk::ShaderStag mask } +#[allow(unused)] pub fn find_vulkan_memory_type( props: &vk::PhysicalDeviceMemoryProperties, device_reqs: u32, @@ -81,3 +86,18 @@ pub unsafe fn vulkan_image_layout_transition_levels( &[barrier], ) } + +pub fn create_allocator( + device: ash::Device, + instance: ash::Instance, + physical_device: vk::PhysicalDevice, +) -> error::Result>> { + let alloc = Allocator::new(&AllocatorCreateDesc { + instance, + device, + physical_device, + debug_settings: Default::default(), + buffer_device_address: false, + })?; + Ok(Arc::new(RwLock::new(alloc))) +}