rendy_factory/
upload.rs

1use {
2    crate::{
3        barriers::Barriers,
4        command::{
5            CommandBuffer, CommandPool, Families, Family, IndividualReset, InitialState, OneShot,
6            PendingOnceState, PrimaryLevel, QueueId, RecordingState, Submission, Transfer,
7        },
8        core::Device,
9        resource::{Buffer, Escape, Handle, Image},
10    },
11    rendy_core::hal::device::{Device as _, OutOfMemory},
12    std::{collections::VecDeque, iter::once},
13};
14
15/// State of the buffer on device.
16#[derive(Clone, Copy, Debug)]
17pub struct BufferState {
18    /// Queue that uses the buffer.
19    pub queue: QueueId,
20
21    /// Stages when buffer get used.
22    pub stage: rendy_core::hal::pso::PipelineStage,
23
24    /// Access performed by device.
25    pub access: rendy_core::hal::buffer::Access,
26}
27
28impl BufferState {
29    /// Create default buffet state.
30    pub fn new(queue: QueueId) -> Self {
31        BufferState {
32            queue,
33            stage: rendy_core::hal::pso::PipelineStage::TOP_OF_PIPE,
34            access: rendy_core::hal::buffer::Access::all(),
35        }
36    }
37
38    /// Set specific stage.
39    pub fn with_stage(mut self, stage: rendy_core::hal::pso::PipelineStage) -> Self {
40        self.stage = stage;
41        self
42    }
43
44    /// Set specific access.
45    pub fn with_access(mut self, access: rendy_core::hal::buffer::Access) -> Self {
46        self.access = access;
47        self
48    }
49}
50
51/// State of the image on device.
52#[derive(Clone, Copy, Debug)]
53pub struct ImageState {
54    /// Queue that uses the image.
55    pub queue: QueueId,
56
57    /// Stages when image get used.
58    pub stage: rendy_core::hal::pso::PipelineStage,
59
60    /// Access performed by device.
61    pub access: rendy_core::hal::image::Access,
62
63    /// Layout in which image is accessed.
64    pub layout: rendy_core::hal::image::Layout,
65}
66
67impl ImageState {
68    /// Create default buffet state.
69    pub fn new(queue: QueueId, layout: rendy_core::hal::image::Layout) -> Self {
70        ImageState {
71            queue,
72            stage: rendy_core::hal::pso::PipelineStage::TOP_OF_PIPE,
73            access: rendy_core::hal::image::Access::all(),
74            layout,
75        }
76    }
77
78    /// Set specific stage.
79    pub fn with_stage(mut self, stage: rendy_core::hal::pso::PipelineStage) -> Self {
80        self.stage = stage;
81        self
82    }
83
84    /// Set specific access.
85    pub fn with_access(mut self, access: rendy_core::hal::image::Access) -> Self {
86        self.access = access;
87        self
88    }
89}
90
91/// Either image state or just layout for image that is not used by device.
92#[derive(Clone, Copy, Debug)]
93pub enum ImageStateOrLayout {
94    /// State of image used by device.
95    State(ImageState),
96
97    /// Layout of image not used by device.
98    Layout(rendy_core::hal::image::Layout),
99}
100
101impl ImageStateOrLayout {
102    /// Create instance that descibes unused image with undefined content
103    /// (or if previous content doesn't need to be preserved).
104    /// This can be used for newly created images.
105    /// Or when whole image is updated.
106    pub fn undefined() -> Self {
107        ImageStateOrLayout::Layout(rendy_core::hal::image::Layout::Undefined)
108    }
109}
110
111impl From<ImageState> for ImageStateOrLayout {
112    fn from(state: ImageState) -> Self {
113        ImageStateOrLayout::State(state)
114    }
115}
116
117impl From<rendy_core::hal::image::Layout> for ImageStateOrLayout {
118    fn from(layout: rendy_core::hal::image::Layout) -> Self {
119        ImageStateOrLayout::Layout(layout)
120    }
121}
122
123#[derive(Debug)]
124pub(crate) struct Uploader<B: rendy_core::hal::Backend> {
125    family_uploads: Vec<Option<parking_lot::Mutex<FamilyUploads<B>>>>,
126}
127
128impl<B> Uploader<B>
129where
130    B: rendy_core::hal::Backend,
131{
132    /// # Safety
133    ///
134    /// `families` must belong to the `device`
135    pub(crate) unsafe fn new(
136        device: &Device<B>,
137        families: &Families<B>,
138    ) -> Result<Self, OutOfMemory> {
139        let mut family_uploads = Vec::new();
140        for family in families.as_slice() {
141            while family_uploads.len() <= family.id().index {
142                family_uploads.push(None);
143            }
144
145            family_uploads[family.id().index] = Some(parking_lot::Mutex::new(FamilyUploads {
146                fences: Vec::new(),
147                pool: family
148                    .create_pool(device)
149                    .map(|pool| pool.with_capability().unwrap())?,
150                next: Vec::new(),
151                pending: VecDeque::new(),
152                command_buffers: Vec::new(),
153                barriers: Barriers::new(
154                    rendy_core::hal::pso::PipelineStage::TRANSFER,
155                    rendy_core::hal::buffer::Access::TRANSFER_WRITE,
156                    rendy_core::hal::image::Access::TRANSFER_WRITE,
157                ),
158            }));
159        }
160
161        Ok(Uploader { family_uploads })
162    }
163
164    /// # Safety
165    ///
166    /// `device` must be the same that was used to create this `Uploader`.
167    /// `buffer` and `staging` must belong to the `device`.
168    ///
169    pub(crate) unsafe fn upload_buffer(
170        &self,
171        device: &Device<B>,
172        buffer: &Buffer<B>,
173        offset: u64,
174        staging: Escape<Buffer<B>>,
175        last: Option<BufferState>,
176        next: BufferState,
177    ) -> Result<(), OutOfMemory> {
178        let mut family_uploads = self.family_uploads[next.queue.family.index]
179            .as_ref()
180            .unwrap()
181            .lock();
182
183        if let Some(last) = last {
184            if last.queue != next.queue {
185                unimplemented!("Can't sync resources across queues");
186            }
187        }
188
189        family_uploads.barriers.add_buffer(
190            last.map_or(rendy_core::hal::pso::PipelineStage::empty(), |l| l.stage),
191            rendy_core::hal::buffer::Access::empty(),
192            next.stage,
193            next.access,
194        );
195
196        let next_upload = family_uploads.next_upload(device, next.queue.index)?;
197        let mut encoder = next_upload.command_buffer.encoder();
198        encoder.copy_buffer(
199            staging.raw(),
200            buffer.raw(),
201            Some(rendy_core::hal::command::BufferCopy {
202                src: 0,
203                dst: offset,
204                size: staging.size(),
205            }),
206        );
207
208        next_upload.staging_buffers.push(staging);
209
210        Ok(())
211    }
212
213    /// # Safety
214    ///
215    /// `image` must belong to the `device` that was used to create this Uploader.
216    ///
217    pub(crate) unsafe fn transition_image(
218        &self,
219        image: Handle<Image<B>>,
220        image_range: rendy_core::hal::image::SubresourceRange,
221        last: ImageStateOrLayout,
222        next: ImageState,
223    ) {
224        use rendy_core::hal::image::{Access, Layout};
225
226        let mut family_uploads = self.family_uploads[next.queue.family.index]
227            .as_ref()
228            .unwrap()
229            .lock();
230
231        let (last_stage, mut last_access, last_layout) = match last.into() {
232            ImageStateOrLayout::State(last) => {
233                if last.queue != next.queue {
234                    unimplemented!("Can't sync resources across queues");
235                }
236                (last.stage, last.access, last.layout)
237            }
238            ImageStateOrLayout::Layout(last_layout) => (
239                rendy_core::hal::pso::PipelineStage::TOP_OF_PIPE,
240                Access::empty(),
241                last_layout,
242            ),
243        };
244
245        if last_layout == Layout::Undefined || last_layout == next.layout {
246            last_access = Access::empty();
247        }
248
249        family_uploads.barriers.add_image(
250            image.clone(),
251            image_range,
252            last_stage,
253            last_access,
254            last_layout,
255            next.layout,
256            next.stage,
257            next.access,
258            next.layout,
259        );
260    }
261
262    /// # Safety
263    ///
264    /// `device` must be the same that was used to create this `Uploader`.
265    /// `image` and `staging` must belong to the `device`.
266    ///
267    pub(crate) unsafe fn upload_image(
268        &self,
269        device: &Device<B>,
270        image: Handle<Image<B>>,
271        data_width: u32,
272        data_height: u32,
273        image_layers: rendy_core::hal::image::SubresourceLayers,
274        image_offset: rendy_core::hal::image::Offset,
275        image_extent: rendy_core::hal::image::Extent,
276        staging: Escape<Buffer<B>>,
277        last: ImageStateOrLayout,
278        next: ImageState,
279    ) -> Result<(), OutOfMemory> {
280        use rendy_core::hal::image::{Access, Layout};
281
282        let mut family_uploads = self.family_uploads[next.queue.family.index]
283            .as_ref()
284            .unwrap()
285            .lock();
286
287        let whole_extent = if image_layers.level == 0 {
288            image.kind().extent()
289        } else {
290            image.kind().level_extent(image_layers.level)
291        };
292
293        let whole_level =
294            image_offset == rendy_core::hal::image::Offset::ZERO && image_extent == whole_extent;
295
296        let image_range = rendy_core::hal::image::SubresourceRange {
297            aspects: image_layers.aspects,
298            levels: image_layers.level..image_layers.level + 1,
299            layers: image_layers.layers.clone(),
300        };
301
302        let (last_stage, mut last_access, last_layout) = match last.into() {
303            ImageStateOrLayout::State(last) => {
304                if last.queue != next.queue {
305                    unimplemented!("Can't sync resources across queues");
306                }
307                (
308                    last.stage,
309                    last.access,
310                    if whole_level {
311                        Layout::Undefined
312                    } else {
313                        last.layout
314                    },
315                )
316            }
317            ImageStateOrLayout::Layout(last_layout) => (
318                rendy_core::hal::pso::PipelineStage::TOP_OF_PIPE,
319                Access::empty(),
320                if whole_level {
321                    Layout::Undefined
322                } else {
323                    last_layout
324                },
325            ),
326        };
327
328        let target_layout = match (last_layout, next.layout) {
329            (Layout::TransferDstOptimal, _) => Layout::TransferDstOptimal,
330            (_, Layout::General) => Layout::General,
331            (Layout::General, _) => Layout::General,
332            _ => Layout::TransferDstOptimal,
333        };
334
335        if last_layout == Layout::Undefined || last_layout == target_layout {
336            last_access = Access::empty();
337        }
338
339        family_uploads.barriers.add_image(
340            image.clone(),
341            image_range,
342            last_stage,
343            last_access,
344            last_layout,
345            target_layout,
346            next.stage,
347            next.access,
348            next.layout,
349        );
350
351        let next_upload = family_uploads.next_upload(device, next.queue.index)?;
352        let mut encoder = next_upload.command_buffer.encoder();
353        encoder.copy_buffer_to_image(
354            staging.raw(),
355            image.raw(),
356            target_layout,
357            Some(rendy_core::hal::command::BufferImageCopy {
358                buffer_offset: 0,
359                buffer_width: data_width,
360                buffer_height: data_height,
361                image_layers,
362                image_offset,
363                image_extent,
364            }),
365        );
366
367        next_upload.staging_buffers.push(staging);
368        Ok(())
369    }
370
371    /// Cleanup pending updates.
372    ///
373    /// # Safety
374    ///
375    /// `device` must be the same that was used to create this `Uploader`.
376    ///
377    pub(crate) unsafe fn cleanup(&mut self, device: &Device<B>) {
378        for uploader in self.family_uploads.iter_mut() {
379            if let Some(uploader) = uploader {
380                uploader.get_mut().cleanup(device);
381            }
382        }
383    }
384
385    /// Flush new updates.
386    ///
387    /// # Safety
388    ///
389    /// `families` must be the same that was used to create this `Uploader`.
390    ///
391    pub(crate) unsafe fn flush(&mut self, families: &mut Families<B>) {
392        for family in families.as_slice_mut() {
393            let uploader = self.family_uploads[family.id().index]
394                .as_mut()
395                .expect("Uploader must be initialized for all families");
396            uploader.get_mut().flush(family);
397        }
398    }
399
400    /// # Safety
401    ///
402    /// `device` must be the same that was used to create this `Uploader`.
403    /// `device` must be idle.
404    ///
405    pub(crate) unsafe fn dispose(&mut self, device: &Device<B>) {
406        self.family_uploads.drain(..).for_each(|fu| {
407            fu.map(|fu| fu.into_inner().dispose(device));
408        });
409    }
410}
411
412#[derive(Debug)]
413pub(crate) struct FamilyUploads<B: rendy_core::hal::Backend> {
414    pool: CommandPool<B, Transfer, IndividualReset>,
415    command_buffers:
416        Vec<[CommandBuffer<B, Transfer, InitialState, PrimaryLevel, IndividualReset>; 2]>,
417    next: Vec<Option<NextUploads<B>>>,
418    pending: VecDeque<PendingUploads<B>>,
419    fences: Vec<B::Fence>,
420    barriers: Barriers<B>,
421}
422
423#[derive(Debug)]
424pub(crate) struct PendingUploads<B: rendy_core::hal::Backend> {
425    barrier_buffer: CommandBuffer<B, Transfer, PendingOnceState, PrimaryLevel, IndividualReset>,
426    command_buffer: CommandBuffer<B, Transfer, PendingOnceState, PrimaryLevel, IndividualReset>,
427    staging_buffers: Vec<Escape<Buffer<B>>>,
428    fence: B::Fence,
429}
430
431#[derive(Debug)]
432struct NextUploads<B: rendy_core::hal::Backend> {
433    barrier_buffer:
434        CommandBuffer<B, Transfer, RecordingState<OneShot>, PrimaryLevel, IndividualReset>,
435    command_buffer:
436        CommandBuffer<B, Transfer, RecordingState<OneShot>, PrimaryLevel, IndividualReset>,
437    staging_buffers: Vec<Escape<Buffer<B>>>,
438    fence: B::Fence,
439}
440
441impl<B> FamilyUploads<B>
442where
443    B: rendy_core::hal::Backend,
444{
445    unsafe fn flush(&mut self, family: &mut Family<B>) {
446        for (queue, mut next) in self
447            .next
448            .drain(..)
449            .enumerate()
450            .filter_map(|(i, x)| x.map(|x| (i, x)))
451        {
452            let mut barriers_encoder = next.barrier_buffer.encoder();
453            let mut encoder = next.command_buffer.encoder();
454
455            self.barriers.encode_before(&mut barriers_encoder);
456            self.barriers.encode_after(&mut encoder);
457
458            let (barriers_submit, barrier_buffer) = next.barrier_buffer.finish().submit_once();
459            let (submit, command_buffer) = next.command_buffer.finish().submit_once();
460
461            family.queue_mut(queue).submit_raw_fence(
462                Some(Submission::new().submits(once(barriers_submit).chain(once(submit)))),
463                Some(&next.fence),
464            );
465
466            self.pending.push_back(PendingUploads {
467                barrier_buffer,
468                command_buffer,
469                staging_buffers: next.staging_buffers,
470                fence: next.fence,
471            });
472        }
473    }
474
475    unsafe fn next_upload(
476        &mut self,
477        device: &Device<B>,
478        queue: usize,
479    ) -> Result<&mut NextUploads<B>, OutOfMemory> {
480        while self.next.len() <= queue {
481            self.next.push(None);
482        }
483
484        let pool = &mut self.pool;
485
486        match &mut self.next[queue] {
487            Some(next) => Ok(next),
488            slot @ None => {
489                let [buf_a, buf_b] = self.command_buffers.pop().unwrap_or_else(|| {
490                    let mut bufs = pool.allocate_buffers(2);
491                    [bufs.remove(1), bufs.remove(0)]
492                });
493                let fence = self
494                    .fences
495                    .pop()
496                    .map_or_else(|| device.create_fence(false), Ok)?;
497                *slot = Some(NextUploads {
498                    barrier_buffer: buf_a.begin(OneShot, ()),
499                    command_buffer: buf_b.begin(OneShot, ()),
500                    staging_buffers: Vec::new(),
501                    fence,
502                });
503
504                Ok(slot.as_mut().unwrap())
505            }
506        }
507    }
508
509    /// Cleanup pending updates.
510    ///
511    /// # Safety
512    ///
513    /// `device` must be the same that was used with other methods of this instance.
514    ///
515    unsafe fn cleanup(&mut self, device: &Device<B>) {
516        while let Some(pending) = self.pending.pop_front() {
517            match device.get_fence_status(&pending.fence) {
518                Ok(false) => {
519                    self.pending.push_front(pending);
520                    return;
521                }
522                Err(rendy_core::hal::device::DeviceLost) => {
523                    panic!("Device lost error is not handled yet");
524                }
525                Ok(true) => {
526                    device
527                        .reset_fence(&pending.fence)
528                        .expect("Can always reset signalled fence");
529                    self.fences.push(pending.fence);
530                    self.command_buffers.push([
531                        pending.command_buffer.mark_complete().reset(),
532                        pending.barrier_buffer.mark_complete().reset(),
533                    ]);
534                }
535            }
536        }
537    }
538
539    /// # Safety
540    ///
541    /// Device must be idle.
542    ///
543    unsafe fn dispose(mut self, device: &Device<B>) {
544        let pool = &mut self.pool;
545        self.pending.drain(..).for_each(|pending| {
546            device.destroy_fence(pending.fence);
547            pool.free_buffers(Some(pending.command_buffer.mark_complete()));
548            pool.free_buffers(Some(pending.barrier_buffer.mark_complete()));
549        });
550
551        self.fences
552            .drain(..)
553            .for_each(|fence| device.destroy_fence(fence));
554        pool.free_buffers(
555            self.command_buffers
556                .drain(..)
557                .flat_map(|[a, b]| once(a).chain(once(b))),
558        );
559
560        pool.free_buffers(self.next.drain(..).filter_map(|n| n).flat_map(|next| {
561            device.destroy_fence(next.fence);
562            once(next.command_buffer).chain(once(next.barrier_buffer))
563        }));
564        drop(pool);
565        self.pool.dispose(device);
566    }
567}