atagen

monolith

public
Star0
Watch0

Commit d2f26399309b

tarball

atagen <boss@atagen.co>2026-10-19 07:56 UTC

2 files changed+147-112
render-vk: use per-transition barrier scopes and batch them per phase

Every transition was ALL_COMMANDS to ALL_COMMANDS with MEMORY_WRITE to READ and WRITE, one vkCmdPipelineBarrier2 per image, so an upload waited on all earlier queue work. Barriers now derive stage and access from the layouts on each side and record all images of a phase in one call. Dmabuf images in the general layout keep the coarse scope.
Modified file: crates/render-vk/src/barrier.rs+126 additions−81 deletions
@@ -24,97 +24,142 @@ impl LayoutLog {
2424 }
2525 }
2626
27-/// A full barrier between all earlier and later work on the image, optionally
28-/// transferring ownership between queue families. Coarse, but correct for
29-/// every transition the renderer makes.
30-pub(crate) unsafe fn barrier(
31- d: &ash::Device,
32- cmd: vk::CommandBuffer,
33- image: vk::Image,
34- old: vk::ImageLayout,
35- new: vk::ImageLayout,
36- src_family: u32,
37- dst_family: u32,
38-) {
39- let b = [vk::ImageMemoryBarrier2::default()
40- .src_stage_mask(vk::PipelineStageFlags2::ALL_COMMANDS)
41- .src_access_mask(vk::AccessFlags2::MEMORY_WRITE)
42- .dst_stage_mask(vk::PipelineStageFlags2::ALL_COMMANDS)
43- .dst_access_mask(vk::AccessFlags2::MEMORY_READ | vk::AccessFlags2::MEMORY_WRITE)
44- .old_layout(old)
45- .new_layout(new)
46- .src_queue_family_index(src_family)
47- .dst_queue_family_index(dst_family)
48- .image(image)
49- .subresource_range(color_range())];
50- unsafe {
51- d.cmd_pipeline_barrier2(
52- cmd,
53- &vk::DependencyInfo::default().image_memory_barriers(&b),
54- )
27+struct Scope {
28+ stage: vk::PipelineStageFlags2,
29+ access: vk::AccessFlags2,
30+}
31+
32+/// What an image may still be doing when it leaves `layout`. Layouts the
33+/// renderer does not use itself, the general layout of dmabufs among them,
34+/// wait for everything.
35+fn leaving(layout: vk::ImageLayout) -> Scope {
36+ let (stage, access) = match layout {
37+ vk::ImageLayout::UNDEFINED => (vk::PipelineStageFlags2::NONE, vk::AccessFlags2::NONE),
38+ vk::ImageLayout::TRANSFER_DST_OPTIMAL => (
39+ vk::PipelineStageFlags2::TRANSFER,
40+ vk::AccessFlags2::TRANSFER_WRITE,
41+ ),
42+ vk::ImageLayout::TRANSFER_SRC_OPTIMAL => {
43+ (vk::PipelineStageFlags2::TRANSFER, vk::AccessFlags2::NONE)
44+ }
45+ vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL => (
46+ vk::PipelineStageFlags2::FRAGMENT_SHADER,
47+ vk::AccessFlags2::NONE,
48+ ),
49+ vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL => (
50+ vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT,
51+ vk::AccessFlags2::COLOR_ATTACHMENT_WRITE,
52+ ),
53+ _ => (
54+ vk::PipelineStageFlags2::ALL_COMMANDS,
55+ vk::AccessFlags2::MEMORY_WRITE,
56+ ),
5557 };
58+ Scope { stage, access }
5659 }
5760
58-/// Moves an image into `layout` for use on this queue. Dmabuf images come
59-/// from the foreign queue family in the general layout, others continue from
60-/// their tracked layout.
61-pub(crate) unsafe fn acquire(
62- d: &ash::Device,
63- cmd: vk::CommandBuffer,
64- family: u32,
65- image: &Arc<ImageInner>,
66- layout: vk::ImageLayout,
67- log: &mut LayoutLog,
68-) {
69- if image.external {
70- unsafe {
71- barrier(
72- d,
73- cmd,
61+/// What an image does once it is in `layout`.
62+fn entering(layout: vk::ImageLayout) -> Scope {
63+ let (stage, access) = match layout {
64+ vk::ImageLayout::TRANSFER_DST_OPTIMAL => (
65+ vk::PipelineStageFlags2::TRANSFER,
66+ vk::AccessFlags2::TRANSFER_WRITE,
67+ ),
68+ vk::ImageLayout::TRANSFER_SRC_OPTIMAL => (
69+ vk::PipelineStageFlags2::TRANSFER,
70+ vk::AccessFlags2::TRANSFER_READ,
71+ ),
72+ vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL => (
73+ vk::PipelineStageFlags2::FRAGMENT_SHADER,
74+ vk::AccessFlags2::SHADER_SAMPLED_READ,
75+ ),
76+ vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL => (
77+ vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT,
78+ vk::AccessFlags2::COLOR_ATTACHMENT_READ | vk::AccessFlags2::COLOR_ATTACHMENT_WRITE,
79+ ),
80+ _ => (
81+ vk::PipelineStageFlags2::ALL_COMMANDS,
82+ vk::AccessFlags2::MEMORY_READ | vk::AccessFlags2::MEMORY_WRITE,
83+ ),
84+ };
85+ Scope { stage, access }
86+}
87+
88+/// The image barriers of one phase of a command buffer, recorded together.
89+#[derive(Default)]
90+pub(crate) struct Barriers(Vec<vk::ImageMemoryBarrier2<'static>>);
91+
92+impl Barriers {
93+ fn push(
94+ &mut self,
95+ image: vk::Image,
96+ (old, src_family): (vk::ImageLayout, u32),
97+ (new, dst_family): (vk::ImageLayout, u32),
98+ ) {
99+ let (from, to) = (leaving(old), entering(new));
100+ self.0.push(
101+ vk::ImageMemoryBarrier2::default()
102+ .src_stage_mask(from.stage)
103+ .src_access_mask(from.access)
104+ .dst_stage_mask(to.stage)
105+ .dst_access_mask(to.access)
106+ .old_layout(old)
107+ .new_layout(new)
108+ .src_queue_family_index(src_family)
109+ .dst_queue_family_index(dst_family)
110+ .image(image)
111+ .subresource_range(color_range()),
112+ );
113+ }
114+
115+ /// Moves an image into `layout` for use on this queue. Dmabuf images come
116+ /// from the foreign queue family in the general layout, others continue
117+ /// from their tracked layout.
118+ pub fn acquire(
119+ &mut self,
120+ family: u32,
121+ image: &Arc<ImageInner>,
122+ layout: vk::ImageLayout,
123+ log: &mut LayoutLog,
124+ ) {
125+ if image.external {
126+ self.push(
74127 image.image,
75- vk::ImageLayout::GENERAL,
76- layout,
77- vk::QUEUE_FAMILY_FOREIGN_EXT,
78- family,
79- )
80- };
81- } else {
82- let old = log.current(image);
83- if old != layout {
84- unsafe {
85- barrier(
86- d,
87- cmd,
128+ (vk::ImageLayout::GENERAL, vk::QUEUE_FAMILY_FOREIGN_EXT),
129+ (layout, family),
130+ );
131+ } else {
132+ let old = log.current(image);
133+ if old != layout {
134+ self.push(
88135 image.image,
89- old,
90- layout,
91- vk::QUEUE_FAMILY_IGNORED,
92- vk::QUEUE_FAMILY_IGNORED,
93- )
94- };
95- log.0.push((image.clone(), layout));
136+ (old, vk::QUEUE_FAMILY_IGNORED),
137+ (layout, vk::QUEUE_FAMILY_IGNORED),
138+ );
139+ log.0.push((image.clone(), layout));
140+ }
141+ }
142+ }
143+
144+ /// Hands a dmabuf image back to the foreign queue family after use.
145+ pub fn release(&mut self, family: u32, image: &ImageInner, layout: vk::ImageLayout) {
146+ if image.external {
147+ self.push(
148+ image.image,
149+ (layout, family),
150+ (vk::ImageLayout::GENERAL, vk::QUEUE_FAMILY_FOREIGN_EXT),
151+ );
96152 }
97153 }
98-}
99154
100-/// Hands a dmabuf image back to the foreign queue family after use.
101-pub(crate) unsafe fn release(
102- d: &ash::Device,
103- cmd: vk::CommandBuffer,
104- family: u32,
105- image: &ImageInner,
106- layout: vk::ImageLayout,
107-) {
108- if image.external {
155+ pub unsafe fn record(self, d: &ash::Device, cmd: vk::CommandBuffer) {
156+ if self.0.is_empty() {
157+ return;
158+ }
109159 unsafe {
110- barrier(
111- d,
160+ d.cmd_pipeline_barrier2(
112161 cmd,
113- image.image,
114- layout,
115- vk::ImageLayout::GENERAL,
116- family,
117- vk::QUEUE_FAMILY_FOREIGN_EXT,
162+ &vk::DependencyInfo::default().image_memory_barriers(&self.0),
118163 )
119164 };
120165 }
Modified file: crates/render-vk/src/renderer/mod.rs+21 additions−31 deletions
@@ -5,7 +5,7 @@ use shm::stage_rows;
55 use stall::{STALL, StallPhase};
66
77 use crate::Error;
8-use crate::barrier::{LayoutLog, acquire, release};
8+use crate::barrier::{Barriers, LayoutLog};
99 use crate::device::Shared;
1010 use crate::dmabuf::{self, DmabufCaps, RENDER_USAGE, SAMPLE_USAGE, VulkanAllocator};
1111 use crate::format::{BYTES_PER_PIXEL, SUPPORTED};
@@ -352,16 +352,16 @@ impl VulkanRenderer {
352352 let d = &self.shared.device.device;
353353 let family = self.shared.device.queue_family;
354354 unsafe {
355+ let mut barriers = Barriers::default();
355356 for u in &uploads {
356- acquire(
357- d,
358- cmd,
357+ barriers.acquire(
359358 family,
360359 &u.image,
361360 vk::ImageLayout::TRANSFER_DST_OPTIMAL,
362361 &mut layouts,
363362 );
364363 }
364+ barriers.record(d, cmd);
365365 for (i, u) in uploads.iter().enumerate() {
366366 if uploads[..i].iter().any(|e| Arc::ptr_eq(&e.image, &u.image)) {
367367 let order = [vk::MemoryBarrier2::default()
@@ -382,16 +382,16 @@ impl VulkanRenderer {
382382 &[u.copy],
383383 );
384384 }
385+ let mut barriers = Barriers::default();
385386 for u in &uploads {
386- acquire(
387- d,
388- cmd,
387+ barriers.acquire(
389388 family,
390389 &u.image,
391390 vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
392391 &mut layouts,
393392 );
394393 }
394+ barriers.record(d, cmd);
395395 }
396396 Recorded { uploads, layouts }
397397 }
@@ -439,24 +439,22 @@ impl VulkanRenderer {
439439 let (w, h) = (image.width, image.height);
440440 let sampler = self.sampler();
441441 unsafe {
442+ let mut barriers = Barriers::default();
442443 for t in &sampled {
443- acquire(
444- d,
445- cmd,
444+ barriers.acquire(
446445 family,
447446 t,
448447 vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
449448 &mut recorded.layouts,
450449 );
451450 }
452- acquire(
453- d,
454- cmd,
451+ barriers.acquire(
455452 family,
456453 &image,
457454 vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
458455 &mut recorded.layouts,
459456 );
457+ barriers.record(d, cmd);
460458 let attachments = [vk::RenderingAttachmentInfo::default()
461459 .image_view(image.view)
462460 .image_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
@@ -552,16 +550,12 @@ impl VulkanRenderer {
552550 }
553551 }
554552 d.cmd_end_rendering(cmd);
555- release(
556- d,
557- cmd,
558- family,
559- &image,
560- vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
561- );
553+ let mut barriers = Barriers::default();
554+ barriers.release(family, &image, vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL);
562555 for t in &sampled {
563- release(d, cmd, family, t, vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL);
556+ barriers.release(family, t, vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL);
564557 }
558+ barriers.record(d, cmd);
565559 }
566560 sampled.push(image);
567561 let fence = self.finish(cmd, recorded, sampled)?;
@@ -693,14 +687,14 @@ impl VulkanRenderer {
693687 let buffer = staging.buffer;
694688 let source = image.clone();
695689 self.one_shot(|d, cmd, family, layouts| unsafe {
696- acquire(
697- d,
698- cmd,
690+ let mut barriers = Barriers::default();
691+ barriers.acquire(
699692 family,
700693 &source,
701694 vk::ImageLayout::TRANSFER_SRC_OPTIMAL,
702695 layouts,
703696 );
697+ barriers.record(d, cmd);
704698 d.cmd_copy_image_to_buffer(
705699 cmd,
706700 source.image,
@@ -708,13 +702,9 @@ impl VulkanRenderer {
708702 buffer,
709703 &[copy],
710704 );
711- release(
712- d,
713- cmd,
714- family,
715- &source,
716- vk::ImageLayout::TRANSFER_SRC_OPTIMAL,
717- );
705+ let mut barriers = Barriers::default();
706+ barriers.release(family, &source, vk::ImageLayout::TRANSFER_SRC_OPTIMAL);
707+ barriers.record(d, cmd);
718708 })?;
719709 }
720710 let mut data = staging.bytes()?[..w as usize * h as usize * BYTES_PER_PIXEL].to_vec();