aboutsummaryrefslogtreecommitdiff
path: root/src/memory/pool/pool.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/memory/pool/pool.rs')
-rw-r--r--src/memory/pool/pool.rs273
1 files changed, 273 insertions, 0 deletions
diff --git a/src/memory/pool/pool.rs b/src/memory/pool/pool.rs
new file mode 100644
index 0000000..0681716
--- /dev/null
+++ b/src/memory/pool/pool.rs
@@ -0,0 +1,273 @@
+// Copyright (c) 2016 The vulkano developers
+// Licensed under the Apache License, Version 2.0
+// <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
+// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
+// at your option. All files in the project carrying such
+// notice may not be copied, modified, or distributed except
+// according to those terms.
+
+use crate::device::physical::MemoryType;
+use crate::device::Device;
+use crate::device::DeviceOwned;
+use crate::memory::pool::AllocLayout;
+use crate::memory::pool::MappingRequirement;
+use crate::memory::pool::MemoryPool;
+use crate::memory::pool::MemoryPoolAlloc;
+use crate::memory::pool::StdHostVisibleMemoryTypePool;
+use crate::memory::pool::StdHostVisibleMemoryTypePoolAlloc;
+use crate::memory::pool::StdNonHostVisibleMemoryTypePool;
+use crate::memory::pool::StdNonHostVisibleMemoryTypePoolAlloc;
+use crate::memory::DeviceMemory;
+use crate::memory::DeviceMemoryAllocError;
+use crate::memory::MappedDeviceMemory;
+use crate::DeviceSize;
+use fnv::FnvHasher;
+use std::collections::hash_map::Entry;
+use std::collections::HashMap;
+use std::hash::BuildHasherDefault;
+use std::sync::Arc;
+use std::sync::Mutex;
+
+#[derive(Debug)]
+pub struct StdMemoryPool {
+ device: Arc<Device>,
+
+ // For each memory type index, stores the associated pool.
+ pools:
+ Mutex<HashMap<(u32, AllocLayout, MappingRequirement), Pool, BuildHasherDefault<FnvHasher>>>,
+}
+
+impl StdMemoryPool {
+ /// Creates a new pool.
+ #[inline]
+ pub fn new(device: Arc<Device>) -> Arc<StdMemoryPool> {
+ let cap = device.physical_device().memory_types().len();
+ let hasher = BuildHasherDefault::<FnvHasher>::default();
+
+ Arc::new(StdMemoryPool {
+ device: device.clone(),
+ pools: Mutex::new(HashMap::with_capacity_and_hasher(cap, hasher)),
+ })
+ }
+}
+
+fn generic_allocation(
+ mem_pool: Arc<StdMemoryPool>,
+ memory_type: MemoryType,
+ size: DeviceSize,
+ alignment: DeviceSize,
+ layout: AllocLayout,
+ map: MappingRequirement,
+) -> Result<StdMemoryPoolAlloc, DeviceMemoryAllocError> {
+ let mut pools = mem_pool.pools.lock().unwrap();
+
+ let memory_type_host_visible = memory_type.is_host_visible();
+ assert!(memory_type_host_visible || map == MappingRequirement::DoNotMap);
+
+ match pools.entry((memory_type.id(), layout, map)) {
+ Entry::Occupied(entry) => match entry.get() {
+ &Pool::HostVisible(ref pool) => {
+ let alloc = StdHostVisibleMemoryTypePool::alloc(&pool, size, alignment)?;
+ let inner = StdMemoryPoolAllocInner::HostVisible(alloc);
+ Ok(StdMemoryPoolAlloc {
+ inner,
+ pool: mem_pool.clone(),
+ })
+ }
+ &Pool::NonHostVisible(ref pool) => {
+ let alloc = StdNonHostVisibleMemoryTypePool::alloc(&pool, size, alignment)?;
+ let inner = StdMemoryPoolAllocInner::NonHostVisible(alloc);
+ Ok(StdMemoryPoolAlloc {
+ inner,
+ pool: mem_pool.clone(),
+ })
+ }
+ },
+
+ Entry::Vacant(entry) => {
+ if memory_type_host_visible {
+ let pool = StdHostVisibleMemoryTypePool::new(mem_pool.device.clone(), memory_type);
+ entry.insert(Pool::HostVisible(pool.clone()));
+ let alloc = StdHostVisibleMemoryTypePool::alloc(&pool, size, alignment)?;
+ let inner = StdMemoryPoolAllocInner::HostVisible(alloc);
+ Ok(StdMemoryPoolAlloc {
+ inner,
+ pool: mem_pool.clone(),
+ })
+ } else {
+ let pool =
+ StdNonHostVisibleMemoryTypePool::new(mem_pool.device.clone(), memory_type);
+ entry.insert(Pool::NonHostVisible(pool.clone()));
+ let alloc = StdNonHostVisibleMemoryTypePool::alloc(&pool, size, alignment)?;
+ let inner = StdMemoryPoolAllocInner::NonHostVisible(alloc);
+ Ok(StdMemoryPoolAlloc {
+ inner,
+ pool: mem_pool.clone(),
+ })
+ }
+ }
+ }
+}
+
+/// Same as `generic_allocation` but with exportable memory fd on Linux.
+#[cfg(target_os = "linux")]
+fn generit_allocation_with_exportable_fd(
+ mem_pool: Arc<StdMemoryPool>,
+ memory_type: MemoryType,
+ size: DeviceSize,
+ alignment: DeviceSize,
+ layout: AllocLayout,
+ map: MappingRequirement,
+) -> Result<StdMemoryPoolAlloc, DeviceMemoryAllocError> {
+ let mut pools = mem_pool.pools.lock().unwrap();
+
+ let memory_type_host_visible = memory_type.is_host_visible();
+ assert!(memory_type_host_visible || map == MappingRequirement::DoNotMap);
+
+ match pools.entry((memory_type.id(), layout, map)) {
+ Entry::Occupied(entry) => match entry.get() {
+ &Pool::HostVisible(ref pool) => {
+ let alloc =
+ StdHostVisibleMemoryTypePool::alloc_with_exportable_fd(&pool, size, alignment)?;
+ let inner = StdMemoryPoolAllocInner::HostVisible(alloc);
+ Ok(StdMemoryPoolAlloc {
+ inner,
+ pool: mem_pool.clone(),
+ })
+ }
+ &Pool::NonHostVisible(ref pool) => {
+ let alloc = StdNonHostVisibleMemoryTypePool::alloc_with_exportable_fd(
+ &pool, size, alignment,
+ )?;
+ let inner = StdMemoryPoolAllocInner::NonHostVisible(alloc);
+ Ok(StdMemoryPoolAlloc {
+ inner,
+ pool: mem_pool.clone(),
+ })
+ }
+ },
+
+ Entry::Vacant(entry) => {
+ if memory_type_host_visible {
+ let pool = StdHostVisibleMemoryTypePool::new(mem_pool.device.clone(), memory_type);
+ entry.insert(Pool::HostVisible(pool.clone()));
+ let alloc =
+ StdHostVisibleMemoryTypePool::alloc_with_exportable_fd(&pool, size, alignment)?;
+ let inner = StdMemoryPoolAllocInner::HostVisible(alloc);
+ Ok(StdMemoryPoolAlloc {
+ inner,
+ pool: mem_pool.clone(),
+ })
+ } else {
+ let pool =
+ StdNonHostVisibleMemoryTypePool::new(mem_pool.device.clone(), memory_type);
+ entry.insert(Pool::NonHostVisible(pool.clone()));
+ let alloc = StdNonHostVisibleMemoryTypePool::alloc_with_exportable_fd(
+ &pool, size, alignment,
+ )?;
+ let inner = StdMemoryPoolAllocInner::NonHostVisible(alloc);
+ Ok(StdMemoryPoolAlloc {
+ inner,
+ pool: mem_pool.clone(),
+ })
+ }
+ }
+ }
+}
+
+unsafe impl MemoryPool for Arc<StdMemoryPool> {
+ type Alloc = StdMemoryPoolAlloc;
+
+ fn alloc_generic(
+ &self,
+ memory_type: MemoryType,
+ size: DeviceSize,
+ alignment: DeviceSize,
+ layout: AllocLayout,
+ map: MappingRequirement,
+ ) -> Result<StdMemoryPoolAlloc, DeviceMemoryAllocError> {
+ generic_allocation(self.clone(), memory_type, size, alignment, layout, map)
+ }
+
+ /// Same as `alloc_generic` but with exportable fd option on Linux.
+ #[cfg(target_os = "linux")]
+ fn alloc_generic_with_exportable_fd(
+ &self,
+ memory_type: MemoryType,
+ size: DeviceSize,
+ alignment: DeviceSize,
+ layout: AllocLayout,
+ map: MappingRequirement,
+ ) -> Result<StdMemoryPoolAlloc, DeviceMemoryAllocError> {
+ generit_allocation_with_exportable_fd(
+ self.clone(),
+ memory_type,
+ size,
+ alignment,
+ layout,
+ map,
+ )
+ }
+}
+
+unsafe impl DeviceOwned for StdMemoryPool {
+ #[inline]
+ fn device(&self) -> &Arc<Device> {
+ &self.device
+ }
+}
+
+#[derive(Debug)]
+enum Pool {
+ HostVisible(Arc<StdHostVisibleMemoryTypePool>),
+ NonHostVisible(Arc<StdNonHostVisibleMemoryTypePool>),
+}
+
+#[derive(Debug)]
+pub struct StdMemoryPoolAlloc {
+ inner: StdMemoryPoolAllocInner,
+ pool: Arc<StdMemoryPool>,
+}
+
+impl StdMemoryPoolAlloc {
+ #[inline]
+ pub fn size(&self) -> DeviceSize {
+ match self.inner {
+ StdMemoryPoolAllocInner::NonHostVisible(ref mem) => mem.size(),
+ StdMemoryPoolAllocInner::HostVisible(ref mem) => mem.size(),
+ }
+ }
+}
+
+unsafe impl MemoryPoolAlloc for StdMemoryPoolAlloc {
+ #[inline]
+ fn memory(&self) -> &DeviceMemory {
+ match self.inner {
+ StdMemoryPoolAllocInner::NonHostVisible(ref mem) => mem.memory(),
+ StdMemoryPoolAllocInner::HostVisible(ref mem) => mem.memory().as_ref(),
+ }
+ }
+
+ #[inline]
+ fn mapped_memory(&self) -> Option<&MappedDeviceMemory> {
+ match self.inner {
+ StdMemoryPoolAllocInner::NonHostVisible(_) => None,
+ StdMemoryPoolAllocInner::HostVisible(ref mem) => Some(mem.memory()),
+ }
+ }
+
+ #[inline]
+ fn offset(&self) -> DeviceSize {
+ match self.inner {
+ StdMemoryPoolAllocInner::NonHostVisible(ref mem) => mem.offset(),
+ StdMemoryPoolAllocInner::HostVisible(ref mem) => mem.offset(),
+ }
+ }
+}
+
+#[derive(Debug)]
+enum StdMemoryPoolAllocInner {
+ NonHostVisible(StdNonHostVisibleMemoryTypePoolAlloc),
+ HostVisible(StdHostVisibleMemoryTypePoolAlloc),
+}