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#[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#[derive(Debug, Clone)]
36pub struct BlitRegion {
37 pub src: BlitImageState,
39 pub dst: BlitImageState,
41}
42
43impl BlitRegion {
44 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#[derive(Debug, Clone)]
139pub struct BlitImageState {
140 pub subresource: rendy_core::hal::image::SubresourceLayers,
142 pub bounds: Range<rendy_core::hal::image::Offset>,
144 pub last_stage: rendy_core::hal::pso::PipelineStage,
146 pub last_access: rendy_core::hal::image::Access,
148 pub last_layout: rendy_core::hal::image::Layout,
150 pub next_stage: rendy_core::hal::pso::PipelineStage,
152 pub next_access: rendy_core::hal::image::Access,
154 pub next_layout: rendy_core::hal::image::Layout,
156}
157
158impl<B> Blitter<B>
159where
160 B: rendy_core::hal::Backend,
161{
162 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 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 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 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 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 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
297pub 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(®.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(®.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 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 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 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}