rendy_factory/
blitter.rs

1use {
2    crate::{
3        barriers::Barriers,
4        command::{
5            CommandBuffer, CommandPool, Encoder, Families, Family, Graphics, IndividualReset,
6            InitialState, Level, OneShot, PendingOnceState, PrimaryLevel, QueueId, RecordingState,
7            Submission, Supports,
8        },
9        core::Device,
10        resource::{Handle, Image},
11        upload::ImageState,
12    },
13    rendy_core::hal::device::{Device as _, OutOfMemory},
14    smallvec::SmallVec,
15    std::{collections::VecDeque, iter::once, ops::DerefMut, ops::Range},
16};
17
18/// Manages blitting images across families and queues.
19#[derive(Debug)]
20pub struct Blitter<B: rendy_core::hal::Backend> {
21    family_ops: Vec<Option<parking_lot::Mutex<FamilyGraphicsOps<B>>>>,
22}
23
24fn subresource_to_range(
25    sub: &rendy_core::hal::image::SubresourceLayers,
26) -> rendy_core::hal::image::SubresourceRange {
27    rendy_core::hal::image::SubresourceRange {
28        aspects: sub.aspects,
29        levels: sub.level..sub.level + 1,
30        layers: sub.layers.clone(),
31    }
32}
33
34/// A region to be blitted including the source and destination images and states,
35#[derive(Debug, Clone)]
36pub struct BlitRegion {
37    /// Region to blit from
38    pub src: BlitImageState,
39    /// Region to blit to
40    pub dst: BlitImageState,
41}
42
43impl BlitRegion {
44    /// Get the blit regions needed to fill the mip levels of an image
45    ///
46    /// # Safety
47    ///
48    /// `last` state must be valid for corresponding image layer at the time of command execution (after memory transfers).
49    /// `last` and `next` should contain at least `image.levels()` elements.
50    /// `image.levels()` must be greater than 1
51    pub fn mip_blits_for_image<B: rendy_core::hal::Backend>(
52        image: &Handle<Image<B>>,
53        last: impl IntoIterator<Item = ImageState>,
54        next: impl IntoIterator<Item = ImageState>,
55    ) -> (QueueId, Vec<BlitRegion>) {
56        assert!(image.levels() > 1);
57
58        let aspects = image.format().surface_desc().aspects;
59
60        let transfer = rendy_core::hal::pso::PipelineStage::TRANSFER;
61        let src_optimal = rendy_core::hal::image::Layout::TransferSrcOptimal;
62        let read = rendy_core::hal::image::Access::TRANSFER_READ;
63        let write = rendy_core::hal::image::Access::TRANSFER_WRITE;
64
65        let mut last_iter = last.into_iter();
66        let mut next_iter = next.into_iter();
67
68        let mut src_last = last_iter.next().unwrap();
69        let mut src_next = next_iter.next().unwrap();
70        assert_eq!(src_last.queue, src_next.queue);
71
72        let queue = src_last.queue;
73
74        let mut blits = Vec::with_capacity(image.levels() as usize - 1);
75
76        for (level, (dst_last, dst_next)) in (1..image.levels())
77            .into_iter()
78            .zip(last_iter.zip(next_iter))
79        {
80            assert_eq!(dst_last.queue, dst_next.queue);
81
82            let begin = level == 1;
83            let end = level == image.levels() - 1;
84
85            blits.push(BlitRegion {
86                src: BlitImageState {
87                    subresource: rendy_core::hal::image::SubresourceLayers {
88                        aspects,
89                        level: level - 1,
90                        layers: 0..image.layers(),
91                    },
92                    bounds: rendy_core::hal::image::Offset::ZERO
93                        .into_bounds(&image.kind().level_extent(level - 1)),
94                    last_stage: if begin { src_last.stage } else { transfer },
95                    last_access: if begin { src_last.access } else { write },
96                    last_layout: if begin { src_last.layout } else { src_optimal },
97                    next_stage: src_next.stage,
98                    next_access: src_next.access,
99                    next_layout: src_next.layout,
100                },
101                dst: BlitImageState {
102                    subresource: rendy_core::hal::image::SubresourceLayers {
103                        aspects,
104                        level,
105                        layers: 0..image.layers(),
106                    },
107                    bounds: rendy_core::hal::image::Offset::ZERO
108                        .into_bounds(&image.kind().level_extent(level)),
109                    last_stage: dst_last.stage,
110                    last_access: rendy_core::hal::image::Access::empty(),
111                    last_layout: rendy_core::hal::image::Layout::Undefined,
112                    next_stage: if end { dst_next.stage } else { transfer },
113                    next_access: if end { dst_next.access } else { read },
114                    next_layout: if end { dst_next.layout } else { src_optimal },
115                },
116            });
117
118            src_last = dst_last;
119            src_next = dst_next;
120        }
121
122        (queue, blits)
123    }
124}
125
126impl From<BlitRegion> for rendy_core::hal::command::ImageBlit {
127    fn from(blit: BlitRegion) -> Self {
128        rendy_core::hal::command::ImageBlit {
129            src_subresource: blit.src.subresource,
130            src_bounds: blit.src.bounds,
131            dst_subresource: blit.dst.subresource,
132            dst_bounds: blit.dst.bounds,
133        }
134    }
135}
136
137/// A region and image states for one image in a blit.
138#[derive(Debug, Clone)]
139pub struct BlitImageState {
140    /// Subresource to use for blit
141    pub subresource: rendy_core::hal::image::SubresourceLayers,
142    /// Image offset range to use for blit
143    pub bounds: Range<rendy_core::hal::image::Offset>,
144    /// Last image stage before blit
145    pub last_stage: rendy_core::hal::pso::PipelineStage,
146    /// Last image access before blit
147    pub last_access: rendy_core::hal::image::Access,
148    /// Last image layout before blit
149    pub last_layout: rendy_core::hal::image::Layout,
150    /// Image stage after blit
151    pub next_stage: rendy_core::hal::pso::PipelineStage,
152    /// Image access after blit
153    pub next_access: rendy_core::hal::image::Access,
154    /// Image layout after blit
155    pub next_layout: rendy_core::hal::image::Layout,
156}
157
158impl<B> Blitter<B>
159where
160    B: rendy_core::hal::Backend,
161{
162    /// # Safety
163    ///
164    /// `families` must belong to the `device`
165    pub(crate) unsafe fn new(
166        device: &Device<B>,
167        families: &Families<B>,
168    ) -> Result<Self, OutOfMemory> {
169        let mut family_ops = Vec::new();
170        for family in families.as_slice() {
171            while family_ops.len() <= family.id().index {
172                family_ops.push(None);
173            }
174
175            family_ops[family.id().index] = Some(parking_lot::Mutex::new(FamilyGraphicsOps {
176                pool: family
177                    .create_pool(device)
178                    .map(|pool| pool.with_capability().unwrap())?,
179                initial: Vec::new(),
180                next: Vec::new(),
181                pending: VecDeque::new(),
182                read_barriers: Barriers::new(
183                    rendy_core::hal::pso::PipelineStage::TRANSFER,
184                    rendy_core::hal::buffer::Access::TRANSFER_READ,
185                    rendy_core::hal::image::Access::TRANSFER_READ,
186                ),
187                write_barriers: Barriers::new(
188                    rendy_core::hal::pso::PipelineStage::TRANSFER,
189                    rendy_core::hal::buffer::Access::TRANSFER_WRITE,
190                    rendy_core::hal::image::Access::TRANSFER_WRITE,
191                ),
192            }));
193        }
194
195        Ok(Blitter { family_ops })
196    }
197    /// Fill all mip levels from the first level of provided image.
198    ///
199    /// # Safety
200    ///
201    /// `device` must be the same that was used to create this `Blitter`.
202    /// `image` must belong to the `device`.
203    /// `last` state must be valid for corresponding image layer at the time of command execution (after memory transfers).
204    /// `last` and `next` should contain at least `image.levels()` elements.
205    /// `image.levels()` must be greater than 1
206    pub unsafe fn fill_mips(
207        &self,
208        device: &Device<B>,
209        image: Handle<Image<B>>,
210        filter: rendy_core::hal::image::Filter,
211        last: impl IntoIterator<Item = ImageState>,
212        next: impl IntoIterator<Item = ImageState>,
213    ) -> Result<(), OutOfMemory> {
214        let (queue, blits) = BlitRegion::mip_blits_for_image(&image, last, next);
215        for blit in blits {
216            log::trace!("Blit: {:#?}", blit);
217            self.blit_image(device, queue, &image, &image, filter, Some(blit))?;
218        }
219        Ok(())
220    }
221
222    /// Blit provided regions of `src_image` to `dst_image`.
223    ///
224    /// # Safety
225    ///
226    /// `device` must be the same that was used to create this `Blitter`.
227    /// `src` and `dst` must belong to the `device`.
228    /// regions' `last_*` states must be valid at the time of command execution (after memory transfers).
229    /// All regions must have distinct subresource layer and level combination.
230    ///
231    pub unsafe fn blit_image(
232        &self,
233        device: &Device<B>,
234        queue_id: QueueId,
235        src_image: &Handle<Image<B>>,
236        dst_image: &Handle<Image<B>>,
237        filter: rendy_core::hal::image::Filter,
238        regions: impl IntoIterator<Item = BlitRegion>,
239    ) -> Result<(), OutOfMemory> {
240        let mut family_ops = self.family_ops[queue_id.family.index]
241            .as_ref()
242            .unwrap()
243            .lock();
244
245        family_ops.next_ops(device, queue_id.index)?;
246
247        let FamilyGraphicsOps { next, .. } = family_ops.deref_mut();
248
249        let next_ops = next[queue_id.index].as_mut().unwrap();
250        let mut encoder = next_ops.command_buffer.encoder();
251
252        blit_image(&mut encoder, src_image, dst_image, filter, regions);
253        Ok(())
254    }
255
256    /// Cleanup pending updates.
257    ///
258    /// # Safety
259    ///
260    /// `device` must be the same that was used to create this `Blitter`.
261    ///
262    pub(crate) unsafe fn cleanup(&mut self, device: &Device<B>) {
263        for blitter in self.family_ops.iter_mut() {
264            if let Some(blitter) = blitter {
265                blitter.get_mut().cleanup(device);
266            }
267        }
268    }
269
270    /// Flush new updates.
271    ///
272    /// # Safety
273    ///
274    /// `families` must be the same that was used to create this `Blitter`.
275    ///
276    pub(crate) unsafe fn flush(&mut self, families: &mut Families<B>) {
277        for family in families.as_slice_mut() {
278            let blitter = self.family_ops[family.id().index]
279                .as_mut()
280                .expect("Blitter must be initialized for all families");
281            blitter.get_mut().flush(family);
282        }
283    }
284
285    /// # Safety
286    ///
287    /// `device` must be the same that was used to create this `Blitter`.
288    /// `device` must be idle.
289    ///
290    pub(crate) unsafe fn dispose(&mut self, device: &Device<B>) {
291        self.family_ops.drain(..).for_each(|fu| {
292            fu.map(|fu| fu.into_inner().dispose(device));
293        });
294    }
295}
296
297/// Blits one or more regions from src_image into dst_image using
298/// specified Filter
299///
300/// # Safety
301///
302/// * `src_image` and `dst_image` must have been created from the same `Device`
303/// as `encoder`
304pub unsafe fn blit_image<B, C, L>(
305    encoder: &mut Encoder<'_, B, C, L>,
306    src_image: &Handle<Image<B>>,
307    dst_image: &Handle<Image<B>>,
308    filter: rendy_core::hal::image::Filter,
309    regions: impl IntoIterator<Item = BlitRegion>,
310) where
311    B: rendy_core::hal::Backend,
312    C: Supports<Graphics>,
313    L: Level,
314{
315    let mut read_barriers = Barriers::new(
316        rendy_core::hal::pso::PipelineStage::TRANSFER,
317        rendy_core::hal::buffer::Access::TRANSFER_READ,
318        rendy_core::hal::image::Access::TRANSFER_READ,
319    );
320
321    let mut write_barriers = Barriers::new(
322        rendy_core::hal::pso::PipelineStage::TRANSFER,
323        rendy_core::hal::buffer::Access::TRANSFER_WRITE,
324        rendy_core::hal::image::Access::TRANSFER_WRITE,
325    );
326
327    let regions = regions
328        .into_iter()
329        .map(|reg| {
330            read_barriers.add_image(
331                src_image.clone(),
332                subresource_to_range(&reg.src.subresource),
333                reg.src.last_stage,
334                reg.src.last_access,
335                reg.src.last_layout,
336                rendy_core::hal::image::Layout::TransferSrcOptimal,
337                reg.src.next_stage,
338                reg.src.next_access,
339                reg.src.next_layout,
340            );
341
342            write_barriers.add_image(
343                dst_image.clone(),
344                subresource_to_range(&reg.dst.subresource),
345                reg.dst.last_stage,
346                reg.dst.last_access,
347                reg.dst.last_layout,
348                rendy_core::hal::image::Layout::TransferDstOptimal,
349                reg.dst.next_stage,
350                reg.dst.next_access,
351                reg.dst.next_layout,
352            );
353
354            reg.into()
355        })
356        .collect::<SmallVec<[_; 1]>>();
357
358    // TODO: synchronize whatever possible on flush.
359    // Currently all barriers are inlined due to dependencies between blits.
360
361    read_barriers.encode_before(encoder);
362    write_barriers.encode_before(encoder);
363
364    encoder.blit_image(
365        src_image.raw(),
366        rendy_core::hal::image::Layout::TransferSrcOptimal,
367        dst_image.raw(),
368        rendy_core::hal::image::Layout::TransferDstOptimal,
369        filter,
370        regions,
371    );
372
373    read_barriers.encode_after(encoder);
374    write_barriers.encode_after(encoder);
375}
376
377#[derive(Debug)]
378pub(crate) struct FamilyGraphicsOps<B: rendy_core::hal::Backend> {
379    pool: CommandPool<B, Graphics, IndividualReset>,
380    initial: Vec<GraphicsOps<B, InitialState>>,
381    next: Vec<Option<GraphicsOps<B, RecordingState<OneShot>>>>,
382    pending: VecDeque<GraphicsOps<B, PendingOnceState>>,
383    read_barriers: Barriers<B>,
384    write_barriers: Barriers<B>,
385}
386
387#[derive(Debug)]
388struct GraphicsOps<B: rendy_core::hal::Backend, S> {
389    command_buffer: CommandBuffer<B, Graphics, S, PrimaryLevel, IndividualReset>,
390    fence: B::Fence,
391}
392
393impl<B> FamilyGraphicsOps<B>
394where
395    B: rendy_core::hal::Backend,
396{
397    unsafe fn flush(&mut self, family: &mut Family<B>) {
398        for (queue, next) in self
399            .next
400            .drain(..)
401            .enumerate()
402            .filter_map(|(i, x)| x.map(|x| (i, x)))
403        {
404            log::trace!("Flush blitter");
405            let (submit, command_buffer) = next.command_buffer.finish().submit_once();
406
407            family.queue_mut(queue).submit_raw_fence(
408                Some(Submission::new().submits(once(submit))),
409                Some(&next.fence),
410            );
411
412            self.pending.push_back(GraphicsOps {
413                command_buffer,
414                fence: next.fence,
415            });
416        }
417    }
418
419    unsafe fn next_ops(
420        &mut self,
421        device: &Device<B>,
422        queue: usize,
423    ) -> Result<&mut GraphicsOps<B, RecordingState<OneShot>>, OutOfMemory> {
424        while self.next.len() <= queue {
425            self.next.push(None);
426        }
427
428        let pool = &mut self.pool;
429
430        match &mut self.next[queue] {
431            Some(next) => Ok(next),
432            slot @ None => {
433                let initial: Result<_, OutOfMemory> = self.initial.pop().map_or_else(
434                    || {
435                        Ok(GraphicsOps {
436                            command_buffer: pool.allocate_buffers(1).remove(0),
437                            fence: device.create_fence(false)?,
438                        })
439                    },
440                    Ok,
441                );
442                let initial = initial?;
443
444                *slot = Some(GraphicsOps {
445                    command_buffer: initial.command_buffer.begin(OneShot, ()),
446                    fence: initial.fence,
447                });
448
449                Ok(slot.as_mut().unwrap())
450            }
451        }
452    }
453
454    /// Cleanup pending updates.
455    ///
456    /// # Safety
457    ///
458    /// `device` must be the same that was used with other methods of this instance.
459    ///
460    unsafe fn cleanup(&mut self, device: &Device<B>) {
461        while let Some(pending) = self.pending.pop_front() {
462            match device.get_fence_status(&pending.fence) {
463                Ok(false) => {
464                    self.pending.push_front(pending);
465                    return;
466                }
467                Err(rendy_core::hal::device::DeviceLost) => {
468                    panic!("Device lost error is not handled yet");
469                }
470                Ok(true) => {
471                    device
472                        .reset_fence(&pending.fence)
473                        .expect("Can always reset signalled fence");
474                    self.initial.push(GraphicsOps {
475                        command_buffer: pending.command_buffer.mark_complete().reset(),
476                        fence: pending.fence,
477                    })
478                }
479            }
480        }
481    }
482
483    /// # Safety
484    ///
485    /// Device must be idle.
486    ///
487    unsafe fn dispose(mut self, device: &Device<B>) {
488        let pool = &mut self.pool;
489        self.pending.drain(..).for_each(|pending| {
490            device.destroy_fence(pending.fence);
491            pool.free_buffers(once(pending.command_buffer.mark_complete()));
492        });
493        self.initial.drain(..).for_each(|initial| {
494            device.destroy_fence(initial.fence);
495            pool.free_buffers(once(initial.command_buffer));
496        });
497        self.next.drain(..).filter_map(|n| n).for_each(|next| {
498            device.destroy_fence(next.fence);
499            pool.free_buffers(once(next.command_buffer));
500        });
501        drop(pool);
502        self.pool.dispose(device);
503    }
504}