rendy_factory/
barriers.rs

1use {
2    crate::{
3        command::Encoder,
4        resource::{Handle, Image},
5    },
6    rendy_core::hal::{buffer, image, memory::Barrier, pso, Backend},
7    std::ops::Range,
8};
9
10/// A variant of `rendy_core::hal::image::Barrier` that uses Handle<Image<B>>
11#[derive(Debug)]
12struct ImageBarrier<B: Backend> {
13    /// The access flags controlling the image.
14    pub states: Range<image::State>,
15    /// The image the barrier controls.
16    pub target: Handle<Image<B>>,
17    /// A `SubresourceRange` that defines which section of an image the barrier applies to.
18    pub range: image::SubresourceRange,
19    // TODO: support queue transfers
20    // pub families: Option<Range<rendy_core::hal::queue::QueueFamilyId>>,
21}
22
23impl<B: Backend> ImageBarrier<B> {
24    fn raw(&self) -> Barrier<'_, B> {
25        Barrier::Image {
26            states: self.states.clone(),
27            target: self.target.raw(),
28            families: None,
29            range: self.range.clone(),
30        }
31    }
32}
33
34/// Handles combining multiple image and buffer barriers that must be
35/// made before and after some target operations.
36#[derive(Debug)]
37pub struct Barriers<B: Backend> {
38    before_stages: pso::PipelineStage,
39    before_buffer_access: buffer::Access,
40    before_image_access: image::Access,
41    before_image_transitions: Vec<ImageBarrier<B>>,
42    target_stages: pso::PipelineStage,
43    target_buffer_access: buffer::Access,
44    target_image_access: image::Access,
45    after_stages: pso::PipelineStage,
46    after_buffer_access: buffer::Access,
47    after_image_access: image::Access,
48    after_image_transitions: Vec<ImageBarrier<B>>,
49}
50
51impl<B: Backend> Barriers<B> {
52    /// Create a new Barriers instance with target stages and accesses
53    pub fn new(
54        target_stages: pso::PipelineStage,
55        target_buffer_access: buffer::Access,
56        target_image_access: image::Access,
57    ) -> Self {
58        Self {
59            before_stages: pso::PipelineStage::empty(),
60            before_buffer_access: buffer::Access::empty(),
61            before_image_access: image::Access::empty(),
62            before_image_transitions: Vec::new(),
63            target_stages,
64            target_buffer_access,
65            target_image_access,
66            after_stages: pso::PipelineStage::empty(),
67            after_buffer_access: buffer::Access::empty(),
68            after_image_access: image::Access::empty(),
69            after_image_transitions: Vec::new(),
70        }
71    }
72
73    /// Add an image to the barriers
74    pub fn add_image(
75        &mut self,
76        image: Handle<Image<B>>,
77        image_range: rendy_core::hal::image::SubresourceRange,
78        last_stage: pso::PipelineStage,
79        last_access: rendy_core::hal::image::Access,
80        last_layout: rendy_core::hal::image::Layout,
81        target_layout: image::Layout,
82        next_stage: pso::PipelineStage,
83        next_access: rendy_core::hal::image::Access,
84        next_layout: rendy_core::hal::image::Layout,
85    ) {
86        self.before_stages |= last_stage;
87        self.before_image_access |= last_access;
88        self.after_stages |= next_stage;
89        self.after_image_access |= next_access;
90
91        if last_layout != target_layout {
92            log::trace!(
93                "Transition last: {:?}",
94                (last_access, last_layout)..(self.target_image_access, target_layout)
95            );
96            self.before_image_transitions.push(ImageBarrier {
97                states: (last_access, last_layout)..(self.target_image_access, target_layout),
98                target: image.clone(),
99                range: image_range.clone(),
100            });
101        }
102
103        if next_layout != target_layout {
104            log::trace!(
105                "Transition next: {:?}",
106                (self.target_image_access, target_layout)..(next_access, next_layout)
107            );
108            self.after_image_transitions.push(ImageBarrier {
109                states: (self.target_image_access, target_layout)..(next_access, next_layout),
110                target: image,
111                range: image_range,
112            })
113        }
114    }
115
116    /// Add a buffer to the barriers
117    pub fn add_buffer(
118        &mut self,
119        last_stage: pso::PipelineStage,
120        last_access: rendy_core::hal::buffer::Access,
121        next_stage: pso::PipelineStage,
122        next_access: rendy_core::hal::buffer::Access,
123    ) {
124        self.before_stages |= last_stage;
125        self.before_buffer_access |= last_access;
126        self.after_stages |= next_stage;
127        self.after_buffer_access |= next_access;
128    }
129
130    /// Encode the barriers that should come before the target operations
131    pub fn encode_before<C, L>(&mut self, encoder: &mut Encoder<'_, B, C, L>) {
132        if !self.before_stages.is_empty() {
133            let transitions = self.before_image_transitions.iter().map(|b| b.raw());
134            let all_images = Some(Barrier::AllImages(
135                self.before_image_access..self.target_image_access,
136            ))
137            .filter(|_| !self.before_image_access.is_empty());
138            let all_buffers = Some(Barrier::AllBuffers(
139                self.before_buffer_access..self.target_buffer_access,
140            ))
141            .filter(|_| !self.before_buffer_access.is_empty());
142
143            unsafe {
144                encoder.pipeline_barrier(
145                    self.before_stages..self.target_stages,
146                    rendy_core::hal::memory::Dependencies::empty(),
147                    transitions.chain(all_images).chain(all_buffers),
148                );
149            }
150        } else {
151            assert_eq!(self.before_image_transitions.len(), 0);
152        }
153
154        self.before_stages = pso::PipelineStage::empty();
155        self.before_image_access = image::Access::empty();
156        self.before_buffer_access = buffer::Access::empty();
157        self.before_image_transitions.clear();
158    }
159
160    /// Encode the barriers that should come after the target operations
161    pub fn encode_after<C, L>(&mut self, encoder: &mut Encoder<'_, B, C, L>) {
162        if !self.target_stages.is_empty() {
163            let transitions = self.after_image_transitions.iter().map(|b| b.raw());
164            let all_images = Some(Barrier::AllImages(
165                self.target_image_access..self.after_image_access,
166            ))
167            .filter(|_| !self.after_image_access.is_empty());
168            let all_buffers = Some(Barrier::AllBuffers(
169                self.target_buffer_access..self.after_buffer_access,
170            ))
171            .filter(|_| !self.after_buffer_access.is_empty());
172
173            unsafe {
174                encoder.pipeline_barrier(
175                    self.target_stages..self.after_stages,
176                    rendy_core::hal::memory::Dependencies::empty(),
177                    transitions.chain(all_images).chain(all_buffers),
178                );
179            }
180        } else {
181            assert_eq!(self.after_image_transitions.len(), 0);
182        }
183
184        self.after_stages = pso::PipelineStage::empty();
185        self.after_image_access = image::Access::empty();
186        self.after_buffer_access = buffer::Access::empty();
187        self.after_image_transitions.clear();
188    }
189}