vk: use gpu-allocator for memory allocations

This commit is contained in:
chyyran 2023-02-09 18:11:34 -05:00
parent 336094cad9
commit 009e740610
16 changed files with 228 additions and 247 deletions

59
Cargo.lock generated
View file

@ -2,6 +2,15 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "addr2line"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97"
dependencies = [
"gimli",
]
[[package]] [[package]]
name = "adler" name = "adler"
version = "1.0.2" version = "1.0.2"
@ -64,6 +73,21 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 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]] [[package]]
name = "bit-set" name = "bit-set"
version = "0.5.3" version = "0.5.3"
@ -564,6 +588,12 @@ dependencies = [
"weezl", "weezl",
] ]
[[package]]
name = "gimli"
version = "0.27.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "221996f774192f0f718773def8201c4ae31f02616a54ccfc2d358bb0e5cefdec"
[[package]] [[package]]
name = "gl" name = "gl"
version = "0.14.0" version = "0.14.0"
@ -625,6 +655,18 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" 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]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.12.3" version = "0.12.3"
@ -930,6 +972,7 @@ dependencies = [
"ash-window", "ash-window",
"bytemuck", "bytemuck",
"glfw 0.49.1", "glfw 0.49.1",
"gpu-allocator",
"librashader-common 0.1.0-rc.2", "librashader-common 0.1.0-rc.2",
"librashader-preprocess", "librashader-preprocess",
"librashader-presets 0.1.0-rc.2", "librashader-presets 0.1.0-rc.2",
@ -937,6 +980,7 @@ dependencies = [
"librashader-runtime", "librashader-runtime",
"librashader-spirv-cross", "librashader-spirv-cross",
"num", "num",
"parking_lot",
"raw-window-handle 0.5.0", "raw-window-handle 0.5.0",
"rayon", "rayon",
"rustc-hash", "rustc-hash",
@ -1290,6 +1334,15 @@ dependencies = [
"malloc_buf", "malloc_buf",
] ]
[[package]]
name = "object"
version = "0.30.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.17.0" version = "1.17.0"
@ -1483,6 +1536,12 @@ dependencies = [
"spirv", "spirv",
] ]
[[package]]
name = "rustc-demangle"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
[[package]] [[package]]
name = "rustc-hash" name = "rustc-hash"
version = "1.1.0" version = "1.1.0"

View file

@ -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. * 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 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. 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 * Direct3D 11
* Framebuffer copies are done via `ID3D11DeviceContext::CopySubresourceRegion` rather than a CPU conversion + copy. * Framebuffer copies are done via `ID3D11DeviceContext::CopySubresourceRegion` rather than a CPU conversion + copy.
* Direct3D 12 * Direct3D 12

View file

@ -24,7 +24,8 @@ rustc-hash = "1.1.0"
bytemuck = "1.12.3" bytemuck = "1.12.3"
thiserror = "1.0.37" thiserror = "1.0.37"
ash = { version = "0.37.1+1.3.235", features = ["linked", "debug"] } 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" rayon = "1.6.1"
[dev-dependencies] [dev-dependencies]

View file

@ -1,7 +1,9 @@
use crate::error; use crate::error;
use crate::vulkan_primitives::VulkanBuffer; use crate::memory::VulkanBuffer;
use ash::vk; use ash::vk;
use gpu_allocator::vulkan::Allocator;
use librashader_runtime::quad::QuadType; use librashader_runtime::quad::QuadType;
use parking_lot::RwLock;
use std::sync::Arc; use std::sync::Arc;
#[rustfmt::skip] #[rustfmt::skip]
@ -30,25 +32,23 @@ pub struct DrawQuad {
impl DrawQuad { impl DrawQuad {
pub fn new( pub fn new(
device: &Arc<ash::Device>, device: &Arc<ash::Device>,
mem_props: &vk::PhysicalDeviceMemoryProperties, allocator: &Arc<RwLock<Allocator>>,
) -> error::Result<DrawQuad> { ) -> error::Result<DrawQuad> {
let mut buffer = VulkanBuffer::new( let mut buffer = VulkanBuffer::new(
device, device,
mem_props, allocator,
vk::BufferUsageFlags::VERTEX_BUFFER, vk::BufferUsageFlags::VERTEX_BUFFER,
2 * std::mem::size_of::<[f32; 16]>(), 2 * std::mem::size_of::<[f32; 16]>(),
)?; )?;
{ {
let mut map = buffer.map()?; let slice = buffer.as_mut_slice()?;
unsafe { slice[0..std::mem::size_of::<[f32; 16]>()]
map.copy_from(0, bytemuck::cast_slice(VBO_OFFSCREEN)); .copy_from_slice(bytemuck::cast_slice(VBO_OFFSCREEN));
map.copy_from( slice[std::mem::size_of::<[f32; 16]>()..]
std::mem::size_of::<[f32; 16]>(), .copy_from_slice(bytemuck::cast_slice(VBO_DEFAULT_FINAL));
bytemuck::cast_slice(VBO_DEFAULT_FINAL),
);
}
} }
Ok(DrawQuad { Ok(DrawQuad {
buffer, buffer,
device: device.clone(), device: device.clone(),

View file

@ -1,4 +1,5 @@
//! Vulkan shader runtime errors. //! Vulkan shader runtime errors.
use gpu_allocator::AllocationError;
use librashader_preprocess::PreprocessError; use librashader_preprocess::PreprocessError;
use librashader_presets::ParsePresetError; use librashader_presets::ParsePresetError;
use librashader_reflect::error::{ShaderCompileError, ShaderReflectError}; use librashader_reflect::error::{ShaderCompileError, ShaderReflectError};
@ -24,6 +25,10 @@ pub enum FilterChainError {
VulkanResult(#[from] ash::vk::Result), VulkanResult(#[from] ash::vk::Result),
#[error("could not find a valid vulkan memory type")] #[error("could not find a valid vulkan memory type")]
VulkanMemoryError(u32), VulkanMemoryError(u32),
#[error("could not allocate gpu memory")]
AllocationError(#[from] AllocationError),
#[error("allocation is already freed")]
AllocationDoesNotExist,
} }
/// Result type for Vulkan filter chains. /// Result type for Vulkan filter chains.

View file

@ -4,15 +4,16 @@ use crate::filter_pass::FilterPass;
use crate::framebuffer::OutputImage; use crate::framebuffer::OutputImage;
use crate::graphics_pipeline::VulkanGraphicsPipeline; use crate::graphics_pipeline::VulkanGraphicsPipeline;
use crate::luts::LutTexture; use crate::luts::LutTexture;
use crate::memory::RawVulkanBuffer;
use crate::options::{FilterChainOptionsVulkan, FrameOptionsVulkan}; use crate::options::{FilterChainOptionsVulkan, FrameOptionsVulkan};
use crate::queue_selection::get_graphics_queue; use crate::queue_selection::get_graphics_queue;
use crate::samplers::SamplerSet; use crate::samplers::SamplerSet;
use crate::texture::{InputImage, OwnedImage, OwnedImageLayout, VulkanImage}; use crate::texture::{InputImage, OwnedImage, OwnedImageLayout, VulkanImage};
use crate::vulkan_primitives::RawVulkanBuffer;
use crate::{error, util}; use crate::{error, util};
use ash::vk; use ash::vk;
use librashader_common::{ImageFormat, Size, Viewport}; use librashader_common::{ImageFormat, Size, Viewport};
use gpu_allocator::vulkan::Allocator;
use librashader_presets::{ShaderPreset, TextureConfig}; use librashader_presets::{ShaderPreset, TextureConfig};
use librashader_reflect::back::targets::SPIRV; use librashader_reflect::back::targets::SPIRV;
use librashader_reflect::back::{CompileReflectShader, CompileShader}; use librashader_reflect::back::{CompileReflectShader, CompileShader};
@ -24,6 +25,7 @@ use librashader_runtime::binding::BindingUtil;
use librashader_runtime::image::{Image, UVDirection}; use librashader_runtime::image::{Image, UVDirection};
use librashader_runtime::quad::QuadType; use librashader_runtime::quad::QuadType;
use librashader_runtime::uniforms::UniformStorage; use librashader_runtime::uniforms::UniformStorage;
use parking_lot::RwLock;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::path::Path; use std::path::Path;
@ -36,9 +38,10 @@ use rayon::prelude::*;
/// A Vulkan device and metadata that is required by the shader runtime. /// A Vulkan device and metadata that is required by the shader runtime.
pub struct VulkanObjects { pub struct VulkanObjects {
pub(crate) device: Arc<ash::Device>, pub(crate) device: Arc<ash::Device>,
pub(crate) memory_properties: vk::PhysicalDeviceMemoryProperties, pub(crate) alloc: Arc<RwLock<Allocator>>,
queue: vk::Queue, queue: vk::Queue,
pipeline_cache: vk::PipelineCache, pipeline_cache: vk::PipelineCache,
// pub(crate) memory_properties: vk::PhysicalDeviceMemoryProperties,
} }
type ShaderPassMeta = type ShaderPassMeta =
@ -75,26 +78,28 @@ impl TryFrom<VulkanInstance> for VulkanObjects {
device.create_pipeline_cache(&vk::PipelineCacheCreateInfo::default(), None)?; device.create_pipeline_cache(&vk::PipelineCacheCreateInfo::default(), None)?;
let queue = get_graphics_queue(&instance, &device, vulkan.physical_device); 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 { Ok(VulkanObjects {
device: Arc::new(device), device: Arc::new(device),
alloc,
queue, queue,
pipeline_cache, pipeline_cache,
memory_properties, // memory_properties,
// debug, // debug,
}) })
} }
} }
} }
impl TryFrom<(vk::PhysicalDevice, ash::Instance, Arc<ash::Device>)> for VulkanObjects { impl TryFrom<(vk::PhysicalDevice, ash::Instance, ash::Device)> for VulkanObjects {
type Error = FilterChainError; type Error = FilterChainError;
fn try_from( fn try_from(value: (vk::PhysicalDevice, ash::Instance, ash::Device)) -> error::Result<Self> {
value: (vk::PhysicalDevice, ash::Instance, Arc<ash::Device>),
) -> error::Result<Self> {
unsafe { unsafe {
let device = value.2; let device = value.2;
@ -103,13 +108,16 @@ impl TryFrom<(vk::PhysicalDevice, ash::Instance, Arc<ash::Device>)> for VulkanOb
let queue = get_graphics_queue(&value.1, &device, value.0); 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 { Ok(VulkanObjects {
device, alloc,
device: Arc::new(device),
queue, queue,
pipeline_cache, pipeline_cache,
memory_properties, // memory_properties,
// debug: value.3, // debug: value.3,
}) })
} }
@ -288,7 +296,7 @@ impl FilterChainVulkan {
.map(|param| (param.name, param.value)) .map(|param| (param.name, param.value))
.collect(), .collect(),
}, },
draw_quad: DrawQuad::new(&device.device, &device.memory_properties)?, draw_quad: DrawQuad::new(&device.device, &device.alloc)?,
device: device.device.clone(), device: device.device.clone(),
output_inputs: output_textures.into_boxed_slice(), output_inputs: output_textures.into_boxed_slice(),
feedback_inputs: feedback_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( let uniform_storage = UniformStorage::new_with_ubo_storage(
RawVulkanBuffer::new( RawVulkanBuffer::new(
&vulkan.device, &vulkan.device,
&vulkan.memory_properties, &vulkan.alloc,
vk::BufferUsageFlags::UNIFORM_BUFFER, vk::BufferUsageFlags::UNIFORM_BUFFER,
ubo_size, ubo_size,
)?, )?,

View file

@ -1,9 +1,9 @@
use crate::filter_chain::FilterCommon; use crate::filter_chain::FilterCommon;
use crate::framebuffer::OutputImage; use crate::framebuffer::OutputImage;
use crate::graphics_pipeline::VulkanGraphicsPipeline; use crate::graphics_pipeline::VulkanGraphicsPipeline;
use crate::memory::RawVulkanBuffer;
use crate::samplers::SamplerSet; use crate::samplers::SamplerSet;
use crate::texture::InputImage; use crate::texture::InputImage;
use crate::vulkan_primitives::RawVulkanBuffer;
use crate::{error, VulkanImage}; use crate::{error, VulkanImage};
use ash::vk; use ash::vk;
use librashader_common::{ImageFormat, Size, Viewport}; use librashader_common::{ImageFormat, Size, Viewport};

View file

@ -22,6 +22,7 @@ use ash::vk;
use librashader_common::Viewport; use librashader_common::Viewport;
use crate::options::FrameOptionsVulkan;
use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}; use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent};
use winit::event_loop::{ControlFlow, EventLoop, EventLoopBuilder}; use winit::event_loop::{ControlFlow, EventLoop, EventLoopBuilder};
use winit::platform::windows::EventLoopBuilderExtWindows; use winit::platform::windows::EventLoopBuilderExtWindows;
@ -256,7 +257,10 @@ impl VulkanWindow {
}, },
cmd, cmd,
frame, frame,
None, Some(&FrameOptionsVulkan {
clear_history: frame == 0,
frame_direction: 0,
}),
) )
.unwrap(); .unwrap();

View file

@ -1,7 +1,6 @@
use crate::hello_triangle::surface::VulkanSurface; use crate::hello_triangle::surface::VulkanSurface;
use crate::hello_triangle::vulkan_base::VulkanBase; use crate::hello_triangle::vulkan_base::VulkanBase;
use crate::util::find_vulkan_memory_type; use crate::memory::VulkanImageMemory;
use crate::vulkan_primitives::VulkanImageMemory;
use ash::prelude::VkResult; use ash::prelude::VkResult;
use ash::vk; use ash::vk;
use ash::vk::Extent3D; use ash::vk::Extent3D;
@ -95,33 +94,9 @@ impl VulkanSwapchain {
let image = base.device.create_image(&create_info, None)?; let image = base.device.create_image(&create_info, None)?;
let mem_reqs = unsafe { base.device.get_image_memory_requirements(image) }; let mem_reqs = unsafe { base.device.get_image_memory_requirements(image) };
// base.debug let memory =
// .loader VulkanImageMemory::new(&base.device, &base.allocator, mem_reqs, &image)
// .set_debug_utils_object_name( .unwrap();
// 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();
render_images.push((image, memory)) render_images.push((image, memory))
} }

View file

@ -5,7 +5,10 @@ use crate::filter_chain::VulkanObjects;
use crate::hello_triangle::physicaldevice::{find_queue_family, pick_physical_device}; use crate::hello_triangle::physicaldevice::{find_queue_family, pick_physical_device};
use crate::util;
use ash::prelude::VkResult; use ash::prelude::VkResult;
use gpu_allocator::vulkan::{Allocator, AllocatorCreateDesc};
use parking_lot::RwLock;
use std::ffi::CStr; use std::ffi::CStr;
use std::sync::Arc; use std::sync::Arc;
@ -20,6 +23,7 @@ pub struct VulkanBase {
// pub debug: VulkanDebug, // pub debug: VulkanDebug,
pub physical_device: vk::PhysicalDevice, pub physical_device: vk::PhysicalDevice,
pub mem_props: vk::PhysicalDeviceMemoryProperties, pub mem_props: vk::PhysicalDeviceMemoryProperties,
pub allocator: Arc<RwLock<Allocator>>,
} }
impl VulkanBase { impl VulkanBase {
@ -61,6 +65,10 @@ impl VulkanBase {
let mem_props = unsafe { instance.get_physical_device_memory_properties(physical_device) }; let mem_props = unsafe { instance.get_physical_device_memory_properties(physical_device) };
dbg!("got memprops"); dbg!("got memprops");
let alloc =
util::create_allocator(device.clone(), instance.clone(), physical_device.clone())
.expect("could not create allocator");
Ok(VulkanBase { Ok(VulkanBase {
entry, entry,
instance, instance,
@ -69,6 +77,7 @@ impl VulkanBase {
physical_device, physical_device,
mem_props, mem_props,
// debug, // debug,
allocator: alloc,
}) })
} }
@ -161,10 +170,8 @@ impl TryFrom<&VulkanBase> for VulkanObjects {
type Error = FilterChainError; type Error = FilterChainError;
fn try_from(value: &VulkanBase) -> Result<Self, Self::Error> { fn try_from(value: &VulkanBase) -> Result<Self, Self::Error> {
VulkanObjects::try_from(( let device = (*value.device).clone();
value.physical_device,
value.instance.clone(), VulkanObjects::try_from((value.physical_device, value.instance.clone(), device))
value.device.clone(),
))
} }
} }

View file

@ -15,12 +15,12 @@ mod graphics_pipeline;
#[cfg(test)] #[cfg(test)]
mod hello_triangle; mod hello_triangle;
mod luts; mod luts;
mod memory;
mod parameters; mod parameters;
mod queue_selection; mod queue_selection;
mod samplers; mod samplers;
mod texture; mod texture;
mod util; mod util;
mod vulkan_primitives;
pub use filter_chain::FilterChainVulkan; pub use filter_chain::FilterChainVulkan;
pub use filter_chain::VulkanInstance; pub use filter_chain::VulkanInstance;

View file

@ -1,6 +1,6 @@
use crate::filter_chain::VulkanObjects; use crate::filter_chain::VulkanObjects;
use crate::memory::{VulkanBuffer, VulkanImageMemory};
use crate::texture::{InputImage, VulkanImage}; use crate::texture::{InputImage, VulkanImage};
use crate::vulkan_primitives::{VulkanBuffer, VulkanImageMemory};
use crate::{error, util}; use crate::{error, util};
use ash::vk; use ash::vk;
use librashader_presets::TextureConfig; use librashader_presets::TextureConfig;
@ -46,21 +46,14 @@ impl LutTexture {
let memory = unsafe { let memory = unsafe {
let mem_reqs = vulkan.device.get_image_memory_requirements(texture); let mem_reqs = vulkan.device.get_image_memory_requirements(texture);
let mem_type = util::find_vulkan_memory_type( // let mem_type = util::find_vulkan_memory_type(
&vulkan.memory_properties, // &vulkan.memory_properties,
mem_reqs.memory_type_bits, // mem_reqs.memory_type_bits,
vk::MemoryPropertyFlags::DEVICE_LOCAL, // vk::MemoryPropertyFlags::DEVICE_LOCAL,
)?; // )?;
VulkanImageMemory::new( VulkanImageMemory::new(&vulkan.device, &vulkan.alloc, mem_reqs, &texture)?
&vulkan.device,
&vk::MemoryAllocateInfo::builder()
.memory_type_index(mem_type)
.allocation_size(mem_reqs.size),
)?
}; };
memory.bind(&texture)?;
let image_subresource = vk::ImageSubresourceRange::builder() let image_subresource = vk::ImageSubresourceRange::builder()
.level_count(image_info.mip_levels) .level_count(image_info.mip_levels)
.layer_count(1) .layer_count(1)
@ -86,14 +79,12 @@ impl LutTexture {
let mut staging = VulkanBuffer::new( let mut staging = VulkanBuffer::new(
&vulkan.device, &vulkan.device,
&vulkan.memory_properties, &vulkan.alloc,
vk::BufferUsageFlags::TRANSFER_SRC, vk::BufferUsageFlags::TRANSFER_SRC,
image.bytes.len(), image.bytes.len(),
)?; )?;
unsafe {
let mut handle = staging.map()?; staging.as_mut_slice()?.copy_from_slice(&image.bytes);
handle.copy_from(0, &image.bytes)
}
unsafe { unsafe {
util::vulkan_image_layout_transition_levels( util::vulkan_image_layout_transition_levels(

View file

@ -1,6 +1,11 @@
use crate::{error, util}; use crate::error;
use crate::error::FilterChainError;
use ash::vk; use ash::vk;
use gpu_allocator::vulkan::{Allocation, AllocationCreateDesc, AllocationScheme, Allocator};
use gpu_allocator::MemoryLocation;
use librashader_runtime::uniforms::UniformStorageAccess; use librashader_runtime::uniforms::UniformStorageAccess;
use parking_lot::RwLock;
use std::ffi::c_void; use std::ffi::c_void;
use std::mem::ManuallyDrop; use std::mem::ManuallyDrop;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
@ -8,32 +13,46 @@ use std::ptr::NonNull;
use std::sync::Arc; use std::sync::Arc;
pub struct VulkanImageMemory { pub struct VulkanImageMemory {
pub handle: vk::DeviceMemory, allocation: Option<Allocation>,
allocator: Arc<RwLock<Allocator>>,
device: Arc<ash::Device>, device: Arc<ash::Device>,
} }
impl VulkanImageMemory { impl VulkanImageMemory {
pub fn new( pub fn new(
device: &Arc<ash::Device>, device: &Arc<ash::Device>,
alloc: &vk::MemoryAllocateInfo, allocator: &Arc<RwLock<Allocator>>,
requirements: vk::MemoryRequirements,
image: &vk::Image,
) -> error::Result<VulkanImageMemory> { ) -> error::Result<VulkanImageMemory> {
let allocation = allocator.write().allocate(&AllocationCreateDesc {
name: "imagemem",
requirements,
location: MemoryLocation::GpuOnly,
linear: false,
allocation_scheme: AllocationScheme::DedicatedImage(*image),
})?;
unsafe { unsafe {
device.bind_image_memory(*image, allocation.memory(), 0)?;
Ok(VulkanImageMemory { Ok(VulkanImageMemory {
handle: device.allocate_memory(alloc, None)?, allocation: Some(allocation),
device: device.clone(), 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 { impl Drop for VulkanImageMemory {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { 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 struct VulkanBuffer {
pub handle: vk::Buffer, pub handle: vk::Buffer,
device: Arc<ash::Device>, device: Arc<ash::Device>,
memory: vk::DeviceMemory, memory: Option<Allocation>,
allocator: Arc<RwLock<Allocator>>,
size: vk::DeviceSize, size: vk::DeviceSize,
} }
pub struct VulkanBufferMapHandle<'a> {
buffer: &'a mut VulkanBuffer,
ptr: *mut c_void,
}
impl VulkanBuffer { impl VulkanBuffer {
pub fn new( pub fn new(
device: &Arc<ash::Device>, device: &Arc<ash::Device>,
mem_props: &vk::PhysicalDeviceMemoryProperties, allocator: &Arc<RwLock<Allocator>>,
usage: vk::BufferUsageFlags, usage: vk::BufferUsageFlags,
size: usize, size: usize,
) -> error::Result<VulkanBuffer> { ) -> error::Result<VulkanBuffer> {
@ -66,45 +81,48 @@ impl VulkanBuffer {
let buffer = device.create_buffer(&buffer_info, None)?; let buffer = device.create_buffer(&buffer_info, None)?;
let memory_reqs = device.get_buffer_memory_requirements(buffer); 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)?; let alloc = allocator.write().allocate(&AllocationCreateDesc {
device.bind_buffer_memory(buffer, alloc, 0)?; 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 { Ok(VulkanBuffer {
handle: buffer, handle: buffer,
memory: alloc, memory: Some(alloc),
allocator: Arc::clone(allocator),
size: size as vk::DeviceSize, size: size as vk::DeviceSize,
device: device.clone(), device: device.clone(),
}) })
} }
} }
pub fn map(&mut self) -> error::Result<VulkanBufferMapHandle> { pub fn as_mut_slice(&mut self) -> error::Result<&mut [u8]> {
let dst = unsafe { let Some(allocation) = self.memory.as_mut() else {
self.device return Err(FilterChainError::AllocationDoesNotExist)
.map_memory(self.memory, 0, self.size, vk::MemoryMapFlags::empty())?
}; };
let Some(allocation) = allocation.mapped_slice_mut() else {
Ok(VulkanBufferMapHandle { return Err(FilterChainError::AllocationDoesNotExist)
buffer: self, };
ptr: dst, Ok(allocation)
})
} }
} }
impl Drop for VulkanBuffer { impl Drop for VulkanBuffer {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
if self.memory != vk::DeviceMemory::null() { if let Some(allocation) = self.memory.take() {
self.device.free_memory(self.memory, None); 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() { 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::<u8>() 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. /// SAFETY: Creating the pointer should be safe in multithreaded contexts.
/// ///
/// Mutation is guarded by DerefMut<Target=[u8]> /// Mutation is guarded by DerefMut<Target=[u8]>
@ -146,18 +144,17 @@ pub struct RawVulkanBuffer {
impl RawVulkanBuffer { impl RawVulkanBuffer {
pub fn new( pub fn new(
device: &Arc<ash::Device>, device: &Arc<ash::Device>,
mem_props: &vk::PhysicalDeviceMemoryProperties, allocator: &Arc<RwLock<Allocator>>,
usage: vk::BufferUsageFlags, usage: vk::BufferUsageFlags,
size: usize, size: usize,
) -> error::Result<Self> { ) -> error::Result<Self> {
let buffer = ManuallyDrop::new(VulkanBuffer::new(device, mem_props, usage, size)?); let buffer = ManuallyDrop::new(VulkanBuffer::new(device, allocator, usage, size)?);
let ptr = unsafe {
NonNull::new_unchecked(device.map_memory( let Some(ptr) = buffer.memory
buffer.memory, .as_ref()
0, .map(|m| m.mapped_ptr())
buffer.size, .flatten() else {
vk::MemoryMapFlags::empty(), return Err(FilterChainError::AllocationDoesNotExist)
)?)
}; };
Ok(RawVulkanBuffer { buffer, ptr }) Ok(RawVulkanBuffer { buffer, ptr })
@ -193,12 +190,7 @@ impl RawVulkanBuffer {
impl Drop for RawVulkanBuffer { impl Drop for RawVulkanBuffer {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
self.buffer.device.unmap_memory(self.buffer.memory); ManuallyDrop::drop(&mut self.buffer);
self.ptr = NonNull::dangling();
if self.buffer.memory != vk::DeviceMemory::null() {
self.buffer.device.free_memory(self.buffer.memory, None);
}
if self.buffer.handle != vk::Buffer::null() { if self.buffer.handle != vk::Buffer::null() {
self.buffer.device.destroy_buffer(self.buffer.handle, None); self.buffer.device.destroy_buffer(self.buffer.handle, None);
} }

View file

@ -1,8 +1,9 @@
use crate::filter_chain::VulkanObjects; use crate::filter_chain::VulkanObjects;
use crate::util::find_vulkan_memory_type; use crate::memory::VulkanImageMemory;
use crate::vulkan_primitives::VulkanImageMemory;
use crate::{error, util}; use crate::{error, util};
use ash::vk; use ash::vk;
use gpu_allocator::vulkan::Allocator;
use parking_lot::RwLock;
use std::sync::Arc; use std::sync::Arc;
use crate::error::FilterChainError; use crate::error::FilterChainError;
@ -12,7 +13,7 @@ use librashader_runtime::scaling::{MipmapSize, ScaleFramebuffer, ViewportSize};
pub struct OwnedImage { pub struct OwnedImage {
pub device: Arc<ash::Device>, pub device: Arc<ash::Device>,
pub mem_props: vk::PhysicalDeviceMemoryProperties, pub allocator: Arc<RwLock<Allocator>>,
pub image_view: vk::ImageView, pub image_view: vk::ImageView,
pub image: VulkanImage, pub image: VulkanImage,
pub memory: VulkanImageMemory, pub memory: VulkanImageMemory,
@ -32,7 +33,7 @@ pub struct OwnedImageLayout {
impl OwnedImage { impl OwnedImage {
fn new_internal( fn new_internal(
device: Arc<ash::Device>, device: Arc<ash::Device>,
mem_props: vk::PhysicalDeviceMemoryProperties, alloc: &Arc<RwLock<Allocator>>,
size: Size<u32>, size: Size<u32>,
mut format: ImageFormat, mut format: ImageFormat,
max_miplevels: u32, max_miplevels: u32,
@ -63,19 +64,7 @@ impl OwnedImage {
let image = unsafe { device.create_image(&image_create_info, None)? }; let image = unsafe { device.create_image(&image_create_info, None)? };
let mem_reqs = unsafe { device.get_image_memory_requirements(image) }; let mem_reqs = unsafe { device.get_image_memory_requirements(image) };
let alloc_info = vk::MemoryAllocateInfo::builder() let memory = VulkanImageMemory::new(&device, alloc, mem_reqs, &image)?;
.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 image_subresource = vk::ImageSubresourceRange::builder() let image_subresource = vk::ImageSubresourceRange::builder()
.base_mip_level(0) .base_mip_level(0)
.base_array_layer(0) .base_array_layer(0)
@ -103,7 +92,7 @@ impl OwnedImage {
Ok(OwnedImage { Ok(OwnedImage {
device, device,
mem_props, allocator: Arc::clone(alloc),
image_view, image_view,
image: VulkanImage { image: VulkanImage {
image, image,
@ -124,7 +113,7 @@ impl OwnedImage {
) -> error::Result<OwnedImage> { ) -> error::Result<OwnedImage> {
Self::new_internal( Self::new_internal(
vulkan.device.clone(), vulkan.device.clone(),
vulkan.memory_properties, &vulkan.alloc,
size, size,
format, format,
max_miplevels, max_miplevels,
@ -150,7 +139,7 @@ impl OwnedImage {
let new = OwnedImage::new_internal( let new = OwnedImage::new_internal(
self.device.clone(), self.device.clone(),
self.mem_props, &self.allocator,
size, size,
if format == ImageFormat::Unknown { if format == ImageFormat::Unknown {
ImageFormat::R8G8B8A8Unorm ImageFormat::R8G8B8A8Unorm

View file

@ -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<VulkanBuffer>,
device: Arc<ash::Device>,
}
impl VkUboRing {
pub fn new(
device: &Arc<ash::Device>,
mem_props: &vk::PhysicalDeviceMemoryProperties,
ring_size: usize,
buffer_size: usize,
) -> error::Result<Self> {
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(())
}
}

View file

@ -1,4 +1,8 @@
use ash::vk; 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;
use crate::error::FilterChainError; use crate::error::FilterChainError;
@ -17,6 +21,7 @@ pub fn binding_stage_to_vulkan_stage(stage_mask: BindingStage) -> vk::ShaderStag
mask mask
} }
#[allow(unused)]
pub fn find_vulkan_memory_type( pub fn find_vulkan_memory_type(
props: &vk::PhysicalDeviceMemoryProperties, props: &vk::PhysicalDeviceMemoryProperties,
device_reqs: u32, device_reqs: u32,
@ -81,3 +86,18 @@ pub unsafe fn vulkan_image_layout_transition_levels(
&[barrier], &[barrier],
) )
} }
pub fn create_allocator(
device: ash::Device,
instance: ash::Instance,
physical_device: vk::PhysicalDevice,
) -> error::Result<Arc<RwLock<Allocator>>> {
let alloc = Allocator::new(&AllocatorCreateDesc {
instance,
device,
physical_device,
debug_settings: Default::default(),
buffer_device_address: false,
})?;
Ok(Arc::new(RwLock::new(alloc)))
}