atagen

monolith

public
Star0
Watch0
e32c28d90a3dda1d0d9fff3d255479d8bd53a4f1
1
ssh://brewery@pls.lobotomise.me:33/atagen/monolith.git

Blame: crates/render-vk/src/staging.rs

e0b486d6097fatagen2026-10-19 07:26 UTC1
use crate::Error;
e0b486d6097fatagen2026-10-19 07:26 UTC2
use crate::device::Shared;
e0b486d6097fatagen2026-10-19 07:26 UTC3
use ash::vk;
e0b486d6097fatagen2026-10-19 07:26 UTC4
use gpu_allocator::MemoryLocation;
e0b486d6097fatagen2026-10-19 07:26 UTC5
use gpu_allocator::vulkan::{Allocation, AllocationCreateDesc, AllocationScheme};
e0b486d6097fatagen2026-10-19 07:26 UTC6
use std::sync::{Arc, Mutex};
e0b486d6097fatagen2026-10-19 07:26 UTC7
use std::time::{Duration, Instant};
e0b486d6097fatagen2026-10-19 07:26 UTC8
e0b486d6097fatagen2026-10-19 07:26 UTC9
/// Pooled staging memory kept at most, past which dropped buffers are freed.
e0b486d6097fatagen2026-10-19 07:26 UTC10
const POOL_BYTES: usize = 32 * 1024 * 1024;
e0b486d6097fatagen2026-10-19 07:26 UTC11
e0b486d6097fatagen2026-10-19 07:26 UTC12
/// How long a pooled buffer may go unused before it is freed.
e0b486d6097fatagen2026-10-19 07:26 UTC13
const POOL_IDLE: Duration = Duration::from_secs(2);
e0b486d6097fatagen2026-10-19 07:26 UTC14
e0b486d6097fatagen2026-10-19 07:26 UTC15
struct Pooled {
e0b486d6097fatagen2026-10-19 07:26 UTC16
    buffer: vk::Buffer,
e0b486d6097fatagen2026-10-19 07:26 UTC17
    allocation: Allocation,
e0b486d6097fatagen2026-10-19 07:26 UTC18
    size: usize,
e0b486d6097fatagen2026-10-19 07:26 UTC19
    usage: vk::BufferUsageFlags,
e0b486d6097fatagen2026-10-19 07:26 UTC20
    location: MemoryLocation,
e0b486d6097fatagen2026-10-19 07:26 UTC21
    returned: Instant,
e0b486d6097fatagen2026-10-19 07:26 UTC22
}
e0b486d6097fatagen2026-10-19 07:26 UTC23
e0b486d6097fatagen2026-10-19 07:26 UTC24
/// Staging buffers kept for reuse, see [`Staging`].
e0b486d6097fatagen2026-10-19 07:26 UTC25
pub(crate) struct StagingPool {
e0b486d6097fatagen2026-10-19 07:26 UTC26
    shared: Arc<Shared>,
e0b486d6097fatagen2026-10-19 07:26 UTC27
    free: Mutex<Vec<Pooled>>,
f8e6bc3e5529atagen2026-10-19 07:52 UTC28
    /// Granularity of flushes and invalidations of non-coherent memory.
f8e6bc3e5529atagen2026-10-19 07:52 UTC29
    atom: u64,
e0b486d6097fatagen2026-10-19 07:26 UTC30
}
e0b486d6097fatagen2026-10-19 07:26 UTC31
e0b486d6097fatagen2026-10-19 07:26 UTC32
impl StagingPool {
e0b486d6097fatagen2026-10-19 07:26 UTC33
    pub fn new(shared: &Arc<Shared>) -> Arc<Self> {
f8e6bc3e5529atagen2026-10-19 07:52 UTC34
        let limits = unsafe {
f8e6bc3e5529atagen2026-10-19 07:52 UTC35
            shared
f8e6bc3e5529atagen2026-10-19 07:52 UTC36
                .device
f8e6bc3e5529atagen2026-10-19 07:52 UTC37
                .instance
f8e6bc3e5529atagen2026-10-19 07:52 UTC38
                .get_physical_device_properties(shared.device.physical)
f8e6bc3e5529atagen2026-10-19 07:52 UTC39
        }
f8e6bc3e5529atagen2026-10-19 07:52 UTC40
        .limits;
e0b486d6097fatagen2026-10-19 07:26 UTC41
        Arc::new(Self {
e0b486d6097fatagen2026-10-19 07:26 UTC42
            shared: shared.clone(),
e0b486d6097fatagen2026-10-19 07:26 UTC43
            free: Mutex::new(Vec::new()),
f8e6bc3e5529atagen2026-10-19 07:52 UTC44
            atom: limits.non_coherent_atom_size.max(1),
e0b486d6097fatagen2026-10-19 07:26 UTC45
        })
e0b486d6097fatagen2026-10-19 07:26 UTC46
    }
e0b486d6097fatagen2026-10-19 07:26 UTC47
e0b486d6097fatagen2026-10-19 07:26 UTC48
    /// Frees pooled buffers that have sat unused for [`POOL_IDLE`].
e0b486d6097fatagen2026-10-19 07:26 UTC49
    pub fn trim(&self) {
e0b486d6097fatagen2026-10-19 07:26 UTC50
        let now = Instant::now();
e0b486d6097fatagen2026-10-19 07:26 UTC51
        let expired: Vec<Pooled> = self
e0b486d6097fatagen2026-10-19 07:26 UTC52
            .free
e0b486d6097fatagen2026-10-19 07:26 UTC53
            .lock()
e0b486d6097fatagen2026-10-19 07:26 UTC54
            .unwrap_or_else(|e| e.into_inner())
e0b486d6097fatagen2026-10-19 07:26 UTC55
            .extract_if(.., |p| now - p.returned >= POOL_IDLE)
e0b486d6097fatagen2026-10-19 07:26 UTC56
            .collect();
e0b486d6097fatagen2026-10-19 07:26 UTC57
        self.destroy(expired.into_iter().map(|p| (p.buffer, p.allocation)));
e0b486d6097fatagen2026-10-19 07:26 UTC58
    }
e0b486d6097fatagen2026-10-19 07:26 UTC59
e0b486d6097fatagen2026-10-19 07:26 UTC60
    fn destroy(&self, buffers: impl IntoIterator<Item = (vk::Buffer, Allocation)>) {
e0b486d6097fatagen2026-10-19 07:26 UTC61
        let mut buffers = buffers.into_iter().peekable();
e0b486d6097fatagen2026-10-19 07:26 UTC62
        if buffers.peek().is_none() {
e0b486d6097fatagen2026-10-19 07:26 UTC63
            return;
e0b486d6097fatagen2026-10-19 07:26 UTC64
        }
e0b486d6097fatagen2026-10-19 07:26 UTC65
        let mut allocator = self
e0b486d6097fatagen2026-10-19 07:26 UTC66
            .shared
e0b486d6097fatagen2026-10-19 07:26 UTC67
            .allocator
e0b486d6097fatagen2026-10-19 07:26 UTC68
            .lock()
e0b486d6097fatagen2026-10-19 07:26 UTC69
            .unwrap_or_else(|e| e.into_inner());
e0b486d6097fatagen2026-10-19 07:26 UTC70
        for (buffer, allocation) in buffers {
e0b486d6097fatagen2026-10-19 07:26 UTC71
            unsafe { self.shared.device.device.destroy_buffer(buffer, None) };
e0b486d6097fatagen2026-10-19 07:26 UTC72
            let _ = allocator.free(allocation);
e0b486d6097fatagen2026-10-19 07:26 UTC73
        }
e0b486d6097fatagen2026-10-19 07:26 UTC74
    }
e0b486d6097fatagen2026-10-19 07:26 UTC75
}
e0b486d6097fatagen2026-10-19 07:26 UTC76
e0b486d6097fatagen2026-10-19 07:26 UTC77
impl Drop for StagingPool {
e0b486d6097fatagen2026-10-19 07:26 UTC78
    fn drop(&mut self) {
e0b486d6097fatagen2026-10-19 07:26 UTC79
        let free = std::mem::take(self.free.get_mut().unwrap_or_else(|e| e.into_inner()));
e0b486d6097fatagen2026-10-19 07:26 UTC80
        self.destroy(free.into_iter().map(|p| (p.buffer, p.allocation)));
e0b486d6097fatagen2026-10-19 07:26 UTC81
    }
e0b486d6097fatagen2026-10-19 07:26 UTC82
}
e0b486d6097fatagen2026-10-19 07:26 UTC83
e0b486d6097fatagen2026-10-19 07:26 UTC84
/// A host-visible buffer for uploads and readbacks. Dropped ones go back to
e0b486d6097fatagen2026-10-19 07:26 UTC85
/// a pool instead of being freed. Allocating and freeing large host-visible
e0b486d6097fatagen2026-10-19 07:26 UTC86
/// memory takes a driver lock on NVIDIA that `vkQueueSubmit` also waits on,
e0b486d6097fatagen2026-10-19 07:26 UTC87
/// so a staging buffer made per upload on the import worker stalled the main
e0b486d6097fatagen2026-10-19 07:26 UTC88
/// thread's submits for 1 to 3 ms.
e0b486d6097fatagen2026-10-19 07:26 UTC89
pub(crate) struct Staging {
e0b486d6097fatagen2026-10-19 07:26 UTC90
    pub pool: Arc<StagingPool>,
e0b486d6097fatagen2026-10-19 07:26 UTC91
    pub buffer: vk::Buffer,
e0b486d6097fatagen2026-10-19 07:26 UTC92
    allocation: Option<Allocation>,
e0b486d6097fatagen2026-10-19 07:26 UTC93
    size: usize,
e0b486d6097fatagen2026-10-19 07:26 UTC94
    usage: vk::BufferUsageFlags,
e0b486d6097fatagen2026-10-19 07:26 UTC95
    location: MemoryLocation,
e0b486d6097fatagen2026-10-19 07:26 UTC96
}
e0b486d6097fatagen2026-10-19 07:26 UTC97
e0b486d6097fatagen2026-10-19 07:26 UTC98
impl Staging {
e0b486d6097fatagen2026-10-19 07:26 UTC99
    pub fn new(
e0b486d6097fatagen2026-10-19 07:26 UTC100
        pool: &Arc<StagingPool>,
e0b486d6097fatagen2026-10-19 07:26 UTC101
        size: usize,
e0b486d6097fatagen2026-10-19 07:26 UTC102
        usage: vk::BufferUsageFlags,
e0b486d6097fatagen2026-10-19 07:26 UTC103
        location: MemoryLocation,
e0b486d6097fatagen2026-10-19 07:26 UTC104
    ) -> Result<Self, Error> {
e0b486d6097fatagen2026-10-19 07:26 UTC105
        let reused = {
e0b486d6097fatagen2026-10-19 07:26 UTC106
            let mut free = pool.free.lock().unwrap_or_else(|e| e.into_inner());
e0b486d6097fatagen2026-10-19 07:26 UTC107
            let fits = |p: &Pooled| {
e0b486d6097fatagen2026-10-19 07:26 UTC108
                p.usage == usage && p.location == location && p.size >= size && p.size <= size * 2
e0b486d6097fatagen2026-10-19 07:26 UTC109
            };
e0b486d6097fatagen2026-10-19 07:26 UTC110
            free.iter().position(fits).map(|i| free.swap_remove(i))
e0b486d6097fatagen2026-10-19 07:26 UTC111
        };
e0b486d6097fatagen2026-10-19 07:26 UTC112
        if let Some(p) = reused {
e0b486d6097fatagen2026-10-19 07:26 UTC113
            return Ok(Self {
e0b486d6097fatagen2026-10-19 07:26 UTC114
                pool: pool.clone(),
e0b486d6097fatagen2026-10-19 07:26 UTC115
                buffer: p.buffer,
e0b486d6097fatagen2026-10-19 07:26 UTC116
                allocation: Some(p.allocation),
e0b486d6097fatagen2026-10-19 07:26 UTC117
                size: p.size,
e0b486d6097fatagen2026-10-19 07:26 UTC118
                usage,
e0b486d6097fatagen2026-10-19 07:26 UTC119
                location,
e0b486d6097fatagen2026-10-19 07:26 UTC120
            });
e0b486d6097fatagen2026-10-19 07:26 UTC121
        }
e0b486d6097fatagen2026-10-19 07:26 UTC122
        let d = &pool.shared.device.device;
e0b486d6097fatagen2026-10-19 07:26 UTC123
        let buffer = unsafe {
e0b486d6097fatagen2026-10-19 07:26 UTC124
            d.create_buffer(
e0b486d6097fatagen2026-10-19 07:26 UTC125
                &vk::BufferCreateInfo::default()
e0b486d6097fatagen2026-10-19 07:26 UTC126
                    .size(size as u64)
e0b486d6097fatagen2026-10-19 07:26 UTC127
                    .usage(usage),
e0b486d6097fatagen2026-10-19 07:26 UTC128
                None,
e0b486d6097fatagen2026-10-19 07:26 UTC129
            )?
e0b486d6097fatagen2026-10-19 07:26 UTC130
        };
e0b486d6097fatagen2026-10-19 07:26 UTC131
        let requirements = unsafe { d.get_buffer_memory_requirements(buffer) };
e0b486d6097fatagen2026-10-19 07:26 UTC132
        let allocation = pool
e0b486d6097fatagen2026-10-19 07:26 UTC133
            .shared
e0b486d6097fatagen2026-10-19 07:26 UTC134
            .allocator
e0b486d6097fatagen2026-10-19 07:26 UTC135
            .lock()
e0b486d6097fatagen2026-10-19 07:26 UTC136
            .unwrap_or_else(|e| e.into_inner())
e0b486d6097fatagen2026-10-19 07:26 UTC137
            .allocate(&AllocationCreateDesc {
e0b486d6097fatagen2026-10-19 07:26 UTC138
                name: "staging",
e0b486d6097fatagen2026-10-19 07:26 UTC139
                requirements,
e0b486d6097fatagen2026-10-19 07:26 UTC140
                location,
e0b486d6097fatagen2026-10-19 07:26 UTC141
                linear: true,
e0b486d6097fatagen2026-10-19 07:26 UTC142
                allocation_scheme: AllocationScheme::GpuAllocatorManaged,
e0b486d6097fatagen2026-10-19 07:26 UTC143
            });
e0b486d6097fatagen2026-10-19 07:26 UTC144
        let allocation = match allocation {
e0b486d6097fatagen2026-10-19 07:26 UTC145
            Ok(a) => a,
e0b486d6097fatagen2026-10-19 07:26 UTC146
            Err(e) => {
e0b486d6097fatagen2026-10-19 07:26 UTC147
                unsafe { d.destroy_buffer(buffer, None) };
e0b486d6097fatagen2026-10-19 07:26 UTC148
                return Err(e.into());
e0b486d6097fatagen2026-10-19 07:26 UTC149
            }
e0b486d6097fatagen2026-10-19 07:26 UTC150
        };
e0b486d6097fatagen2026-10-19 07:26 UTC151
        unsafe { d.bind_buffer_memory(buffer, allocation.memory(), allocation.offset())? };
e0b486d6097fatagen2026-10-19 07:26 UTC152
        Ok(Self {
e0b486d6097fatagen2026-10-19 07:26 UTC153
            pool: pool.clone(),
e0b486d6097fatagen2026-10-19 07:26 UTC154
            buffer,
e0b486d6097fatagen2026-10-19 07:26 UTC155
            allocation: Some(allocation),
e0b486d6097fatagen2026-10-19 07:26 UTC156
            size,
e0b486d6097fatagen2026-10-19 07:26 UTC157
            usage,
e0b486d6097fatagen2026-10-19 07:26 UTC158
            location,
e0b486d6097fatagen2026-10-19 07:26 UTC159
        })
e0b486d6097fatagen2026-10-19 07:26 UTC160
    }
e0b486d6097fatagen2026-10-19 07:26 UTC161
f8e6bc3e5529atagen2026-10-19 07:52 UTC162
    /// The mapped bytes, with whatever the GPU wrote to them made visible.
acf5e9848529atagen2026-10-19 07:27 UTC163
    pub fn bytes(&self) -> Result<&[u8], Error> {
f8e6bc3e5529atagen2026-10-19 07:52 UTC164
        self.host_sync(false)?;
e0b486d6097fatagen2026-10-19 07:26 UTC165
        self.allocation
e0b486d6097fatagen2026-10-19 07:26 UTC166
            .as_ref()
e0b486d6097fatagen2026-10-19 07:26 UTC167
            .and_then(|a| a.mapped_slice())
acf5e9848529atagen2026-10-19 07:27 UTC168
            .ok_or(Error::StagingUnmapped)
e0b486d6097fatagen2026-10-19 07:26 UTC169
    }
e0b486d6097fatagen2026-10-19 07:26 UTC170
f8e6bc3e5529atagen2026-10-19 07:52 UTC171
    /// Makes what the host wrote through `bytes_mut` visible to the GPU.
f8e6bc3e5529atagen2026-10-19 07:52 UTC172
    pub fn flush(&self) -> Result<(), Error> {
f8e6bc3e5529atagen2026-10-19 07:52 UTC173
        self.host_sync(true)
f8e6bc3e5529atagen2026-10-19 07:52 UTC174
    }
f8e6bc3e5529atagen2026-10-19 07:52 UTC175
f8e6bc3e5529atagen2026-10-19 07:52 UTC176
    /// Flushes or invalidates the mapping. Coherent memory needs neither.
f8e6bc3e5529atagen2026-10-19 07:52 UTC177
    fn host_sync(&self, flush: bool) -> Result<(), Error> {
f8e6bc3e5529atagen2026-10-19 07:52 UTC178
        let Some(allocation) = self.allocation.as_ref() else {
f8e6bc3e5529atagen2026-10-19 07:52 UTC179
            return Ok(());
f8e6bc3e5529atagen2026-10-19 07:52 UTC180
        };
f8e6bc3e5529atagen2026-10-19 07:52 UTC181
        if allocation
f8e6bc3e5529atagen2026-10-19 07:52 UTC182
            .memory_properties()
f8e6bc3e5529atagen2026-10-19 07:52 UTC183
            .contains(vk::MemoryPropertyFlags::HOST_COHERENT)
f8e6bc3e5529atagen2026-10-19 07:52 UTC184
        {
f8e6bc3e5529atagen2026-10-19 07:52 UTC185
            return Ok(());
f8e6bc3e5529atagen2026-10-19 07:52 UTC186
        }
f8e6bc3e5529atagen2026-10-19 07:52 UTC187
        let atom = self.pool.atom;
f8e6bc3e5529atagen2026-10-19 07:52 UTC188
        let range = [vk::MappedMemoryRange::default()
f8e6bc3e5529atagen2026-10-19 07:52 UTC189
            .memory(unsafe { allocation.memory() })
f8e6bc3e5529atagen2026-10-19 07:52 UTC190
            .offset(allocation.offset() / atom * atom)
f8e6bc3e5529atagen2026-10-19 07:52 UTC191
            .size(vk::WHOLE_SIZE)];
f8e6bc3e5529atagen2026-10-19 07:52 UTC192
        let d = &self.pool.shared.device.device;
f8e6bc3e5529atagen2026-10-19 07:52 UTC193
        unsafe {
f8e6bc3e5529atagen2026-10-19 07:52 UTC194
            if flush {
f8e6bc3e5529atagen2026-10-19 07:52 UTC195
                d.flush_mapped_memory_ranges(&range)?;
f8e6bc3e5529atagen2026-10-19 07:52 UTC196
            } else {
f8e6bc3e5529atagen2026-10-19 07:52 UTC197
                d.invalidate_mapped_memory_ranges(&range)?;
f8e6bc3e5529atagen2026-10-19 07:52 UTC198
            }
f8e6bc3e5529atagen2026-10-19 07:52 UTC199
        }
f8e6bc3e5529atagen2026-10-19 07:52 UTC200
        Ok(())
f8e6bc3e5529atagen2026-10-19 07:52 UTC201
    }
f8e6bc3e5529atagen2026-10-19 07:52 UTC202
acf5e9848529atagen2026-10-19 07:27 UTC203
    pub fn bytes_mut(&mut self) -> Result<&mut [u8], Error> {
e0b486d6097fatagen2026-10-19 07:26 UTC204
        self.allocation
e0b486d6097fatagen2026-10-19 07:26 UTC205
            .as_mut()
e0b486d6097fatagen2026-10-19 07:26 UTC206
            .and_then(|a| a.mapped_slice_mut())
acf5e9848529atagen2026-10-19 07:27 UTC207
            .ok_or(Error::StagingUnmapped)
e0b486d6097fatagen2026-10-19 07:26 UTC208
    }
e0b486d6097fatagen2026-10-19 07:26 UTC209
}
e0b486d6097fatagen2026-10-19 07:26 UTC210
e0b486d6097fatagen2026-10-19 07:26 UTC211
impl Drop for Staging {
e0b486d6097fatagen2026-10-19 07:26 UTC212
    fn drop(&mut self) {
e0b486d6097fatagen2026-10-19 07:26 UTC213
        let Some(allocation) = self.allocation.take() else {
e0b486d6097fatagen2026-10-19 07:26 UTC214
            return;
e0b486d6097fatagen2026-10-19 07:26 UTC215
        };
e0b486d6097fatagen2026-10-19 07:26 UTC216
        let mut free = self.pool.free.lock().unwrap_or_else(|e| e.into_inner());
e0b486d6097fatagen2026-10-19 07:26 UTC217
        let pooled: usize = free.iter().map(|p| p.size).sum();
e0b486d6097fatagen2026-10-19 07:26 UTC218
        if pooled + self.size <= POOL_BYTES {
e0b486d6097fatagen2026-10-19 07:26 UTC219
            free.push(Pooled {
e0b486d6097fatagen2026-10-19 07:26 UTC220
                buffer: self.buffer,
e0b486d6097fatagen2026-10-19 07:26 UTC221
                allocation,
e0b486d6097fatagen2026-10-19 07:26 UTC222
                size: self.size,
e0b486d6097fatagen2026-10-19 07:26 UTC223
                usage: self.usage,
e0b486d6097fatagen2026-10-19 07:26 UTC224
                location: self.location,
e0b486d6097fatagen2026-10-19 07:26 UTC225
                returned: Instant::now(),
e0b486d6097fatagen2026-10-19 07:26 UTC226
            });
e0b486d6097fatagen2026-10-19 07:26 UTC227
            return;
e0b486d6097fatagen2026-10-19 07:26 UTC228
        }
e0b486d6097fatagen2026-10-19 07:26 UTC229
        drop(free);
e0b486d6097fatagen2026-10-19 07:26 UTC230
        self.pool.destroy([(self.buffer, allocation)]);
e0b486d6097fatagen2026-10-19 07:26 UTC231
    }
e0b486d6097fatagen2026-10-19 07:26 UTC232
}