screen_13/pool/
mod.rs

1//! Resource leasing and pooling types.
2//!
3//! _Screen 13_ provides caching for acceleration structure, buffer and image resources which may be
4//! leased from configurable pools using their corresponding information structure. Most programs
5//! will do fine with a single [`FifoPool`](self::fifo::FifoPool).
6//!
7//! Leased resources may be bound directly to a render graph and used in the same manner as regular
8//! resources. After rendering has finished, the leased resources will return to the pool for reuse.
9//!
10//! # Buckets
11//!
12//! The provided [`Pool`] implementations store resources in buckets, with each implementation
13//! offering a different strategy which balances performance (_more buckets_) with memory efficiency
14//! (_fewer buckets_).
15//!
16//! _Screen 13_'s pools can be grouped into two major categories:
17//!
18//! * Single-bucket: [`FifoPool`](self::fifo::FifoPool)
19//! * Multi-bucket: [`LazyPool`](self::lazy::LazyPool), [`HashPool`](self::hash::HashPool)
20//!
21//! # Examples
22//!
23//! Leasing an image:
24//!
25//! ```no_run
26//! # use std::sync::Arc;
27//! # use ash::vk;
28//! # use screen_13::driver::DriverError;
29//! # use screen_13::driver::device::{Device, DeviceInfo};
30//! # use screen_13::driver::image::{ImageInfo};
31//! # use screen_13::pool::{Pool};
32//! # use screen_13::pool::lazy::{LazyPool};
33//! # fn main() -> Result<(), DriverError> {
34//! # let device = Arc::new(Device::create_headless(DeviceInfo::default())?);
35//! let mut pool = LazyPool::new(&device);
36//!
37//! let info = ImageInfo::image_2d(8, 8, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::STORAGE);
38//! let my_image = pool.lease(info)?;
39//!
40//! assert!(my_image.info.usage.contains(vk::ImageUsageFlags::STORAGE));
41//! # Ok(()) }
42//! ```
43//!
44//! # When Should You Use Which Pool?
45//!
46//! These are fairly high-level break-downs of when each pool should be considered. You may need
47//! to investigate each type of pool individually to provide the absolute best fit for your purpose.
48//!
49//! ### Use a [`FifoPool`](self::fifo::FifoPool) when:
50//! * Low memory usage is most important
51//! * Automatic bucket management is desired
52//!
53//! ### Use a [`LazyPool`](self::lazy::LazyPool) when:
54//! * Resources have different attributes each frame
55//!
56//! ### Use a [`HashPool`](self::hash::HashPool) when:
57//! * High performance is most important
58//! * Resources have consistent attributes each frame
59//!
60//! # When Should You Use Resource Aliasing?
61//!
62//! Wrapping any pool using [`AliasPool::new`](self::alias::AliasPool::new) enables resource
63//! aliasing, which prevents excess resources from being created even when different parts of your
64//! code request new resources.
65//!
66//! **_NOTE:_** Render graph submission will automatically attempt to re-order submitted passes to
67//! reduce contention between individual resources.
68//!
69//! **_NOTE:_** In cases where multiple aliased resources using identical request information are
70//! used in the same render graph pass you must ensure the resources are aliased from different
71//! pools. There is currently no tagging or filter which would prevent "ping-pong" rendering of such
72//! resources from being the same actual resources; this causes Vulkan validation warnings when
73//! reading from and writing to the same images, or whatever your operations may be.
74//!
75//! ### Pros:
76//!
77//! * Fewer resources are created overall
78//! * Wrapped pools behave like and retain all functionality of unwrapped pools
79//! * Easy to experiment with and benchmark in your existing code
80//!
81//! ### Cons:
82//!
83//! * Non-zero cost: Atomic load and compatibility check per active alias
84//! * May cause GPU stalling if there is not enough work being submitted
85//! * Aliased resources are typed `Arc<Lease<T>>` and are not guaranteed to be mutable or unique
86
87pub mod alias;
88pub mod fifo;
89pub mod hash;
90pub mod lazy;
91
92use {
93    crate::driver::{
94        CommandBuffer, DriverError,
95        accel_struct::{
96            AccelerationStructure, AccelerationStructureInfo, AccelerationStructureInfoBuilder,
97        },
98        buffer::{Buffer, BufferInfo, BufferInfoBuilder},
99        image::{Image, ImageInfo, ImageInfoBuilder},
100    },
101    derive_builder::{Builder, UninitializedFieldError},
102    std::{
103        fmt::Debug,
104        mem::ManuallyDrop,
105        ops::{Deref, DerefMut},
106        sync::{Arc, Weak},
107        thread::panicking,
108    },
109};
110
111#[cfg(feature = "parking_lot")]
112use parking_lot::Mutex;
113
114#[cfg(not(feature = "parking_lot"))]
115use std::sync::Mutex;
116
117type Cache<T> = Arc<Mutex<Vec<T>>>;
118type CacheRef<T> = Weak<Mutex<Vec<T>>>;
119
120fn lease_command_buffer(cache: &mut Vec<CommandBuffer>) -> Option<CommandBuffer> {
121    for idx in 0..cache.len() {
122        if unsafe {
123            let cmd_buf = cache.get_unchecked(idx);
124
125            // Don't lease this command buffer if it is unsignalled; we'll create a new one
126            // and wait for this, and those behind it, to signal.
127            cmd_buf
128                .device
129                .get_fence_status(cmd_buf.fence)
130                .unwrap_or_default()
131        } {
132            return Some(cache.swap_remove(idx));
133        }
134    }
135
136    None
137}
138
139/// Holds a leased resource and implements `Drop` in order to return the resource.
140///
141/// This simple wrapper type implements only the `AsRef`, `AsMut`, `Deref` and `DerefMut` traits
142/// and provides no other functionality. A freshly leased resource is guaranteed to have no other
143/// owners and may be mutably accessed.
144#[derive(Debug)]
145pub struct Lease<T> {
146    cache_ref: CacheRef<T>,
147    item: ManuallyDrop<T>,
148}
149
150impl<T> Lease<T> {
151    #[inline(always)]
152    fn new(cache_ref: CacheRef<T>, item: T) -> Self {
153        Self {
154            cache_ref,
155            item: ManuallyDrop::new(item),
156        }
157    }
158}
159
160impl<T> AsRef<T> for Lease<T> {
161    fn as_ref(&self) -> &T {
162        &self.item
163    }
164}
165
166impl<T> AsMut<T> for Lease<T> {
167    fn as_mut(&mut self) -> &mut T {
168        &mut self.item
169    }
170}
171
172impl<T> Deref for Lease<T> {
173    type Target = T;
174
175    fn deref(&self) -> &Self::Target {
176        &self.item
177    }
178}
179
180impl<T> DerefMut for Lease<T> {
181    fn deref_mut(&mut self) -> &mut Self::Target {
182        &mut self.item
183    }
184}
185
186impl<T> Drop for Lease<T> {
187    #[profiling::function]
188    fn drop(&mut self) {
189        if panicking() {
190            return;
191        }
192
193        // If the pool cache has been dropped we must manually drop the item, otherwise it goes back
194        // into the pool.
195        if let Some(cache) = self.cache_ref.upgrade() {
196            #[cfg_attr(not(feature = "parking_lot"), allow(unused_mut))]
197            let mut cache = cache.lock();
198
199            #[cfg(not(feature = "parking_lot"))]
200            let mut cache = cache.unwrap();
201
202            if cache.len() == cache.capacity() {
203                cache.pop();
204            }
205
206            cache.push(unsafe { ManuallyDrop::take(&mut self.item) });
207        } else {
208            unsafe {
209                ManuallyDrop::drop(&mut self.item);
210            }
211        }
212    }
213}
214
215/// Allows leasing of resources using driver information structures.
216pub trait Pool<I, T> {
217    /// Lease a resource.
218    fn lease(&mut self, info: I) -> Result<Lease<T>, DriverError>;
219}
220
221// Enable leasing items using their info builder type for convenience
222macro_rules! lease_builder {
223    ($info:ident => $item:ident) => {
224        paste::paste! {
225            impl<T> Pool<[<$info Builder>], $item> for T where T: Pool<$info, $item> {
226                fn lease(&mut self, builder: [<$info Builder>]) -> Result<Lease<$item>, DriverError> {
227                    let info = builder.build();
228
229                    self.lease(info)
230                }
231            }
232        }
233    };
234}
235
236lease_builder!(AccelerationStructureInfo => AccelerationStructure);
237lease_builder!(BufferInfo => Buffer);
238lease_builder!(ImageInfo => Image);
239
240/// Information used to create a [`FifoPool`](self::fifo::FifoPool),
241/// [`HashPool`](self::hash::HashPool) or [`LazyPool`](self::lazy::LazyPool) instance.
242#[derive(Builder, Clone, Copy, Debug)]
243#[builder(
244    build_fn(private, name = "fallible_build", error = "PoolInfoBuilderError"),
245    derive(Clone, Copy, Debug),
246    pattern = "owned"
247)]
248#[non_exhaustive]
249pub struct PoolInfo {
250    /// The maximum size of a single bucket of acceleration structure resource instances. The
251    /// default value is [`PoolInfo::DEFAULT_RESOURCE_CAPACITY`].
252    ///
253    /// # Note
254    ///
255    /// Individual [`Pool`] implementations store varying numbers of buckets. Read the documentation
256    /// of each implementation to understand how this affects total number of stored acceleration
257    /// structure instances.
258    #[builder(default = "PoolInfo::DEFAULT_RESOURCE_CAPACITY", setter(strip_option))]
259    pub accel_struct_capacity: usize,
260
261    /// The maximum size of a single bucket of buffer resource instances. The default value is
262    /// [`PoolInfo::DEFAULT_RESOURCE_CAPACITY`].
263    ///
264    /// # Note
265    ///
266    /// Individual [`Pool`] implementations store varying numbers of buckets. Read the documentation
267    /// of each implementation to understand how this affects total number of stored buffer
268    /// instances.
269    #[builder(default = "PoolInfo::DEFAULT_RESOURCE_CAPACITY", setter(strip_option))]
270    pub buffer_capacity: usize,
271
272    /// The maximum size of a single bucket of image resource instances. The default value is
273    /// [`PoolInfo::DEFAULT_RESOURCE_CAPACITY`].
274    ///
275    /// # Note
276    ///
277    /// Individual [`Pool`] implementations store varying numbers of buckets. Read the documentation
278    /// of each implementation to understand how this affects total number of stored image
279    /// instances.
280    #[builder(default = "PoolInfo::DEFAULT_RESOURCE_CAPACITY", setter(strip_option))]
281    pub image_capacity: usize,
282}
283
284impl PoolInfo {
285    /// The maximum size of a single bucket of resource instances.
286    pub const DEFAULT_RESOURCE_CAPACITY: usize = 16;
287
288    /// Constructs a new `PoolInfo` with the given acceleration structure, buffer and image resource
289    /// capacity for any single bucket.
290    pub const fn with_capacity(resource_capacity: usize) -> Self {
291        Self {
292            accel_struct_capacity: resource_capacity,
293            buffer_capacity: resource_capacity,
294            image_capacity: resource_capacity,
295        }
296    }
297
298    fn default_cache<T>() -> Cache<T> {
299        Cache::new(Mutex::new(Vec::with_capacity(
300            Self::DEFAULT_RESOURCE_CAPACITY,
301        )))
302    }
303
304    fn explicit_cache<T>(capacity: usize) -> Cache<T> {
305        Cache::new(Mutex::new(Vec::with_capacity(capacity)))
306    }
307}
308
309impl Default for PoolInfo {
310    fn default() -> Self {
311        PoolInfoBuilder::default().into()
312    }
313}
314
315impl From<PoolInfoBuilder> for PoolInfo {
316    fn from(info: PoolInfoBuilder) -> Self {
317        info.build()
318    }
319}
320
321impl From<usize> for PoolInfo {
322    fn from(value: usize) -> Self {
323        Self {
324            accel_struct_capacity: value,
325            buffer_capacity: value,
326            image_capacity: value,
327        }
328    }
329}
330
331// HACK: https://github.com/colin-kiegel/rust-derive-builder/issues/56
332impl PoolInfoBuilder {
333    /// Builds a new `PoolInfo`.
334    pub fn build(self) -> PoolInfo {
335        self.fallible_build()
336            .expect("All required fields set at initialization")
337    }
338}
339
340#[derive(Debug)]
341struct PoolInfoBuilderError;
342
343impl From<UninitializedFieldError> for PoolInfoBuilderError {
344    fn from(_: UninitializedFieldError) -> Self {
345        Self
346    }
347}