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#[derive(Clone, Copy, Debug)]
17pub struct BufferState {
18 pub queue: QueueId,
20
21 pub stage: rendy_core::hal::pso::PipelineStage,
23
24 pub access: rendy_core::hal::buffer::Access,
26}
27
28impl BufferState {
29 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 pub fn with_stage(mut self, stage: rendy_core::hal::pso::PipelineStage) -> Self {
40 self.stage = stage;
41 self
42 }
43
44 pub fn with_access(mut self, access: rendy_core::hal::buffer::Access) -> Self {
46 self.access = access;
47 self
48 }
49}
50
51#[derive(Clone, Copy, Debug)]
53pub struct ImageState {
54 pub queue: QueueId,
56
57 pub stage: rendy_core::hal::pso::PipelineStage,
59
60 pub access: rendy_core::hal::image::Access,
62
63 pub layout: rendy_core::hal::image::Layout,
65}
66
67impl ImageState {
68 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 pub fn with_stage(mut self, stage: rendy_core::hal::pso::PipelineStage) -> Self {
80 self.stage = stage;
81 self
82 }
83
84 pub fn with_access(mut self, access: rendy_core::hal::image::Access) -> Self {
86 self.access = access;
87 self
88 }
89}
90
91#[derive(Clone, Copy, Debug)]
93pub enum ImageStateOrLayout {
94 State(ImageState),
96
97 Layout(rendy_core::hal::image::Layout),
99}
100
101impl ImageStateOrLayout {
102 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 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 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 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 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 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 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 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 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 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}