1use api::units::*;
11use api::ImageFormat;
12use crate::gpu_types::ImageSource;
13use crate::internal_types::{TextureSource, CacheTextureId, FastHashMap, FastHashSet, FrameId};
14use crate::internal_types::size_of_frame_vec;
15use crate::render_task::{StaticRenderTaskSurface, RenderTaskLocation, RenderTask, SubTask};
16use crate::render_target::RenderTargetKind;
17use crate::render_task::{RenderTaskData, RenderTaskKind};
18use crate::renderer::GpuBufferAddress;
19use crate::renderer::GpuBufferBuilder;
20use crate::resource_cache::ResourceCache;
21use crate::texture_pack::GuillotineAllocator;
22use crate::prim_store::DeferredResolve;
23use crate::image_source::{resolve_image, resolve_cached_render_task};
24use smallvec::SmallVec;
25use topological_sort::TopologicalSort;
26
27use crate::render_target::{RenderTargetList, PictureCacheTarget, RenderTarget};
28use crate::util::{Allocation, VecHelper};
29use std::{usize, f32};
30
31use crate::internal_types::{FrameVec, FrameMemory};
32
33#[cfg(test)]
34use crate::frame_allocator::FrameAllocator;
35
36const TEXTURE_DIMENSION_MASK: i32 = 0xFF;
40
41pub struct RenderTaskAllocation<'a> {
47 pub alloc: Allocation<'a, RenderTask>,
48}
49
50impl<'l> RenderTaskAllocation<'l> {
51 #[inline(always)]
52 pub fn init(self, value: RenderTask) -> RenderTaskId {
53 RenderTaskId::from_index(self.alloc.init(value))
54 }
55}
56
57#[derive(Copy, Clone, PartialEq, Eq, Hash)]
58#[derive(MallocSizeOf)]
59#[cfg_attr(feature = "capture", derive(Serialize))]
60#[cfg_attr(feature = "replay", derive(Deserialize))]
61pub struct RenderTaskId {
62 pub index: u32,
63 pub sub_rect_index: u16,
64}
65
66impl RenderTaskId {
67 pub const INVALID: RenderTaskId = RenderTaskId {
68 index: u32::MAX,
69 sub_rect_index: u16::MAX,
70 };
71
72 #[inline]
73 fn from_index(index: usize) -> Self {
74 RenderTaskId { index: index as u32, sub_rect_index: u16::MAX }
75 }
76
77 #[inline]
78 pub fn index(&self) -> usize {
79 self.index as usize
80 }
81
82 pub fn has_sub_rect(&self) -> bool {
83 self.sub_rect_index != u16::MAX
84 }
85}
86
87impl std::fmt::Debug for RenderTaskId {
88 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
89 if *self == Self::INVALID {
90 write!(f, "<invalid>")
91 } else {
92 write!(f, "#{}", self.index)
93 }
94 }
95}
96
97#[cfg_attr(feature = "capture", derive(Serialize))]
98#[cfg_attr(feature = "replay", derive(Deserialize))]
99#[derive(Clone)]
100pub struct SubTaskRange(std::ops::Range<u32>);
101
102impl SubTaskRange {
103 pub fn empty() -> Self { SubTaskRange(0..0) }
104 pub fn is_empty(&self) -> bool { self.0.is_empty() }
105}
106
107impl std::fmt::Debug for SubTaskRange {
108 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
109 if self.is_empty() {
110 write!(f, "<empty>")
111 } else {
112 self.0.fmt(f)
113 }
114 }
115}
116
117#[derive(Copy, Clone, PartialEq, Eq, Hash)]
118#[derive(MallocSizeOf)]
119#[cfg_attr(feature = "capture", derive(Serialize))]
120#[cfg_attr(feature = "replay", derive(Deserialize))]
121pub struct SubTaskId(u32);
122
123impl std::fmt::Debug for SubTaskId {
124 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
125 write!(f, "#{}", self.0)
126 }
127}
128
129impl Iterator for SubTaskRange {
130 type Item = SubTaskId;
131 fn next(&mut self) -> Option<Self::Item> {
132 Some(SubTaskId(self.0.next()?))
133 }
134}
135
136#[cfg_attr(feature = "capture", derive(Serialize))]
137#[cfg_attr(feature = "replay", derive(Deserialize))]
138#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, PartialOrd, Ord)]
139pub struct PassId(usize);
140
141impl PassId {
142 pub const MIN: PassId = PassId(0);
143 pub const MAX: PassId = PassId(!0 - 1);
144 pub const INVALID: PassId = PassId(!0 - 2);
145}
146
147#[cfg_attr(feature = "capture", derive(Serialize))]
151#[cfg_attr(feature = "replay", derive(Deserialize))]
152struct Surface {
153 kind: RenderTargetKind,
155 allocator: GuillotineAllocator,
157 is_shared: bool,
159 lifetime_group: PassId,
163 pending_frees: usize,
166}
167
168impl Surface {
169 fn alloc_rect(
172 &mut self,
173 size: DeviceIntSize,
174 kind: RenderTargetKind,
175 is_shared: bool,
176 lifetime_group: PassId,
177 ) -> Option<DeviceIntPoint> {
178 if self.kind == kind && self.is_shared == is_shared && self.lifetime_group == lifetime_group {
179 self.allocator
180 .allocate(&size)
181 .map(|(_slice, origin)| origin)
182 } else {
183 None
184 }
185 }
186}
187
188#[cfg_attr(feature = "capture", derive(Serialize))]
191#[cfg_attr(feature = "replay", derive(Deserialize))]
192#[derive(Debug)]
193pub enum SubPassSurface {
194 Dynamic {
196 texture_id: CacheTextureId,
198 target_kind: RenderTargetKind,
200 used_rect: DeviceIntRect,
203 },
204 Persistent {
205 surface: StaticRenderTaskSurface,
207 },
208}
209
210#[cfg_attr(feature = "capture", derive(Serialize))]
212#[cfg_attr(feature = "replay", derive(Deserialize))]
213pub struct SubPass {
214 pub surface: SubPassSurface,
216 pub task_ids: FrameVec<RenderTaskId>,
218}
219
220#[cfg_attr(feature = "capture", derive(Serialize))]
223#[cfg_attr(feature = "replay", derive(Deserialize))]
224pub struct Pass {
225 pub task_ids: FrameVec<RenderTaskId>,
227 pub sub_passes: FrameVec<SubPass>,
229 pub textures_to_invalidate: FrameVec<CacheTextureId>,
232}
233
234#[cfg_attr(feature = "capture", derive(Serialize))]
235#[cfg_attr(feature = "replay", derive(Deserialize))]
236pub struct TaskSubRect {
237 pub sub_rect: DeviceIntRect,
238 source_task: RenderTaskId,
239 uv_address: GpuBufferAddress,
240}
241
242#[cfg_attr(feature = "capture", derive(Serialize))]
245#[cfg_attr(feature = "replay", derive(Deserialize))]
246pub struct RenderTaskGraph {
247 pub tasks: FrameVec<RenderTask>,
249 pub sub_tasks: FrameVec<SubTask>,
252
253 pub sub_rects: FrameVec<TaskSubRect>,
254
255 pub passes: FrameVec<Pass>,
257
258 frame_id: FrameId,
260
261 pub task_data: FrameVec<RenderTaskData>,
263
264 #[cfg(test)]
266 surface_count: usize,
267
268 #[cfg(test)]
270 unique_surfaces: FastHashSet<CacheTextureId>,
271}
272
273pub struct RenderTaskGraphBuilder {
276 tasks: Vec<RenderTask>,
278 sub_tasks: Vec<SubTask>,
280 sub_rects: Vec<TaskSubRect>,
283
284 roots: FastHashSet<RenderTaskId>,
286
287 frame_id: FrameId,
289
290 textures_to_free: FastHashSet<CacheTextureId>,
293
294 freed_tasks: FastHashSet<RenderTaskId>,
298
299 active_surfaces: FastHashMap<CacheTextureId, Surface>,
302}
303
304impl RenderTaskGraphBuilder {
305 pub fn new() -> Self {
308 RenderTaskGraphBuilder {
309 tasks: Vec::new(),
310 sub_tasks: Vec::new(),
311 sub_rects: Vec::new(),
312 roots: FastHashSet::default(),
313 frame_id: FrameId::INVALID,
314 textures_to_free: FastHashSet::default(),
315 freed_tasks: FastHashSet::default(),
316 active_surfaces: FastHashMap::default(),
317 }
318 }
319
320 pub fn frame_id(&self) -> FrameId {
321 self.frame_id
322 }
323
324 pub fn begin_frame(&mut self, frame_id: FrameId) {
326 self.frame_id = frame_id;
327 self.roots.clear();
328 }
329
330 pub fn get_task(
334 &self,
335 task_id: RenderTaskId,
336 ) -> &RenderTask {
337 &self.tasks[task_id.index as usize]
338 }
339
340 pub fn get_task_mut(
344 &mut self,
345 task_id: RenderTaskId,
346 ) -> &mut RenderTask {
347 &mut self.tasks[task_id.index as usize]
348 }
349
350 pub fn add(&mut self) -> RenderTaskAllocation {
352 self.roots.insert(
354 RenderTaskId::from_index(self.tasks.len()),
355 );
356
357 RenderTaskAllocation {
358 alloc: self.tasks.alloc(),
359 }
360 }
361
362 pub fn begin_sub_tasks(&self) -> SubTaskRange {
363 let first = self.sub_tasks.len() as u32;
364 SubTaskRange(first..first)
365 }
366
367 pub fn push_sub_task(&mut self, in_range: &mut SubTaskRange, task: SubTask) {
368 assert_eq!(in_range.0.end as usize, self.sub_tasks.len());
370 in_range.0.end += 1;
371
372 self.sub_tasks.push(task);
373 }
374
375 pub fn add_dependency(
377 &mut self,
378 task_id: RenderTaskId,
379 input: RenderTaskId,
380 ) {
381 self.tasks[task_id.index as usize].children.push(input);
382
383 self.roots.remove(&input);
385 }
386
387 pub fn add_sub_rect(&mut self, source_task: RenderTaskId, sub_rect: &DeviceIntRect) -> RenderTaskId {
388 assert!(self.sub_rects.len() < u16::MAX as usize);
389 if source_task == RenderTaskId::INVALID {
390 return RenderTaskId::INVALID;
391 }
392
393 let sub_rect_index = self.sub_rects.len() as u16;
394 self.sub_rects.push(TaskSubRect {
395 source_task,
396 sub_rect: *sub_rect,
397 uv_address: GpuBufferAddress::INVALID,
398 });
399
400 RenderTaskId {
401 index: source_task.index,
402 sub_rect_index,
403 }
404 }
405
406 pub fn end_frame(
408 &mut self,
409 resource_cache: &mut ResourceCache,
410 gpu_buffers: &mut GpuBufferBuilder,
411 deferred_resolves: &mut FrameVec<DeferredResolve>,
412 max_shared_surface_size: i32,
413 memory: &FrameMemory,
414 ) -> RenderTaskGraph {
415 let task_count = self.tasks.len();
417
418 let mut tasks = memory.new_vec_with_capacity(task_count);
423 for task in self.tasks.drain(..) {
424 tasks.push(task)
425 }
426
427 let mut sub_tasks = memory.new_vec_with_capacity(self.sub_tasks.len());
428 for task in self.sub_tasks.drain(..) {
429 sub_tasks.push(task)
430 }
431
432 let mut graph = RenderTaskGraph {
433 tasks,
434 sub_tasks,
435 sub_rects: memory.new_vec(),
436 passes: memory.new_vec(),
437 task_data: memory.new_vec_with_capacity(task_count),
438 frame_id: self.frame_id,
439 #[cfg(test)]
440 surface_count: 0,
441 #[cfg(test)]
442 unique_surfaces: FastHashSet::default(),
443 };
444
445 let mut pass_count = 0;
454 let mut passes = memory.new_vec();
455 let mut task_sorter = TopologicalSort::<RenderTaskId>::new();
456
457 for (parent_id, task) in graph.tasks.iter().enumerate() {
459 let parent_id = RenderTaskId::from_index(parent_id);
460
461 for child_id in &task.children {
462 task_sorter.add_dependency(
463 parent_id,
464 *child_id,
465 );
466 }
467 }
468
469 loop {
471 let tasks = task_sorter.pop_all();
473
474 if tasks.is_empty() {
476 assert!(task_sorter.is_empty());
479 break;
480 } else {
481 for task_id in &tasks {
483 graph.tasks[task_id.index as usize].render_on = PassId(pass_count);
484 }
485
486 passes.push(tasks);
488 pass_count += 1;
489 }
490 }
491
492 pass_count = pass_count.max(1);
494
495 for pass in passes {
500 for task_id in pass {
501 assign_free_pass(
502 task_id,
503 &mut graph,
504 );
505 }
506 }
507
508 for _ in 0 .. pass_count {
510 graph.passes.push(Pass {
511 task_ids: memory.new_vec(),
512 sub_passes: memory.new_vec(),
513 textures_to_invalidate: memory.new_vec(),
514 });
515 }
516
517 for (index, task) in graph.tasks.iter().enumerate() {
519 if task.kind.is_a_rendering_operation() {
520 let id = RenderTaskId::from_index(index);
521 graph.passes[task.render_on.0].task_ids.push(id);
522 }
523 }
524
525 assert!(self.active_surfaces.is_empty());
529
530 for (pass_id, pass) in graph.passes.iter_mut().enumerate().rev() {
531 assert!(self.textures_to_free.is_empty());
532
533 for task_id in &pass.task_ids {
536
537 let task_location = graph.tasks[task_id.index as usize].location.clone();
538
539 match task_location {
540 RenderTaskLocation::Unallocated { size } => {
541 let task = &mut graph.tasks[task_id.index as usize];
542
543 let mut location = None;
544 let kind = task.kind.target_kind();
545
546 let can_use_shared_surface =
547 task.kind.can_use_shared_surface();
548
549 if can_use_shared_surface {
550 for sub_pass in &mut pass.sub_passes {
554 if let SubPassSurface::Dynamic { texture_id, ref mut used_rect, .. } = sub_pass.surface {
555 let surface = self.active_surfaces.get_mut(&texture_id).unwrap();
556 if let Some(p) = surface.alloc_rect(size, kind, true, task.free_after) {
557 surface.pending_frees += 1;
558 location = Some((texture_id, p));
559 *used_rect = used_rect.union(&DeviceIntRect::from_origin_and_size(p, size));
560 sub_pass.task_ids.push(*task_id);
561 break;
562 }
563 }
564 }
565 }
566
567 if location.is_none() {
568 let can_use_shared_surface = can_use_shared_surface &&
575 size.width <= max_shared_surface_size &&
576 size.height <= max_shared_surface_size;
577
578 let surface_size = if can_use_shared_surface {
579 DeviceIntSize::new(
580 max_shared_surface_size,
581 max_shared_surface_size,
582 )
583 } else {
584 DeviceIntSize::new(
586 (size.width + TEXTURE_DIMENSION_MASK) & !TEXTURE_DIMENSION_MASK,
587 (size.height + TEXTURE_DIMENSION_MASK) & !TEXTURE_DIMENSION_MASK,
588 )
589 };
590
591 if surface_size.is_empty() {
592 let task_name = graph.tasks[task_id.index as usize].kind.as_str();
595 panic!("{} render task has invalid size {:?}", task_name, surface_size);
596 }
597
598 let format = match kind {
599 RenderTargetKind::Color => ImageFormat::RGBA8,
600 RenderTargetKind::Alpha => ImageFormat::R8,
601 };
602
603 let texture_id = resource_cache.get_or_create_render_target_from_pool(
605 surface_size,
606 format,
607 );
608
609 let mut surface = Surface {
611 kind,
612 allocator: GuillotineAllocator::new(Some(surface_size)),
613 is_shared: can_use_shared_surface,
614 lifetime_group: task.free_after,
615 pending_frees: 1,
616 };
617
618 let p = surface.alloc_rect(
620 size,
621 kind,
622 can_use_shared_surface,
623 task.free_after,
624 ).expect("bug: alloc must succeed!");
625
626 location = Some((texture_id, p));
627
628 let _prev_surface = self.active_surfaces.insert(texture_id, surface);
631 assert!(_prev_surface.is_none());
632
633 #[cfg(test)]
635 {
636 graph.surface_count += 1;
637 graph.unique_surfaces.insert(texture_id);
638 }
639
640 let mut task_ids = memory.new_vec();
641 task_ids.push(*task_id);
642
643 pass.sub_passes.push(SubPass {
645 surface: SubPassSurface::Dynamic {
646 texture_id,
647 target_kind: kind,
648 used_rect: DeviceIntRect::from_origin_and_size(p, size),
649 },
650 task_ids,
651 });
652 }
653
654 assert!(location.is_some());
656 task.location = RenderTaskLocation::Dynamic {
657 texture_id: location.unwrap().0,
658 rect: DeviceIntRect::from_origin_and_size(location.unwrap().1, size),
659 };
660 }
661 RenderTaskLocation::Existing { parent_task_id, size: existing_size, .. } => {
662 let parent_task_location = graph.tasks[parent_task_id.index as usize].location.clone();
663
664 match parent_task_location {
665 RenderTaskLocation::Unallocated { .. } |
666 RenderTaskLocation::CacheRequest { .. } |
667 RenderTaskLocation::Existing { .. } => {
668 panic!("bug: reference to existing task must be allocated by now");
669 }
670 RenderTaskLocation::Dynamic { texture_id, rect, .. } => {
671 assert_eq!(existing_size, rect.size());
672
673 let surface = self.active_surfaces.get_mut(&texture_id).unwrap();
674 surface.pending_frees += 1;
675
676 let kind = graph.tasks[parent_task_id.index as usize].kind.target_kind();
677 let mut task_ids = memory.new_vec();
678 task_ids.push(*task_id);
679 pass.sub_passes.push(SubPass {
681 surface: SubPassSurface::Dynamic {
682 texture_id,
683 target_kind: kind,
684 used_rect: rect, },
686 task_ids,
687 });
688
689 let task = &mut graph.tasks[task_id.index as usize];
690 task.location = parent_task_location;
691 }
692 RenderTaskLocation::Static { .. } => {
693 unreachable!("bug: not possible since we don't dup static locations");
694 }
695 }
696 }
697 RenderTaskLocation::Static { ref surface, .. } => {
698 let mut task_ids = memory.new_vec();
701 task_ids.push(*task_id);
702 pass.sub_passes.push(SubPass {
703 surface: SubPassSurface::Persistent {
704 surface: surface.clone(),
705 },
706 task_ids,
707 });
708 }
709 RenderTaskLocation::CacheRequest { .. } => {
710 }
712 RenderTaskLocation::Dynamic { .. } => {
713 panic!("bug: encountered an already allocated task");
715 }
716 }
717 }
718
719 assert!(self.freed_tasks.is_empty());
723 for task_id in &pass.task_ids {
724 let task = &graph.tasks[task_id.index as usize];
725 for child_id in &task.children {
726 let child_task = &graph.tasks[child_id.index as usize];
727 match child_task.location {
728 RenderTaskLocation::Unallocated { .. } |
729 RenderTaskLocation::Existing { .. } => panic!("bug: must be allocated"),
730 RenderTaskLocation::Dynamic { texture_id, .. } => {
731 if child_task.free_after == PassId(pass_id) &&
732 self.freed_tasks.insert(*child_id)
733 {
734 let surface = self.active_surfaces.get_mut(&texture_id).unwrap();
735 surface.pending_frees -= 1;
736 if surface.pending_frees == 0 {
737 self.textures_to_free.insert(texture_id);
738 }
739 }
740 }
741 RenderTaskLocation::Static { .. } => {}
742 RenderTaskLocation::CacheRequest { .. } => {}
743 }
744 }
745 }
746 self.freed_tasks.clear();
747
748 for texture_id in self.textures_to_free.drain() {
751 resource_cache.return_render_target_to_pool(texture_id);
752 self.active_surfaces.remove(&texture_id).unwrap();
753 pass.textures_to_invalidate.push(texture_id);
754 }
755 }
756
757 if !self.active_surfaces.is_empty() {
758 graph.print();
759 assert!(self.active_surfaces.is_empty());
762 }
763
764 for task in &mut graph.tasks {
769 let cache_item = if let Some(ref cache_handle) = task.cache_handle {
773 Some(resolve_cached_render_task(
774 cache_handle,
775 resource_cache,
776 ))
777 } else if let RenderTaskKind::Image(info) = &task.kind {
778 Some(resolve_image(
779 info.request,
780 resource_cache,
781 &mut gpu_buffers.f32,
782 deferred_resolves,
783 info.is_composited,
784 ))
785 } else {
786 None
788 };
789
790 if let Some(cache_item) = &cache_item {
791 task.uv_rect_handle = gpu_buffers.f32.resolve_handle(cache_item.uv_rect_handle);
792
793 if let RenderTaskLocation::CacheRequest { .. } = &task.location {
798 let source = cache_item.texture_id;
799 task.location = RenderTaskLocation::Static {
800 surface: StaticRenderTaskSurface::ReadOnly { source },
801 rect: cache_item.uv_rect,
802 };
803 }
804 }
805
806 let target_rect = task.get_target_rect();
808
809 if cache_item.is_none() {
811 let image_source = ImageSource {
812 p0: target_rect.min.to_f32(),
813 p1: target_rect.max.to_f32(),
814 user_data: [0.0; 4],
815 uv_rect_kind: task.uv_rect_kind,
816 };
817
818 let uv_rect_handle = image_source.write_gpu_blocks(&mut gpu_buffers.f32);
819 task.uv_rect_handle = gpu_buffers.f32.resolve_handle(uv_rect_handle);
820 }
821
822 task.kind.write_gpu_blocks(gpu_buffers);
825
826 graph.task_data.push(
827 task.kind.write_task_data(target_rect)
828 );
829 }
830
831 graph.sub_rects.reserve(self.sub_rects.len());
832 for item in &self.sub_rects {
833 let task = &graph.tasks[item.source_task.index()];
834 let task_rect = task.get_target_rect();
835 let rect = item.sub_rect
836 .translate(task_rect.min.to_vector())
837 .intersection_unchecked(&task_rect);
838
839 let image_source = ImageSource {
840 p0: rect.min.to_f32(),
841 p1: rect.max.to_f32(),
842 user_data: [0.0; 4],
843 uv_rect_kind: task.uv_rect_kind,
844 };
845
846 let uv_rect_handle = image_source.write_gpu_blocks(&mut gpu_buffers.f32);
847
848 graph.sub_rects.push(TaskSubRect {
849 source_task: item.source_task,
850 sub_rect: item.sub_rect,
851 uv_address: gpu_buffers.f32.resolve_handle(uv_rect_handle),
852 });
853 }
854
855 graph
856 }
857}
858
859impl RenderTaskGraph {
860 #[allow(dead_code)]
862 pub fn print(
863 &self,
864 ) {
865 print!("-- RenderTaskGraph --\n");
866
867 for (i, task) in self.tasks.iter().enumerate() {
868 print!("Task {} [{}]: render_on={} free_after={} children={:?} target_size={:?}\n",
869 i,
870 task.kind.as_str(),
871 task.render_on.0,
872 task.free_after.0,
873 task.children,
874 task.get_target_size(),
875 );
876 }
877
878 for (p, pass) in self.passes.iter().enumerate() {
879 print!("Pass {}:\n", p);
880
881 for (s, sub_pass) in pass.sub_passes.iter().enumerate() {
882 print!("\tSubPass {}: {:?}\n",
883 s,
884 sub_pass.surface,
885 );
886
887 for task_id in &sub_pass.task_ids {
888 print!("\t\tTask {:?}\n", task_id.index);
889 }
890 }
891 }
892 }
893
894 pub fn resolve_texture(
895 &self,
896 task_id: impl Into<Option<RenderTaskId>>,
897 ) -> Option<TextureSource> {
898 let task_id = task_id.into()?;
899 let task = &self[task_id];
900
901 match task.get_texture_source() {
902 TextureSource::Invalid => None,
903 source => Some(source),
904 }
905 }
906
907 pub fn resolve_location(
908 &self,
909 task_id: impl Into<Option<RenderTaskId>>,
910 ) -> Option<(GpuBufferAddress, TextureSource)> {
911 self.resolve_impl(task_id.into()?)
912 }
913
914 fn resolve_impl(
915 &self,
916 task_id: RenderTaskId,
917 ) -> Option<(GpuBufferAddress, TextureSource)> {
918 let task = &self[task_id];
919 let texture_source = task.get_texture_source();
920
921 if let TextureSource::Invalid = texture_source {
922 return None;
923 }
924
925 let uv_address = if task_id.has_sub_rect() {
926 self.sub_rects[task_id.sub_rect_index as usize].uv_address
927 } else {
928 task.get_texture_address()
929 };
930
931 assert!(uv_address.is_valid());
932
933 Some((uv_address, texture_source))
934 }
935
936 pub fn report_memory(&self) -> usize {
937 let mut mem = size_of_frame_vec(&self.tasks)
942 + size_of_frame_vec(&self.task_data)
943 + size_of_frame_vec(&self.passes);
944
945 for pass in &self.passes {
946 mem += size_of_frame_vec(&pass.task_ids)
947 + size_of_frame_vec(&pass.sub_passes)
948 + size_of_frame_vec(&pass.textures_to_invalidate);
949 for sub_pass in &pass.sub_passes {
950 mem += size_of_frame_vec(&sub_pass.task_ids);
951 }
952 }
953
954 mem
955 }
956
957 #[cfg(test)]
958 pub fn new_for_testing() -> Self {
959 let allocator = FrameAllocator::fallback();
960 RenderTaskGraph {
961 tasks: allocator.clone().new_vec(),
962 sub_tasks: allocator.clone().new_vec(),
963 sub_rects: allocator.clone().new_vec(),
964 passes: allocator.clone().new_vec(),
965 frame_id: FrameId::INVALID,
966 task_data: allocator.clone().new_vec(),
967 surface_count: 0,
968 unique_surfaces: FastHashSet::default(),
969 }
970 }
971
972 #[cfg(test)]
974 pub fn surface_counts(&self) -> (usize, usize) {
975 (self.surface_count, self.unique_surfaces.len())
976 }
977
978 #[cfg(debug_assertions)]
980 pub fn frame_id(&self) -> FrameId {
981 self.frame_id
982 }
983}
984
985impl std::ops::Index<RenderTaskId> for RenderTaskGraph {
987 type Output = RenderTask;
988 fn index(&self, id: RenderTaskId) -> &RenderTask {
989 &self.tasks[id.index as usize]
990 }
991}
992
993impl std::ops::Index<SubTaskId> for RenderTaskGraph {
994 type Output = SubTask;
995 fn index(&self, id: SubTaskId) -> &SubTask {
996 &self.sub_tasks[id.0 as usize]
997 }
998}
999
1000fn assign_free_pass(
1001 id: RenderTaskId,
1002 graph: &mut RenderTaskGraph,
1003) {
1004 let task = &mut graph.tasks[id.index as usize];
1005 let render_on = task.render_on;
1006
1007 let mut child_task_ids: SmallVec<[RenderTaskId; 8]> = SmallVec::new();
1008 child_task_ids.extend_from_slice(&task.children);
1009
1010 for child_id in child_task_ids {
1011 let child_location = graph.tasks[child_id.index as usize].location.clone();
1012
1013 match child_location {
1018 RenderTaskLocation::CacheRequest { .. } => {}
1019 RenderTaskLocation::Static { .. } => {
1020 }
1023 RenderTaskLocation::Dynamic { .. } => {
1024 panic!("bug: should not be allocated yet");
1025 }
1026 RenderTaskLocation::Unallocated { .. } |
1027 RenderTaskLocation::Existing { .. } => {
1028 let child_task = &mut graph.tasks[child_id.index as usize];
1029 child_task.free_after = child_task.free_after.min(render_on);
1030 }
1031 }
1032 }
1033}
1034
1035#[cfg_attr(feature = "capture", derive(Serialize))]
1041#[cfg_attr(feature = "replay", derive(Deserialize))]
1042pub struct RenderPass {
1043 pub alpha: RenderTargetList,
1045 pub color: RenderTargetList,
1046 pub texture_cache: FastHashMap<CacheTextureId, RenderTarget>,
1047 pub picture_cache: FrameVec<PictureCacheTarget>,
1048 pub textures_to_invalidate: FrameVec<CacheTextureId>,
1049}
1050
1051impl RenderPass {
1052 pub fn new(src: &Pass, memory: &mut FrameMemory) -> Self {
1054 RenderPass {
1055 color: RenderTargetList::new(memory.allocator()),
1056 alpha: RenderTargetList::new(memory.allocator()),
1057 texture_cache: FastHashMap::default(),
1058 picture_cache: memory.allocator().new_vec(),
1059 textures_to_invalidate: src.textures_to_invalidate.clone(),
1060 }
1061 }
1062}
1063
1064#[cfg(feature = "capture")]
1066pub fn dump_render_tasks_as_svg(
1067 render_tasks: &RenderTaskGraph,
1068 output: &mut dyn std::io::Write,
1069) -> std::io::Result<()> {
1070 use svg_fmt::*;
1071
1072 let node_width = 80.0;
1073 let node_height = 30.0;
1074 let vertical_spacing = 8.0;
1075 let horizontal_spacing = 20.0;
1076 let margin = 10.0;
1077 let text_size = 10.0;
1078
1079 let mut pass_rects = Vec::new();
1080 let mut nodes = vec![None; render_tasks.tasks.len()];
1081
1082 let mut x = margin;
1083 let mut max_y: f32 = 0.0;
1084
1085 #[derive(Clone)]
1086 struct Node {
1087 rect: Rectangle,
1088 label: Text,
1089 size: Text,
1090 }
1091
1092 for pass in render_tasks.passes.iter().rev() {
1093 let mut layout = VerticalLayout::new(x, margin, node_width);
1094
1095 for task_id in &pass.task_ids {
1096 let task_index = task_id.index as usize;
1097 let task = &render_tasks.tasks[task_index];
1098
1099 let rect = layout.push_rectangle(node_height);
1100
1101 let tx = rect.x + rect.w / 2.0;
1102 let ty = rect.y + 10.0;
1103
1104 let label = text(tx, ty, format!("{}", task.kind.as_str()));
1105 let size = text(tx, ty + 12.0, format!("{:?}", task.location.size()));
1106
1107 nodes[task_index] = Some(Node { rect, label, size });
1108
1109 layout.advance(vertical_spacing);
1110 }
1111
1112 pass_rects.push(layout.total_rectangle());
1113
1114 x += node_width + horizontal_spacing;
1115 max_y = max_y.max(layout.y + margin);
1116 }
1117
1118 let mut links = Vec::new();
1119 for node_index in 0..nodes.len() {
1120 if nodes[node_index].is_none() {
1121 continue;
1122 }
1123
1124 let task = &render_tasks.tasks[node_index];
1125 for dep in &task.children {
1126 let dep_index = dep.index as usize;
1127
1128 if let (&Some(ref node), &Some(ref dep_node)) = (&nodes[node_index], &nodes[dep_index]) {
1129 links.push((
1130 dep_node.rect.x + dep_node.rect.w,
1131 dep_node.rect.y + dep_node.rect.h / 2.0,
1132 node.rect.x,
1133 node.rect.y + node.rect.h / 2.0,
1134 ));
1135 }
1136 }
1137 }
1138
1139 let svg_w = x + margin;
1140 let svg_h = max_y + margin;
1141 writeln!(output, "{}", BeginSvg { w: svg_w, h: svg_h })?;
1142
1143 writeln!(output,
1145 " {}",
1146 rectangle(0.0, 0.0, svg_w, svg_h)
1147 .inflate(1.0, 1.0)
1148 .fill(rgb(50, 50, 50))
1149 )?;
1150
1151 for rect in pass_rects {
1153 writeln!(output,
1154 " {}",
1155 rect.inflate(3.0, 3.0)
1156 .border_radius(4.0)
1157 .opacity(0.4)
1158 .fill(black())
1159 )?;
1160 }
1161
1162 for (x1, y1, x2, y2) in links {
1164 dump_task_dependency_link(output, x1, y1, x2, y2);
1165 }
1166
1167 for node in &nodes {
1169 if let Some(node) = node {
1170 writeln!(output,
1171 " {}",
1172 node.rect
1173 .clone()
1174 .fill(black())
1175 .border_radius(3.0)
1176 .opacity(0.5)
1177 .offset(0.0, 2.0)
1178 )?;
1179 writeln!(output,
1180 " {}",
1181 node.rect
1182 .clone()
1183 .fill(rgb(200, 200, 200))
1184 .border_radius(3.0)
1185 .opacity(0.8)
1186 )?;
1187
1188 writeln!(output,
1189 " {}",
1190 node.label
1191 .clone()
1192 .size(text_size)
1193 .align(Align::Center)
1194 .color(rgb(50, 50, 50))
1195 )?;
1196 writeln!(output,
1197 " {}",
1198 node.size
1199 .clone()
1200 .size(text_size * 0.7)
1201 .align(Align::Center)
1202 .color(rgb(50, 50, 50))
1203 )?;
1204 }
1205 }
1206
1207 writeln!(output, "{}", EndSvg)
1208}
1209
1210#[allow(dead_code)]
1211fn dump_task_dependency_link(
1212 output: &mut dyn std::io::Write,
1213 x1: f32, y1: f32,
1214 x2: f32, y2: f32,
1215) {
1216 use svg_fmt::*;
1217
1218 let simple_path = (y1 - y2).abs() > 1.0 || (x2 - x1) < 45.0;
1222
1223 let mid_x = (x1 + x2) / 2.0;
1224 if simple_path {
1225 write!(output, " {}",
1226 path().move_to(x1, y1)
1227 .cubic_bezier_to(mid_x, y1, mid_x, y2, x2, y2)
1228 .fill(Fill::None)
1229 .stroke(Stroke::Color(rgb(100, 100, 100), 3.0))
1230 ).unwrap();
1231 } else {
1232 let ctrl1_x = (mid_x + x1) / 2.0;
1233 let ctrl2_x = (mid_x + x2) / 2.0;
1234 let ctrl_y = y1 - 25.0;
1235 write!(output, " {}",
1236 path().move_to(x1, y1)
1237 .cubic_bezier_to(ctrl1_x, y1, ctrl1_x, ctrl_y, mid_x, ctrl_y)
1238 .cubic_bezier_to(ctrl2_x, ctrl_y, ctrl2_x, y2, x2, y2)
1239 .fill(Fill::None)
1240 .stroke(Stroke::Color(rgb(100, 100, 100), 3.0))
1241 ).unwrap();
1242 }
1243}
1244
1245#[cfg(test)]
1247fn pc_target(
1248 surface_id: u64,
1249 tile_x: i32,
1250 tile_y: i32,
1251) -> RenderTaskLocation {
1252 use crate::{
1253 composite::{NativeSurfaceId, NativeTileId},
1254 picture::ResolvedSurfaceTexture,
1255 };
1256
1257 let width = 512;
1258 let height = 512;
1259
1260 RenderTaskLocation::Static {
1261 surface: StaticRenderTaskSurface::PictureCache {
1262 surface: ResolvedSurfaceTexture::Native {
1263 id: NativeTileId {
1264 surface_id: NativeSurfaceId(surface_id),
1265 x: tile_x,
1266 y: tile_y,
1267 },
1268 size: DeviceIntSize::new(width, height),
1269 },
1270 },
1271 rect: DeviceIntSize::new(width, height).into(),
1272 }
1273}
1274
1275#[cfg(test)]
1276impl RenderTaskGraphBuilder {
1277 fn test_expect(
1278 mut self,
1279 pass_count: usize,
1280 total_surface_count: usize,
1281 unique_surfaces: &[(i32, i32, ImageFormat)],
1282 ) {
1283 use crate::{internal_types::FrameStamp, renderer::{GpuBufferBuilderF, GpuBufferBuilderI}};
1284 use api::{DocumentId, IdNamespace};
1285
1286 let mut rc = ResourceCache::new_for_testing();
1287
1288 let mut frame_stamp = FrameStamp::first(DocumentId::new(IdNamespace(1), 1));
1289 frame_stamp.advance();
1290
1291 let frame_memory = FrameMemory::fallback();
1292 let mut gpu_buffers = GpuBufferBuilder {
1293 f32: GpuBufferBuilderF::new(&frame_memory, 0, FrameId::first()),
1294 i32: GpuBufferBuilderI::new(&frame_memory, 0, FrameId::first()),
1295 };
1296 let g = self.end_frame(&mut rc, &mut gpu_buffers, &mut frame_memory.new_vec(), 2048, &frame_memory);
1297 g.print();
1298
1299 assert_eq!(g.passes.len(), pass_count);
1300 assert_eq!(g.surface_counts(), (total_surface_count, unique_surfaces.len()));
1301
1302 rc.validate_surfaces(unique_surfaces);
1303 }
1304}
1305
1306#[cfg(test)]
1308fn task_location(location: RenderTaskLocation) -> RenderTask {
1309 RenderTask::new_test(
1310 location,
1311 RenderTargetKind::Color,
1312 )
1313}
1314
1315#[cfg(test)]
1317fn task_dynamic(size: i32) -> RenderTask {
1318 RenderTask::new_test(
1319 RenderTaskLocation::Unallocated { size: DeviceIntSize::new(size, size) },
1320 RenderTargetKind::Color,
1321 )
1322}
1323
1324#[test]
1325fn fg_test_1() {
1326 let mut gb = RenderTaskGraphBuilder::new();
1330
1331 let root_target = pc_target(0, 0, 0);
1332
1333 let root = gb.add().init(task_location(root_target.clone()));
1334
1335 let readback = gb.add().init(task_dynamic(100));
1336 gb.add_dependency(readback, root);
1337
1338 let mix_blend_content = gb.add().init(task_dynamic(50));
1339
1340 let content = gb.add().init(task_location(root_target));
1341 gb.add_dependency(content, readback);
1342 gb.add_dependency(content, mix_blend_content);
1343
1344 gb.test_expect(3, 1, &[
1345 (2048, 2048, ImageFormat::RGBA8),
1346 ]);
1347}
1348
1349#[test]
1350fn fg_test_3() {
1351 let mut gb = RenderTaskGraphBuilder::new();
1355
1356 let pc_root = gb.add().init(task_location(pc_target(0, 0, 0)));
1357
1358 let child_pic_0 = gb.add().init(task_dynamic(128));
1359 let child_pic_1 = gb.add().init(task_dynamic(3000));
1360
1361 gb.add_dependency(pc_root, child_pic_0);
1362 gb.add_dependency(pc_root, child_pic_1);
1363
1364 gb.test_expect(2, 2, &[
1365 (2048, 2048, ImageFormat::RGBA8),
1366 (3072, 3072, ImageFormat::RGBA8),
1367 ]);
1368}
1369
1370#[test]
1371fn fg_test_4() {
1372 let mut gb = RenderTaskGraphBuilder::new();
1376
1377 let pc_root = gb.add().init(task_location(pc_target(0, 0, 0)));
1378
1379 let child_pic_0 = gb.add().init(task_dynamic(128));
1380 let child_pic_1 = gb.add().init(task_dynamic(128));
1381 let child_pic_2 = gb.add().init(task_dynamic(128));
1382
1383 gb.add_dependency(pc_root, child_pic_0);
1384 gb.add_dependency(child_pic_0, child_pic_1);
1385 gb.add_dependency(child_pic_1, child_pic_2);
1386
1387 gb.test_expect(4, 3, &[
1388 (2048, 2048, ImageFormat::RGBA8),
1389 (2048, 2048, ImageFormat::RGBA8),
1390 ]);
1391}
1392
1393#[test]
1394fn fg_test_5() {
1395 let mut gb = RenderTaskGraphBuilder::new();
1400
1401 let pc_root = gb.add().init(task_location(pc_target(0, 0, 0)));
1402
1403 let child_pic_0 = gb.add().init(task_dynamic(128));
1404 let child_pic_1 = gb.add().init(task_dynamic(64));
1405 let child_pic_2 = gb.add().init(task_dynamic(32));
1406 let child_pic_3 = gb.add().init(task_dynamic(16));
1407
1408 gb.add_dependency(pc_root, child_pic_0);
1409 gb.add_dependency(child_pic_0, child_pic_1);
1410 gb.add_dependency(child_pic_1, child_pic_2);
1411 gb.add_dependency(child_pic_2, child_pic_3);
1412 gb.add_dependency(pc_root, child_pic_3);
1413
1414 gb.test_expect(5, 4, &[
1415 (2048, 2048, ImageFormat::RGBA8),
1416 (2048, 2048, ImageFormat::RGBA8),
1417 (2048, 2048, ImageFormat::RGBA8),
1418 ]);
1419}
1420
1421#[test]
1422fn fg_test_6() {
1423 let mut gb = RenderTaskGraphBuilder::new();
1427
1428 let pc_root_1 = gb.add().init(task_location(pc_target(0, 0, 0)));
1429 let pc_root_2 = gb.add().init(task_location(pc_target(0, 1, 0)));
1430
1431 let child_pic = gb.add().init(task_dynamic(128));
1432
1433 gb.add_dependency(pc_root_1, child_pic);
1434 gb.add_dependency(pc_root_2, child_pic);
1435
1436 gb.test_expect(2, 1, &[
1437 (2048, 2048, ImageFormat::RGBA8),
1438 ]);
1439}
1440
1441#[test]
1442fn fg_test_7() {
1443 let mut gb = RenderTaskGraphBuilder::new();
1447
1448 let pc_root = gb.add().init(task_location(pc_target(0, 0, 0)));
1449
1450 let child0 = gb.add().init(task_dynamic(16));
1451 let child1 = gb.add().init(task_dynamic(16));
1452
1453 let child2 = gb.add().init(task_dynamic(16));
1454 let child3 = gb.add().init(task_dynamic(16));
1455
1456 gb.add_dependency(pc_root, child0);
1457 gb.add_dependency(child0, child1);
1458 gb.add_dependency(pc_root, child1);
1459
1460 gb.add_dependency(pc_root, child2);
1461 gb.add_dependency(child2, child3);
1462
1463 gb.test_expect(3, 3, &[
1464 (2048, 2048, ImageFormat::RGBA8),
1465 (2048, 2048, ImageFormat::RGBA8),
1466 (2048, 2048, ImageFormat::RGBA8),
1467 ]);
1468}