Commit 61ebcd0a3619
tarball3 files changed+147-42
@@ -39,11 +39,24 @@ struct Pending {
3939
fence: Arc<FenceInner>,4040
cmd: vk::CommandBuffer,4141
_keep: Vec<Arc<ImageInner>>,42
- _staging: Option<Staging>,42
+ _staging: Vec<Staging>,4343
}4444
45
+/// A queued copy of staged pixels into `image`, recorded by the next submit.46
+struct Upload {47
+ image: Arc<ImageInner>,48
+ staging: Staging,49
+ copy: vk::BufferImageCopy,50
+}51
+52
+/// Queued staging memory past which uploads are submitted without waiting53
+/// for a frame.54
+const UPLOAD_BATCH_BYTES: usize = 64 * 1024 * 1024;55
+4556
pub struct VulkanRenderer {4657
pending: Vec<Pending>,58
+ uploads: Vec<Upload>,59
+ upload_bytes: usize,4760
dmabuf_textures: HashMap<WeakDmabuf, VulkanTexture>,4861
dmabuf_targets: HashMap<WeakDmabuf, Arc<ImageInner>>,4962
pipelines: Pipelines,@@ -118,6 +131,8 @@ impl VulkanRenderer {
118131
}119132
Ok(Self {120133
pending: Vec::new(),134
+ uploads: Vec::new(),135
+ upload_bytes: 0,121136
dmabuf_textures: HashMap::new(),122137
dmabuf_targets: HashMap::new(),123138
pipelines: Pipelines::new(&shared)?,@@ -251,21 +266,97 @@ impl VulkanRenderer {
251266
Ok(fence)252267
}253268
254
- /// Records, submits and waits for a short command buffer.269
+ /// Records, submits and waits for a short command buffer. Queued uploads270
+ /// go first, so `record` sees their pixels.255271
fn one_shot(256272
&mut self,257273
record: impl FnOnce(&ash::Device, vk::CommandBuffer, u32),258274
) -> Result<(), Error> {259275
let cmd = self.begin()?;276
+ let uploads = self.record_uploads(cmd);260277
record(261278
&self.shared.device.device,262279
cmd,263280
self.shared.device.queue_family,264281
);282
+ let fence = self.finish(cmd, uploads, Vec::new())?;283
+ Ok(fence.wait()?)284
+ }285
+286
+ /// Submits `cmd`, which holds `uploads` and works on `keep`, and tracks287
+ /// it until its fence signals.288
+ fn finish(289
+ &mut self,290
+ cmd: vk::CommandBuffer,291
+ uploads: Vec<Upload>,292
+ mut keep: Vec<Arc<ImageInner>>,293
+ ) -> Result<Arc<FenceInner>, Error> {265294
let fence = self.end_and_submit(cmd)?;266
- let res = fence.wait();267
- self.spare_cmds.push(cmd);268
- Ok(res?)295
+ keep.extend(uploads.iter().map(|u| u.image.clone()));296
+ self.pending.push(Pending {297
+ fence: fence.clone(),298
+ cmd,299
+ _keep: keep,300
+ _staging: uploads.into_iter().map(|u| u.staging).collect(),301
+ });302
+ Ok(fence)303
+ }304
+305
+ /// Records every queued upload into `cmd` and hands them back to keep306
+ /// alive until it finishes.307
+ fn record_uploads(&mut self, cmd: vk::CommandBuffer) -> Vec<Upload> {308
+ let uploads = std::mem::take(&mut self.uploads);309
+ self.upload_bytes = 0;310
+ if uploads.is_empty() {311
+ return uploads;312
+ }313
+ let d = &self.shared.device.device;314
+ let family = self.shared.device.queue_family;315
+ unsafe {316
+ for u in &uploads {317
+ acquire(d, cmd, family, &u.image, vk::ImageLayout::TRANSFER_DST_OPTIMAL);318
+ }319
+ for (i, u) in uploads.iter().enumerate() {320
+ if uploads[..i].iter().any(|e| Arc::ptr_eq(&e.image, &u.image)) {321
+ let order = [vk::MemoryBarrier2::default()322
+ .src_stage_mask(vk::PipelineStageFlags2::TRANSFER)323
+ .src_access_mask(vk::AccessFlags2::TRANSFER_WRITE)324
+ .dst_stage_mask(vk::PipelineStageFlags2::TRANSFER)325
+ .dst_access_mask(vk::AccessFlags2::TRANSFER_WRITE)];326
+ d.cmd_pipeline_barrier2(327
+ cmd,328
+ &vk::DependencyInfo::default().memory_barriers(&order),329
+ );330
+ }331
+ d.cmd_copy_buffer_to_image(332
+ cmd,333
+ u.staging.buffer,334
+ u.image.image,335
+ vk::ImageLayout::TRANSFER_DST_OPTIMAL,336
+ &[u.copy],337
+ );338
+ }339
+ for u in &uploads {340
+ acquire(341
+ d,342
+ cmd,343
+ family,344
+ &u.image,345
+ vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,346
+ );347
+ }348
+ }349
+ uploads350
+ }351
+352
+ /// Submits the queued uploads without waiting for a frame to carry them.353
+ fn flush_uploads(&mut self) -> Result<(), Error> {354
+ if self.uploads.is_empty() {355
+ return Ok(());356
+ }357
+ let cmd = self.begin()?;358
+ let uploads = self.record_uploads(cmd);359
+ self.finish(cmd, uploads, Vec::new()).map(drop)269360
}270361
271362
#[profiling::function]@@ -294,6 +385,7 @@ impl VulkanRenderer {
294385
});295386
}296387
let cmd = self.begin()?;388
+ let uploads = self.record_uploads(cmd);297389
let d = &self.shared.device.device;298390
let family = self.shared.device.queue_family;299391
let push = &self.shared.device.push_descriptor;@@ -416,14 +508,8 @@ impl VulkanRenderer {
416508
release(d, cmd, family, t, vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL);417509
}418510
}419
- let fence = self.end_and_submit(cmd)?;420511
sampled.push(image);421
- self.pending.push(Pending {422
- fence: fence.clone(),423
- cmd,424
- _keep: sampled,425
- _staging: None,426
- });512
+ let fence = self.finish(cmd, uploads, sampled)?;427513
Ok(SyncPoint::from(VkFence(fence)))428514
}429515
@@ -454,8 +540,8 @@ impl VulkanRenderer {
454540
}455541
456542
/// Uploads `region` of `image`, taking each image row `y` in the region457
- /// from `row(y)`. The CPU does not wait: the copy is ordered before later458
- /// frames by its barrier, and its staging memory lives until its fence.543
+ /// from `row(y)`. The pixels are staged now and copied by the next submit,544
+ /// which the CPU does not wait for.459545
#[profiling::function]460546
fn upload_with<'a>(461547
&mut self,@@ -474,7 +560,7 @@ impl VulkanRenderer {
474560
self.copy_staged(image, region, staging)475561
}476562
477
- /// Copies staged pixels into `region` of `image` on the GPU.563
+ /// Queues staged pixels to be copied into `region` of `image`.478564
fn copy_staged(479565
&mut self,480566
image: &Arc<ImageInner>,@@ -499,33 +585,15 @@ impl VulkanRenderer {
499585
height: h as u32,500586
depth: 1,501587
});502
- let cmd = self.begin()?;503
- let d = &self.shared.device.device;504
- let family = self.shared.device.queue_family;505
- unsafe {506
- acquire(d, cmd, family, image, vk::ImageLayout::TRANSFER_DST_OPTIMAL);507
- d.cmd_copy_buffer_to_image(508
- cmd,509
- staging.buffer,510
- image.image,511
- vk::ImageLayout::TRANSFER_DST_OPTIMAL,512
- &[copy],513
- );514
- acquire(515
- d,516
- cmd,517
- family,518
- image,519
- vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,520
- );521
- }522
- let fence = self.end_and_submit(cmd)?;523
- self.pending.push(Pending {524
- fence,525
- cmd,526
- _keep: vec![image.clone()],527
- _staging: Some(staging),588
+ self.uploads.push(Upload {589
+ image: image.clone(),590
+ staging,591
+ copy,528592
});593
+ self.upload_bytes += w * h * BYTES_PER_PIXEL;594
+ if self.upload_bytes > UPLOAD_BATCH_BYTES {595
+ self.flush_uploads()?;596
+ }529597
Ok(())530598
}531599
@@ -64,7 +64,7 @@ impl VulkanRenderer {
6464
fence,6565
cmd,6666
_keep: Vec::new(),67
- _staging: None,67
+ _staging: Vec::new(),6868
});6969
}7070
Ok(())@@ -238,3 +238,40 @@ fn quarter_turn_on_a_wide_output_fills_it() {
238238
assert!(g.0 != r.0 && g.1 != r.1, "{transform:?} not diagonal");239239
}240240
}241
+242
+#[test]243
+fn queued_uploads_to_one_texture_land_in_order() {244
+ let mut s = Scene::new();245
+ let size = (2, 2);246
+ let bytes = |p: Pixel| -> Vec<u8> { [p; 4].iter().flatten().copied().collect() };247
+ let tex = s.texture(size, &[RED; 4]);248
+ for colour in [WHITE, GREEN] {249
+ s.renderer250
+ .update_memory(&tex, &bytes(colour), Rectangle::from_size(size.into()))251
+ .unwrap();252
+ }253
+ let read = s254
+ .renderer255
+ .copy_texture(&tex, Rectangle::from_size(size.into()), Fourcc::Abgr8888)256
+ .unwrap();257
+ assert_eq!(s.renderer.map_texture(&read).unwrap()[..4], GREEN);258
+ s.renderer259
+ .update_memory(&tex, &bytes(WHITE), Rectangle::from_size(size.into()))260
+ .unwrap();261
+ s.renderer262
+ .update_memory(&tex, &bytes(RED), Rectangle::from_size(size.into()))263
+ .unwrap();264
+ s.draw(Transform::Normal, |f| {265
+ f.render_texture_from_to(266
+ &tex,267
+ Rectangle::from_size((2.0, 2.0).into()),268
+ full(),269
+ &[full()],270
+ &[],271
+ Transform::Normal,272
+ 1.0,273
+ )274
+ .unwrap();275
+ });276
+ assert_eq!(at(&s.pixels(), 32, 32), RED);277
+}