gpu_alloc_gfx/
lib.rs

1//!
2//! # Gfx backend for `gpu-alloc`
3//!  
4//! # Usage example
5//!
6//! ```ignore
7//!
8//! use {
9//!     eyre::eyre,
10//!     gfx_backend_vulkan::Instance,
11//!     gfx_hal::{
12//!         adapter::{Gpu, PhysicalDevice as _},
13//!         queue::QueueFamily as _,
14//!         Features, Instance as _,
15//!     },
16//!     gpu_alloc::{Config, GpuAllocator, Request, UsageFlags},
17//!     gpu_alloc_gfx::{gfx_device_properties, GfxMemoryDevice},
18//! };
19//!
20//! fn main() -> eyre::Result<()> {
21//!     color_eyre::install()?;
22//!
23//!     let instance =
24//!         Instance::create("gpu_alloc-example", 1).map_err(|_| eyre!("Unsupported backend"))?;
25//!
26//!     let adapters = instance.enumerate_adapters();
27//!
28//!     let adapter = adapters
29//!         .iter()
30//!         .min_by_key(|a| {
31//!             use gfx_hal::adapter::DeviceType::*;
32//!             match a.info.device_type {
33//!                 Other => 4,
34//!                 IntegratedGpu => 1,
35//!                 DiscreteGpu => 0,
36//!                 VirtualGpu => 2,
37//!                 Cpu => 3,
38//!             }
39//!         })
40//!         .ok_or_else(|| eyre!("No adapters found"))?;
41//!
42//!     let queue_family = adapter
43//!         .queue_families
44//!         .iter()
45//!         .min_by_key(|qf| {
46//!             use gfx_hal::queue::QueueType::*;
47//!             match qf.queue_type() {
48//!                 General => 0,
49//!                 Graphics => 1,
50//!                 Compute => 3,
51//!                 Transfer => 4,
52//!             }
53//!         })
54//!         .ok_or_else(|| eyre!("No queue families found"))?;
55//!
56//!     let props = gfx_device_properties(adapter);
57//!
58//!     let Gpu { device, .. } = unsafe {
59//!         adapter
60//!             .physical_device
61//!             .open(&[(queue_family, &[1.0])], Features::empty())
62//!     }?;
63//!
64//!     let config = Config::i_am_potato();
65//!
66//!     let mut allocator = GpuAllocator::new(config, props);
67//!
68//!     let mut block = unsafe {
69//!         allocator.alloc(
70//!             GfxMemoryDevice::wrap(&device),
71//!             Request {
72//!                 size: 10,
73//!                 align_mask: 1,
74//!                 usage: UsageFlags::HOST_ACCESS,
75//!                 memory_types: !0,
76//!             },
77//!         )
78//!     }?;
79//!
80//!     unsafe {
81//!         block.write_bytes(
82//!             GfxMemoryDevice::wrap(&device),
83//!             0,
84//!             &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
85//!         )
86//!     }?;
87//!
88//!     unsafe { allocator.dealloc(GfxMemoryDevice::wrap(&device), block) }
89//!
90//!     Ok(())
91//! }
92//!
93
94use {
95    gfx_hal::{
96        adapter::{Adapter, PhysicalDevice as _},
97        device::{AllocationError, Device, MapError, OutOfMemory as GfxOutOfMemory},
98        memory::{Properties, Segment},
99        Backend, MemoryTypeId,
100    },
101    gpu_alloc_types::{
102        AllocationFlags, DeviceMapError, DeviceProperties, MappedMemoryRange, MemoryDevice,
103        MemoryHeap, MemoryPropertyFlags, MemoryType, OutOfMemory,
104    },
105    std::{convert::TryFrom as _, ptr::NonNull},
106};
107
108#[repr(transparent)]
109pub struct GfxMemoryDevice<B: Backend> {
110    device: B::Device,
111}
112
113impl<B> GfxMemoryDevice<B>
114where
115    B: Backend,
116{
117    pub fn wrap<D>(device: &D) -> &Self
118    where
119        D: Device<B>,
120        B: Backend<Device = D>,
121    {
122        unsafe {
123            // Safe because `Self` is `repr(transparent)`
124            // with only non-zero-sized field being `D`.
125            &*(device as *const D as *const Self)
126        }
127    }
128}
129
130impl<B> MemoryDevice<B::Memory> for GfxMemoryDevice<B>
131where
132    B: Backend,
133{
134    #[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
135    unsafe fn allocate_memory(
136        &self,
137        size: u64,
138        memory_type: u32,
139        flags: AllocationFlags,
140    ) -> Result<B::Memory, OutOfMemory> {
141        debug_assert!(flags.is_empty(), "No allocation flags supported");
142
143        let memory_type =
144            MemoryTypeId(usize::try_from(memory_type).expect("memory_type out of bound"));
145
146        match self.device.allocate_memory(memory_type, size) {
147            Ok(memory) => Ok(memory),
148            Err(AllocationError::OutOfMemory(GfxOutOfMemory::Device)) => {
149                Err(OutOfMemory::OutOfDeviceMemory)
150            }
151            Err(AllocationError::OutOfMemory(GfxOutOfMemory::Host)) => {
152                Err(OutOfMemory::OutOfHostMemory)
153            }
154            Err(AllocationError::TooManyObjects) => panic!("Too many objects"),
155        }
156    }
157
158    #[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
159    unsafe fn deallocate_memory(&self, memory: B::Memory) {
160        self.device.free_memory(memory);
161    }
162
163    #[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
164    unsafe fn map_memory(
165        &self,
166        memory: &mut B::Memory,
167        offset: u64,
168        size: u64,
169    ) -> Result<NonNull<u8>, DeviceMapError> {
170        let result = self.device.map_memory(
171            memory,
172            Segment {
173                offset,
174                size: Some(size),
175            },
176        );
177
178        match result {
179            Ok(ptr) => Ok(NonNull::new(ptr).expect("Pointer to memory mapping must not be null")),
180            Err(MapError::OutOfMemory(GfxOutOfMemory::Device)) => {
181                Err(DeviceMapError::OutOfDeviceMemory)
182            }
183            Err(MapError::OutOfMemory(GfxOutOfMemory::Host)) => {
184                Err(DeviceMapError::OutOfHostMemory)
185            }
186            Err(MapError::OutOfBounds) => panic!("Memory mapping out of bounds"),
187            Err(MapError::MappingFailed) => Err(DeviceMapError::MapFailed),
188            Err(MapError::Access) => panic!("Attempt to map non-host-visible memory"),
189        }
190    }
191
192    #[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
193    unsafe fn unmap_memory(&self, memory: &mut B::Memory) {
194        self.device.unmap_memory(memory);
195    }
196
197    #[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
198    unsafe fn invalidate_memory_ranges(
199        &self,
200        ranges: &[MappedMemoryRange<'_, B::Memory>],
201    ) -> Result<(), OutOfMemory> {
202        self.device
203            .invalidate_mapped_memory_ranges(ranges.iter().map(|range| {
204                (
205                    &*range.memory,
206                    Segment {
207                        offset: range.offset,
208                        size: Some(range.size),
209                    },
210                )
211            }))
212            .map_err(|err| match err {
213                GfxOutOfMemory::Device => OutOfMemory::OutOfDeviceMemory,
214                GfxOutOfMemory::Host => OutOfMemory::OutOfHostMemory,
215            })
216    }
217
218    #[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
219    unsafe fn flush_memory_ranges(
220        &self,
221        ranges: &[MappedMemoryRange<'_, B::Memory>],
222    ) -> Result<(), OutOfMemory> {
223        self.device
224            .flush_mapped_memory_ranges(ranges.iter().map(|range| {
225                (
226                    &*range.memory,
227                    Segment {
228                        offset: range.offset,
229                        size: Some(range.size),
230                    },
231                )
232            }))
233            .map_err(|err| match err {
234                GfxOutOfMemory::Device => OutOfMemory::OutOfDeviceMemory,
235                GfxOutOfMemory::Host => OutOfMemory::OutOfHostMemory,
236            })
237    }
238}
239
240/// Returns `DeviceProperties` from gfx's `Adapter`, required to create `GpuAllocator`.
241pub fn gfx_device_properties<B>(adapter: &Adapter<B>) -> DeviceProperties<'static>
242where
243    B: Backend,
244{
245    let limits = adapter.physical_device.limits();
246    let memory_properties = adapter.physical_device.memory_properties();
247    DeviceProperties {
248        max_memory_allocation_count: u32::try_from(limits.max_memory_allocation_count)
249            .unwrap_or(u32::max_value()),
250        max_memory_allocation_size: u64::max_value(),
251        non_coherent_atom_size: u64::try_from(limits.non_coherent_atom_size)
252            .unwrap_or(u64::max_value()),
253        memory_types: memory_properties
254            .memory_types
255            .iter()
256            .map(|memory_type| MemoryType {
257                props: memory_properties_from_gfx(memory_type.properties),
258                heap: u32::try_from(memory_type.heap_index)
259                    .expect("Memory heap index should fit `u32`"),
260            })
261            .collect(),
262        memory_heaps: memory_properties
263            .memory_heaps
264            .iter()
265            .map(|&memory_heap| MemoryHeap {
266                size: memory_heap.size,
267            })
268            .collect(),
269        buffer_device_address: false,
270    }
271}
272
273pub fn memory_properties_from_gfx(props: Properties) -> MemoryPropertyFlags {
274    let mut result = MemoryPropertyFlags::empty();
275    if props.contains(Properties::DEVICE_LOCAL) {
276        result |= MemoryPropertyFlags::DEVICE_LOCAL;
277    }
278    if props.contains(Properties::CPU_VISIBLE) {
279        result |= MemoryPropertyFlags::HOST_VISIBLE;
280    }
281    if props.contains(Properties::COHERENT) {
282        result |= MemoryPropertyFlags::HOST_COHERENT;
283    }
284    if props.contains(Properties::CPU_CACHED) {
285        result |= MemoryPropertyFlags::HOST_CACHED;
286    }
287    if props.contains(Properties::LAZILY_ALLOCATED) {
288        result |= MemoryPropertyFlags::LAZILY_ALLOCATED;
289    }
290    result
291}
292
293pub fn memory_properties_to_gfx(props: MemoryPropertyFlags) -> Properties {
294    let mut result = Properties::empty();
295    if props.contains(MemoryPropertyFlags::DEVICE_LOCAL) {
296        result |= Properties::DEVICE_LOCAL;
297    }
298    if props.contains(MemoryPropertyFlags::HOST_VISIBLE) {
299        result |= Properties::CPU_VISIBLE;
300    }
301    if props.contains(MemoryPropertyFlags::HOST_COHERENT) {
302        result |= Properties::COHERENT;
303    }
304    if props.contains(MemoryPropertyFlags::HOST_CACHED) {
305        result |= Properties::CPU_CACHED;
306    }
307    if props.contains(MemoryPropertyFlags::LAZILY_ALLOCATED) {
308        result |= Properties::LAZILY_ALLOCATED;
309    }
310    result
311}