Commit e0b486d6097f
tarball6 files changed+204-193
@@ -8,27 +8,10 @@ use std::sync::Mutex;
88
/// Fields drop in declaration order, so the allocator frees its memory before99
/// the device is destroyed.1010
pub(crate) struct Shared {11
- /// Staging buffers kept for reuse, see `Staging`.12
- pub staging_pool: Mutex<Vec<crate::image::Pooled>>,1311
pub allocator: Mutex<Allocator>,1412
pub device: Device,1513
}1614
17
-impl Drop for Shared {18
- fn drop(&mut self) {19
- let pool = std::mem::take(20
- self.staging_pool21
- .get_mut()22
- .unwrap_or_else(|e| e.into_inner()),23
- );24
- let allocator = self.allocator.get_mut().unwrap_or_else(|e| e.into_inner());25
- for p in pool {26
- unsafe { self.device.device.destroy_buffer(p.buffer, None) };27
- let _ = allocator.free(p.allocation);28
- }29
- }30
-}31
-3215
/// Loaders for the extensions that dmabuf import, export and fence export3316
/// need. Absent when the device lacks any of them.3417
pub(crate) struct DmabufExt {@@ -219,7 +202,6 @@ impl Shared {
219202
spare_fences: Mutex::new(Vec::new()),220203
};221204
Ok(Self {222
- staging_pool: Mutex::new(Vec::new()),223205
allocator: Mutex::new(allocator?),224206
device,225207
})@@ -9,7 +9,6 @@ use smithay::backend::renderer::{Texture, TextureMapping};
99
use std::fmt;1010
use std::marker::PhantomData;1111
use std::sync::{Arc, Mutex};12
-use std::time::{Duration, Instant};1312
1413
pub(crate) enum Memory {1514
Allocated(Allocation),@@ -181,168 +180,6 @@ pub(crate) fn color_range() -> vk::ImageSubresourceRange {
181180
.layer_count(1)182181
}183182
184
-/// A host-visible buffer for uploads and readbacks. Dropped ones go back to185
-/// a pool on `Shared` instead of being freed. Allocating and freeing large186
-/// host-visible memory takes a driver lock on NVIDIA that `vkQueueSubmit`187
-/// also waits on, so a staging buffer made per upload on the import worker188
-/// stalled the main thread's submits for 1 to 3 ms.189
-pub(crate) struct Staging {190
- pub shared: Arc<Shared>,191
- pub buffer: vk::Buffer,192
- pub allocation: Option<Allocation>,193
- size: usize,194
- usage: vk::BufferUsageFlags,195
- location: MemoryLocation,196
-}197
-198
-pub(crate) struct Pooled {199
- pub buffer: vk::Buffer,200
- pub allocation: Allocation,201
- size: usize,202
- usage: vk::BufferUsageFlags,203
- location: MemoryLocation,204
- returned: Instant,205
-}206
-207
-/// Pooled staging memory kept at most, past which dropped buffers are freed.208
-const POOL_BYTES: usize = 32 * 1024 * 1024;209
-210
-/// How long a pooled buffer may go unused before it is freed.211
-const POOL_IDLE: Duration = Duration::from_secs(2);212
-213
-/// Frees pooled staging buffers that have sat unused for [`POOL_IDLE`].214
-pub(crate) fn trim_staging(shared: &Shared) {215
- let now = Instant::now();216
- let expired: Vec<Pooled> = shared217
- .staging_pool218
- .lock()219
- .unwrap_or_else(|e| e.into_inner())220
- .extract_if(.., |p| now - p.returned >= POOL_IDLE)221
- .collect();222
- if expired.is_empty() {223
- return;224
- }225
- let mut allocator = shared.allocator.lock().unwrap_or_else(|e| e.into_inner());226
- for p in expired {227
- unsafe { shared.device.device.destroy_buffer(p.buffer, None) };228
- let _ = allocator.free(p.allocation);229
- }230
-}231
-232
-impl Staging {233
- pub fn new(234
- shared: &Arc<Shared>,235
- size: usize,236
- usage: vk::BufferUsageFlags,237
- location: MemoryLocation,238
- ) -> Result<Self, Error> {239
- let reused = {240
- let mut pool = shared241
- .staging_pool242
- .lock()243
- .unwrap_or_else(|e| e.into_inner());244
- let fits = |p: &Pooled| {245
- p.usage == usage && p.location == location && p.size >= size && p.size <= size * 2246
- };247
- pool.iter().position(fits).map(|i| pool.swap_remove(i))248
- };249
- if let Some(p) = reused {250
- return Ok(Self {251
- shared: shared.clone(),252
- buffer: p.buffer,253
- allocation: Some(p.allocation),254
- size: p.size,255
- usage,256
- location,257
- });258
- }259
- let d = &shared.device.device;260
- let buffer = unsafe {261
- d.create_buffer(262
- &vk::BufferCreateInfo::default()263
- .size(size as u64)264
- .usage(usage),265
- None,266
- )?267
- };268
- let requirements = unsafe { d.get_buffer_memory_requirements(buffer) };269
- let allocation = shared270
- .allocator271
- .lock()272
- .unwrap_or_else(|e| e.into_inner())273
- .allocate(&AllocationCreateDesc {274
- name: "staging",275
- requirements,276
- location,277
- linear: true,278
- allocation_scheme: AllocationScheme::GpuAllocatorManaged,279
- });280
- let allocation = match allocation {281
- Ok(a) => a,282
- Err(e) => {283
- unsafe { d.destroy_buffer(buffer, None) };284
- return Err(e.into());285
- }286
- };287
- unsafe { d.bind_buffer_memory(buffer, allocation.memory(), allocation.offset())? };288
- Ok(Self {289
- shared: shared.clone(),290
- buffer,291
- allocation: Some(allocation),292
- size,293
- usage,294
- location,295
- })296
- }297
-298
- pub fn bytes(&self) -> &[u8] {299
- self.allocation300
- .as_ref()301
- .and_then(|a| a.mapped_slice())302
- .unwrap_or(&[])303
- }304
-305
- pub fn bytes_mut(&mut self) -> &mut [u8] {306
- self.allocation307
- .as_mut()308
- .and_then(|a| a.mapped_slice_mut())309
- .unwrap_or(&mut [])310
- }311
-}312
-313
-impl Drop for Staging {314
- fn drop(&mut self) {315
- let Some(allocation) = self.allocation.take() else {316
- return;317
- };318
- let mut pool = self319
- .shared320
- .staging_pool321
- .lock()322
- .unwrap_or_else(|e| e.into_inner());323
- let pooled: usize = pool.iter().map(|p| p.size).sum();324
- if pooled + self.size <= POOL_BYTES {325
- pool.push(Pooled {326
- buffer: self.buffer,327
- allocation,328
- size: self.size,329
- usage: self.usage,330
- location: self.location,331
- returned: Instant::now(),332
- });333
- return;334
- }335
- drop(pool);336
- unsafe { self.shared.device.device.destroy_buffer(self.buffer, None) };337
- let _ = self338
- .shared339
- .allocator340
- .lock()341
- .unwrap_or_else(|e| e.into_inner())342
- .free(allocation);343
- }344
-}345
-346183
/// A sampled texture.347184
#[derive(Clone, Debug)]348185
pub struct VulkanTexture(pub(crate) Arc<ImageInner>);@@ -18,6 +18,7 @@ mod frame;
1818
mod image;1919
mod pipeline;2020
mod renderer;21
+mod staging;2122
mod sync;2223
mod transform;2324
@@ -10,8 +10,9 @@ use crate::device::Shared;
1010
use crate::dmabuf::{self, DmabufCaps, RENDER_USAGE, SAMPLE_USAGE, VulkanAllocator};1111
use crate::format::{BYTES_PER_PIXEL, SUPPORTED};1212
use crate::frame::{Op, VulkanFrame};13
-use crate::image::{ImageInner, Staging, VulkanImage, VulkanMapping, VulkanTarget, VulkanTexture};13
+use crate::image::{ImageInner, VulkanImage, VulkanMapping, VulkanTarget, VulkanTexture};1414
use crate::pipeline::Pipelines;15
+use crate::staging::{Staging, StagingPool};1516
use crate::sync::{FenceInner, VkFence};1617
use ash::vk;1718
use gpu_allocator::MemoryLocation;@@ -55,6 +56,7 @@ pub struct VulkanRenderer {
5556
debug: DebugFlags,5657
context: ContextId<VulkanTexture>,5758
caps: Arc<DmabufCaps>,59
+ staging_pool: Arc<StagingPool>,5860
shared: Arc<Shared>,5961
submissions: Cell<u64>,6062
/// Shm uploads staged off the main thread, by buffer.@@ -130,6 +132,7 @@ impl VulkanRenderer {
130132
debug: DebugFlags::empty(),131133
context: ContextId::new(),132134
caps: Arc::new(DmabufCaps::query(&shared)),135
+ staging_pool: StagingPool::new(&shared),133136
shared,134137
})135138
}@@ -187,7 +190,7 @@ impl VulkanRenderer {
187190
self.dmabuf_textures.retain(|k, _| !k.is_gone());188191
self.dmabuf_targets.retain(|k, _| !k.is_gone());189192
self.staged.retain(|_, (b, _)| b.is_alive());190
- crate::image::trim_staging(&self.shared);193
+ self.staging_pool.trim();191194
}192195
193196
fn begin(&mut self) -> Result<vk::CommandBuffer, Error> {@@ -467,7 +470,7 @@ impl VulkanRenderer {
467470
if region.size.w <= 0 || region.size.h <= 0 {468471
return Ok(());469472
}470
- let staging = stage_rows(&self.shared, region, row)?;473
+ let staging = stage_rows(&self.staging_pool, region, row)?;471474
self.copy_staged(image, region, staging)472475
}473476
@@ -541,7 +544,7 @@ impl VulkanRenderer {
541544
let swap = needs_swizzle(image.fourcc, fourcc)?;542545
let len = (w as usize * h as usize * BYTES_PER_PIXEL).max(BYTES_PER_PIXEL);543546
let staging = Staging::new(544
- &self.shared,547
+ &self.staging_pool,545548
len,546549
vk::BufferUsageFlags::TRANSFER_DST,547550
MemoryLocation::GpuToCpu,@@ -1,8 +1,8 @@
11
use super::VulkanRenderer;22
use crate::Error;3
-use crate::device::Shared;43
use crate::format::BYTES_PER_PIXEL;5
-use crate::image::{ImageInner, Staging, VulkanTexture};4
+use crate::image::{ImageInner, VulkanTexture};5
+use crate::staging::{Staging, StagingPool};66
use ash::vk;77
use gpu_allocator::MemoryLocation;88
use smithay::backend::allocator::Fourcc;@@ -100,14 +100,14 @@ struct ShmCache(Mutex<Option<VulkanTexture>>);
100100
101101
/// Copies `region` of a full-image `row` source into new staging memory.102102
pub(super) fn stage_rows<'a>(103
- shared: &Arc<Shared>,103
+ pool: &Arc<StagingPool>,104104
region: Rectangle<i32, Buffer>,105105
row: impl Fn(usize) -> &'a [u8],106106
) -> Result<Staging, Error> {107107
let (w, h) = (region.size.w as usize, region.size.h as usize);108108
let line = w * BYTES_PER_PIXEL;109109
let mut staging = Staging::new(110
- shared,110
+ pool,111111
line * h,112112
vk::BufferUsageFlags::TRANSFER_SRC,113113
MemoryLocation::CpuToGpu,@@ -132,7 +132,7 @@ pub struct StagedShm {
132132
/// it came from takes the result back with `adopt_staged`.133133
#[derive(Clone)]134134
pub struct ShmStager {135
- shared: Arc<Shared>,135
+ staging_pool: Arc<StagingPool>,136136
}137137
138138
impl ShmStager {@@ -161,7 +161,7 @@ impl ShmStager {
161161
let rw = region.size.w as usize * BYTES_PER_PIXEL;162162
// SAFETY: the bounds check above keeps every row inside the163163
// `len` bytes smithay mapped at `ptr`.164
- let staging = stage_rows(&self.shared, region, |y| unsafe {164
+ let staging = stage_rows(&self.staging_pool, region, |y| unsafe {165165
std::slice::from_raw_parts(ptr.add(offset + y * stride + x), rw)166166
})?;167167
Ok(StagedShm {@@ -178,13 +178,13 @@ impl ShmStager {
178178
impl VulkanRenderer {179179
pub fn shm_stager(&self) -> ShmStager {180180
ShmStager {181
- shared: self.shared.clone(),181
+ staging_pool: self.staging_pool.clone(),182182
}183183
}184184
185185
/// Hands back pixels a stager copied, used by the next import of `buffer`.186186
pub fn adopt_staged(&mut self, buffer: &WlBuffer, staged: StagedShm) {187
- if Arc::ptr_eq(&staged.staging.shared, &self.shared) {187
+ if Arc::ptr_eq(&staged.staging.pool, &self.staging_pool) {188188
self.staged.insert(buffer.id(), (buffer.clone(), staged));189189
}190190
}@@ -0,0 +1,188 @@
1
+use crate::Error;2
+use crate::device::Shared;3
+use ash::vk;4
+use gpu_allocator::MemoryLocation;5
+use gpu_allocator::vulkan::{Allocation, AllocationCreateDesc, AllocationScheme};6
+use std::sync::{Arc, Mutex};7
+use std::time::{Duration, Instant};8
+9
+/// Pooled staging memory kept at most, past which dropped buffers are freed.10
+const POOL_BYTES: usize = 32 * 1024 * 1024;11
+12
+/// How long a pooled buffer may go unused before it is freed.13
+const POOL_IDLE: Duration = Duration::from_secs(2);14
+15
+struct Pooled {16
+ buffer: vk::Buffer,17
+ allocation: Allocation,18
+ size: usize,19
+ usage: vk::BufferUsageFlags,20
+ location: MemoryLocation,21
+ returned: Instant,22
+}23
+24
+/// Staging buffers kept for reuse, see [`Staging`].25
+pub(crate) struct StagingPool {26
+ shared: Arc<Shared>,27
+ free: Mutex<Vec<Pooled>>,28
+}29
+30
+impl StagingPool {31
+ pub fn new(shared: &Arc<Shared>) -> Arc<Self> {32
+ Arc::new(Self {33
+ shared: shared.clone(),34
+ free: Mutex::new(Vec::new()),35
+ })36
+ }37
+38
+ /// Frees pooled buffers that have sat unused for [`POOL_IDLE`].39
+ pub fn trim(&self) {40
+ let now = Instant::now();41
+ let expired: Vec<Pooled> = self42
+ .free43
+ .lock()44
+ .unwrap_or_else(|e| e.into_inner())45
+ .extract_if(.., |p| now - p.returned >= POOL_IDLE)46
+ .collect();47
+ self.destroy(expired.into_iter().map(|p| (p.buffer, p.allocation)));48
+ }49
+50
+ fn destroy(&self, buffers: impl IntoIterator<Item = (vk::Buffer, Allocation)>) {51
+ let mut buffers = buffers.into_iter().peekable();52
+ if buffers.peek().is_none() {53
+ return;54
+ }55
+ let mut allocator = self56
+ .shared57
+ .allocator58
+ .lock()59
+ .unwrap_or_else(|e| e.into_inner());60
+ for (buffer, allocation) in buffers {61
+ unsafe { self.shared.device.device.destroy_buffer(buffer, None) };62
+ let _ = allocator.free(allocation);63
+ }64
+ }65
+}66
+67
+impl Drop for StagingPool {68
+ fn drop(&mut self) {69
+ let free = std::mem::take(self.free.get_mut().unwrap_or_else(|e| e.into_inner()));70
+ self.destroy(free.into_iter().map(|p| (p.buffer, p.allocation)));71
+ }72
+}73
+74
+/// A host-visible buffer for uploads and readbacks. Dropped ones go back to75
+/// a pool instead of being freed. Allocating and freeing large host-visible76
+/// memory takes a driver lock on NVIDIA that `vkQueueSubmit` also waits on,77
+/// so a staging buffer made per upload on the import worker stalled the main78
+/// thread's submits for 1 to 3 ms.79
+pub(crate) struct Staging {80
+ pub pool: Arc<StagingPool>,81
+ pub buffer: vk::Buffer,82
+ allocation: Option<Allocation>,83
+ size: usize,84
+ usage: vk::BufferUsageFlags,85
+ location: MemoryLocation,86
+}87
+88
+impl Staging {89
+ pub fn new(90
+ pool: &Arc<StagingPool>,91
+ size: usize,92
+ usage: vk::BufferUsageFlags,93
+ location: MemoryLocation,94
+ ) -> Result<Self, Error> {95
+ let reused = {96
+ let mut free = pool.free.lock().unwrap_or_else(|e| e.into_inner());97
+ let fits = |p: &Pooled| {98
+ p.usage == usage && p.location == location && p.size >= size && p.size <= size * 299
+ };100
+ free.iter().position(fits).map(|i| free.swap_remove(i))101
+ };102
+ if let Some(p) = reused {103
+ return Ok(Self {104
+ pool: pool.clone(),105
+ buffer: p.buffer,106
+ allocation: Some(p.allocation),107
+ size: p.size,108
+ usage,109
+ location,110
+ });111
+ }112
+ let d = &pool.shared.device.device;113
+ let buffer = unsafe {114
+ d.create_buffer(115
+ &vk::BufferCreateInfo::default()116
+ .size(size as u64)117
+ .usage(usage),118
+ None,119
+ )?120
+ };121
+ let requirements = unsafe { d.get_buffer_memory_requirements(buffer) };122
+ let allocation = pool123
+ .shared124
+ .allocator125
+ .lock()126
+ .unwrap_or_else(|e| e.into_inner())127
+ .allocate(&AllocationCreateDesc {128
+ name: "staging",129
+ requirements,130
+ location,131
+ linear: true,132
+ allocation_scheme: AllocationScheme::GpuAllocatorManaged,133
+ });134
+ let allocation = match allocation {135
+ Ok(a) => a,136
+ Err(e) => {137
+ unsafe { d.destroy_buffer(buffer, None) };138
+ return Err(e.into());139
+ }140
+ };141
+ unsafe { d.bind_buffer_memory(buffer, allocation.memory(), allocation.offset())? };142
+ Ok(Self {143
+ pool: pool.clone(),144
+ buffer,145
+ allocation: Some(allocation),146
+ size,147
+ usage,148
+ location,149
+ })150
+ }151
+152
+ pub fn bytes(&self) -> &[u8] {153
+ self.allocation154
+ .as_ref()155
+ .and_then(|a| a.mapped_slice())156
+ .unwrap_or(&[])157
+ }158
+159
+ pub fn bytes_mut(&mut self) -> &mut [u8] {160
+ self.allocation161
+ .as_mut()162
+ .and_then(|a| a.mapped_slice_mut())163
+ .unwrap_or(&mut [])164
+ }165
+}166
+167
+impl Drop for Staging {168
+ fn drop(&mut self) {169
+ let Some(allocation) = self.allocation.take() else {170
+ return;171
+ };172
+ let mut free = self.pool.free.lock().unwrap_or_else(|e| e.into_inner());173
+ let pooled: usize = free.iter().map(|p| p.size).sum();174
+ if pooled + self.size <= POOL_BYTES {175
+ free.push(Pooled {176
+ buffer: self.buffer,177
+ allocation,178
+ size: self.size,179
+ usage: self.usage,180
+ location: self.location,181
+ returned: Instant::now(),182
+ });183
+ return;184
+ }185
+ drop(free);186
+ self.pool.destroy([(self.buffer, allocation)]);187
+ }188
+}