1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, ImageDescriptor, ImageFormat};
6 use api::{DeviceSize, PremultipliedColorF};
7 use box_shadow::{BoxShadowCacheKey};
8 use clip::{ClipSource, ClipStore, ClipWorkItem};
9 use clip_scroll_tree::CoordinateSystemId;
10 use device::TextureFilter;
11 use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
12 use gpu_types::{ImageSource, PictureType, RasterizationSpace};
13 use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture};
14 use picture::ContentOrigin;
15 use prim_store::{PrimitiveIndex, ImageCacheKey};
16 #[cfg(feature = "debugger")]
17 use print_tree::{PrintTreePrinter};
18 use resource_cache::{CacheItem, ResourceCache};
19 use std::{cmp, ops, usize, f32, i32};
20 use texture_cache::{TextureCache, TextureCacheHandle};
21 use tiling::{RenderPass, RenderTargetIndex};
22 use tiling::{RenderTargetKind};
23
24 const FLOATS_PER_RENDER_TASK_INFO: usize = 12;
25 pub const MAX_BLUR_STD_DEVIATION: f32 = 4.0;
26 pub const MIN_DOWNSCALING_RT_SIZE: i32 = 128;
27
28 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
29 #[cfg_attr(feature = "capture", derive(Serialize))]
30 #[cfg_attr(feature = "replay", derive(Deserialize))]
31 pub struct RenderTaskId(pub u32); // TODO(gw): Make private when using GPU cache!
32
33 #[derive(Debug, Copy, Clone)]
34 #[repr(C)]
35 #[cfg_attr(feature = "capture", derive(Serialize))]
36 #[cfg_attr(feature = "replay", derive(Deserialize))]
37 pub struct RenderTaskAddress(pub u32);
38
39 #[derive(Debug)]
40 #[cfg_attr(feature = "capture", derive(Serialize))]
41 #[cfg_attr(feature = "replay", derive(Deserialize))]
42 pub struct RenderTaskTree {
43 pub tasks: Vec<RenderTask>,
44 pub task_data: Vec<RenderTaskData>,
45 next_saved: SavedTargetIndex,
46 }
47
48 impl RenderTaskTree {
new() -> Self49 pub fn new() -> Self {
50 RenderTaskTree {
51 tasks: Vec::new(),
52 task_data: Vec::new(),
53 next_saved: SavedTargetIndex(0),
54 }
55 }
56
add(&mut self, task: RenderTask) -> RenderTaskId57 pub fn add(&mut self, task: RenderTask) -> RenderTaskId {
58 let id = RenderTaskId(self.tasks.len() as u32);
59 self.tasks.push(task);
60 id
61 }
62
max_depth(&self, id: RenderTaskId, depth: usize, max_depth: &mut usize)63 pub fn max_depth(&self, id: RenderTaskId, depth: usize, max_depth: &mut usize) {
64 let depth = depth + 1;
65 *max_depth = cmp::max(*max_depth, depth);
66 let task = &self.tasks[id.0 as usize];
67 for child in &task.children {
68 self.max_depth(*child, depth, max_depth);
69 }
70 }
71
assign_to_passes( &self, id: RenderTaskId, pass_index: usize, passes: &mut Vec<RenderPass>, )72 pub fn assign_to_passes(
73 &self,
74 id: RenderTaskId,
75 pass_index: usize,
76 passes: &mut Vec<RenderPass>,
77 ) {
78 let task = &self.tasks[id.0 as usize];
79
80 for child in &task.children {
81 self.assign_to_passes(*child, pass_index - 1, passes);
82 }
83
84 // Sanity check - can be relaxed if needed
85 match task.location {
86 RenderTaskLocation::Fixed(..) => {
87 debug_assert!(pass_index == passes.len() - 1);
88 }
89 RenderTaskLocation::Dynamic(..) |
90 RenderTaskLocation::TextureCache(..) => {
91 debug_assert!(pass_index < passes.len() - 1);
92 }
93 }
94
95 // If this task can be shared between multiple
96 // passes, render it in the first pass so that
97 // it is available to all subsequent passes.
98 let pass_index = if task.is_shared() {
99 debug_assert!(task.children.is_empty());
100 0
101 } else {
102 pass_index
103 };
104
105 let pass = &mut passes[pass_index];
106 pass.add_render_task(id, task.get_dynamic_size(), task.target_kind());
107 }
108
get_task_address(&self, id: RenderTaskId) -> RenderTaskAddress109 pub fn get_task_address(&self, id: RenderTaskId) -> RenderTaskAddress {
110 RenderTaskAddress(id.0)
111 }
112
build(&mut self)113 pub fn build(&mut self) {
114 for task in &self.tasks {
115 self.task_data.push(task.write_task_data());
116 }
117 }
118
save_target(&mut self) -> SavedTargetIndex119 pub fn save_target(&mut self) -> SavedTargetIndex {
120 let id = self.next_saved;
121 self.next_saved.0 += 1;
122 id
123 }
124 }
125
126 impl ops::Index<RenderTaskId> for RenderTaskTree {
127 type Output = RenderTask;
index(&self, id: RenderTaskId) -> &RenderTask128 fn index(&self, id: RenderTaskId) -> &RenderTask {
129 &self.tasks[id.0 as usize]
130 }
131 }
132
133 impl ops::IndexMut<RenderTaskId> for RenderTaskTree {
index_mut(&mut self, id: RenderTaskId) -> &mut RenderTask134 fn index_mut(&mut self, id: RenderTaskId) -> &mut RenderTask {
135 &mut self.tasks[id.0 as usize]
136 }
137 }
138
139 #[derive(Debug)]
140 #[cfg_attr(feature = "capture", derive(Serialize))]
141 #[cfg_attr(feature = "replay", derive(Deserialize))]
142 pub enum RenderTaskLocation {
143 Fixed(DeviceIntRect),
144 Dynamic(Option<(DeviceIntPoint, RenderTargetIndex)>, DeviceIntSize),
145 TextureCache(SourceTexture, i32, DeviceIntRect),
146 }
147
148 #[derive(Debug)]
149 #[cfg_attr(feature = "capture", derive(Serialize))]
150 #[cfg_attr(feature = "replay", derive(Deserialize))]
151 pub struct CacheMaskTask {
152 actual_rect: DeviceIntRect,
153 pub clips: Vec<ClipWorkItem>,
154 pub coordinate_system_id: CoordinateSystemId,
155 }
156
157 #[derive(Debug)]
158 #[cfg_attr(feature = "capture", derive(Serialize))]
159 #[cfg_attr(feature = "replay", derive(Deserialize))]
160 pub struct ClipRegionTask {
161 pub clip_data_address: GpuCacheAddress,
162 }
163
164 #[derive(Debug)]
165 #[cfg_attr(feature = "capture", derive(Serialize))]
166 #[cfg_attr(feature = "replay", derive(Deserialize))]
167 pub struct PictureTask {
168 pub prim_index: PrimitiveIndex,
169 pub target_kind: RenderTargetKind,
170 pub content_origin: ContentOrigin,
171 pub color: PremultipliedColorF,
172 pub pic_type: PictureType,
173 pub uv_rect_handle: GpuCacheHandle,
174 }
175
176 #[derive(Debug)]
177 #[cfg_attr(feature = "capture", derive(Serialize))]
178 #[cfg_attr(feature = "replay", derive(Deserialize))]
179 pub struct BlurTask {
180 pub blur_std_deviation: f32,
181 pub target_kind: RenderTargetKind,
182 pub color: PremultipliedColorF,
183 pub uv_rect_handle: GpuCacheHandle,
184 }
185
186 impl BlurTask {
187 #[cfg(feature = "debugger")]
print_with<T: PrintTreePrinter>(&self, pt: &mut T)188 fn print_with<T: PrintTreePrinter>(&self, pt: &mut T) {
189 pt.add_item(format!("std deviation: {}", self.blur_std_deviation));
190 pt.add_item(format!("target: {:?}", self.target_kind));
191 }
192 }
193
194 // Where the source data for a blit task can be found.
195 #[derive(Debug)]
196 #[cfg_attr(feature = "capture", derive(Serialize))]
197 #[cfg_attr(feature = "replay", derive(Deserialize))]
198 pub enum BlitSource {
199 Image {
200 key: ImageCacheKey,
201 },
202 RenderTask {
203 task_id: RenderTaskId,
204 },
205 }
206
207 #[derive(Debug)]
208 #[cfg_attr(feature = "capture", derive(Serialize))]
209 #[cfg_attr(feature = "replay", derive(Deserialize))]
210 pub struct BlitTask {
211 pub source: BlitSource,
212 }
213
214 #[derive(Debug)]
215 #[cfg_attr(feature = "capture", derive(Serialize))]
216 #[cfg_attr(feature = "replay", derive(Deserialize))]
217 pub struct RenderTaskData {
218 pub data: [f32; FLOATS_PER_RENDER_TASK_INFO],
219 }
220
221 #[derive(Debug)]
222 #[cfg_attr(feature = "capture", derive(Serialize))]
223 #[cfg_attr(feature = "replay", derive(Deserialize))]
224 pub enum RenderTaskKind {
225 Picture(PictureTask),
226 CacheMask(CacheMaskTask),
227 ClipRegion(ClipRegionTask),
228 VerticalBlur(BlurTask),
229 HorizontalBlur(BlurTask),
230 Readback(DeviceIntRect),
231 Scaling(RenderTargetKind),
232 Blit(BlitTask),
233 }
234
235 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
236 #[cfg_attr(feature = "capture", derive(Serialize))]
237 #[cfg_attr(feature = "replay", derive(Deserialize))]
238 pub enum ClearMode {
239 // Applicable to color and alpha targets.
240 Zero,
241 One,
242
243 // Applicable to color targets only.
244 Transparent,
245 }
246
247 #[derive(Debug)]
248 #[cfg_attr(feature = "capture", derive(Serialize))]
249 #[cfg_attr(feature = "replay", derive(Deserialize))]
250 pub struct RenderTask {
251 pub location: RenderTaskLocation,
252 pub children: Vec<RenderTaskId>,
253 pub kind: RenderTaskKind,
254 pub clear_mode: ClearMode,
255 pub saved_index: Option<SavedTargetIndex>,
256 }
257
258 impl RenderTask {
new_picture( location: RenderTaskLocation, prim_index: PrimitiveIndex, target_kind: RenderTargetKind, content_origin: ContentOrigin, color: PremultipliedColorF, clear_mode: ClearMode, children: Vec<RenderTaskId>, pic_type: PictureType, ) -> Self259 pub fn new_picture(
260 location: RenderTaskLocation,
261 prim_index: PrimitiveIndex,
262 target_kind: RenderTargetKind,
263 content_origin: ContentOrigin,
264 color: PremultipliedColorF,
265 clear_mode: ClearMode,
266 children: Vec<RenderTaskId>,
267 pic_type: PictureType,
268 ) -> Self {
269 RenderTask {
270 children,
271 location,
272 kind: RenderTaskKind::Picture(PictureTask {
273 prim_index,
274 target_kind,
275 content_origin,
276 color,
277 pic_type,
278 uv_rect_handle: GpuCacheHandle::new(),
279 }),
280 clear_mode,
281 saved_index: None,
282 }
283 }
284
new_readback(screen_rect: DeviceIntRect) -> Self285 pub fn new_readback(screen_rect: DeviceIntRect) -> Self {
286 RenderTask {
287 children: Vec::new(),
288 location: RenderTaskLocation::Dynamic(None, screen_rect.size),
289 kind: RenderTaskKind::Readback(screen_rect),
290 clear_mode: ClearMode::Transparent,
291 saved_index: None,
292 }
293 }
294
new_blit( size: DeviceIntSize, source: BlitSource, ) -> Self295 pub fn new_blit(
296 size: DeviceIntSize,
297 source: BlitSource,
298 ) -> Self {
299 let mut children = Vec::new();
300
301 // If this blit uses a render task as a source,
302 // ensure it's added as a child task. This will
303 // ensure it gets allocated in the correct pass
304 // and made available as an input when this task
305 // executes.
306 if let BlitSource::RenderTask { task_id } = source {
307 children.push(task_id);
308 }
309
310 RenderTask {
311 children,
312 location: RenderTaskLocation::Dynamic(None, size),
313 kind: RenderTaskKind::Blit(BlitTask {
314 source,
315 }),
316 clear_mode: ClearMode::Transparent,
317 saved_index: None,
318 }
319 }
320
new_mask( outer_rect: DeviceIntRect, clips: Vec<ClipWorkItem>, prim_coordinate_system_id: CoordinateSystemId, clip_store: &mut ClipStore, gpu_cache: &mut GpuCache, resource_cache: &mut ResourceCache, render_tasks: &mut RenderTaskTree, ) -> Self321 pub fn new_mask(
322 outer_rect: DeviceIntRect,
323 clips: Vec<ClipWorkItem>,
324 prim_coordinate_system_id: CoordinateSystemId,
325 clip_store: &mut ClipStore,
326 gpu_cache: &mut GpuCache,
327 resource_cache: &mut ResourceCache,
328 render_tasks: &mut RenderTaskTree,
329 ) -> Self {
330 let mut children = Vec::new();
331
332 // Step through the clip sources that make up this mask. If we find
333 // any box-shadow clip sources, request that image from the render
334 // task cache. This allows the blurred box-shadow rect to be cached
335 // in the texture cache across frames.
336 // TODO(gw): Consider moving this logic outside this function, especially
337 // as we add more clip sources that depend on render tasks.
338 // TODO(gw): If this ever shows up in a profile, we could pre-calculate
339 // whether a ClipSources contains any box-shadows and skip
340 // this iteration for the majority of cases.
341 for clip_item in &clips {
342 let clip_sources = clip_store.get_opt_mut(&clip_item.clip_sources).expect("bug");
343 for &mut (ref mut clip, _) in &mut clip_sources.clips {
344 match *clip {
345 ClipSource::BoxShadow(ref mut info) => {
346 let (cache_size, cache_key) = info.cache_key
347 .as_ref()
348 .expect("bug: no cache key set")
349 .clone();
350 let blur_radius_dp = cache_key.blur_radius_dp as f32;
351 let clip_data_address = gpu_cache.get_address(&info.clip_data_handle);
352
353 // Request a cacheable render task with a blurred, minimal
354 // sized box-shadow rect.
355 info.cache_item = resource_cache.request_render_task(
356 RenderTaskCacheKey {
357 size: cache_size,
358 kind: RenderTaskCacheKeyKind::BoxShadow(cache_key),
359 },
360 gpu_cache,
361 render_tasks,
362 |render_tasks| {
363 // Draw the rounded rect.
364 let mask_task = RenderTask::new_rounded_rect_mask(
365 cache_size,
366 clip_data_address,
367 );
368
369 let mask_task_id = render_tasks.add(mask_task);
370
371 // Blur it
372 let blur_render_task = RenderTask::new_blur(
373 blur_radius_dp,
374 mask_task_id,
375 render_tasks,
376 RenderTargetKind::Alpha,
377 ClearMode::Zero,
378 PremultipliedColorF::TRANSPARENT,
379 );
380
381 let root_task_id = render_tasks.add(blur_render_task);
382 children.push(root_task_id);
383
384 (root_task_id, false)
385 }
386 );
387 }
388 ClipSource::Rectangle(..) |
389 ClipSource::RoundedRectangle(..) |
390 ClipSource::Image(..) |
391 ClipSource::BorderCorner(..) => {}
392 }
393 }
394 }
395
396 RenderTask {
397 children,
398 location: RenderTaskLocation::Dynamic(None, outer_rect.size),
399 kind: RenderTaskKind::CacheMask(CacheMaskTask {
400 actual_rect: outer_rect,
401 clips,
402 coordinate_system_id: prim_coordinate_system_id,
403 }),
404 clear_mode: ClearMode::One,
405 saved_index: None,
406 }
407 }
408
new_rounded_rect_mask( size: DeviceIntSize, clip_data_address: GpuCacheAddress, ) -> Self409 pub fn new_rounded_rect_mask(
410 size: DeviceIntSize,
411 clip_data_address: GpuCacheAddress,
412 ) -> Self {
413 RenderTask {
414 children: Vec::new(),
415 location: RenderTaskLocation::Dynamic(None, size),
416 kind: RenderTaskKind::ClipRegion(ClipRegionTask {
417 clip_data_address,
418 }),
419 clear_mode: ClearMode::One,
420 saved_index: None,
421 }
422 }
423
424 // Construct a render task to apply a blur to a primitive.
425 // The render task chain that is constructed looks like:
426 //
427 // PrimitiveCacheTask: Draw the primitives.
428 // ^
429 // |
430 // DownscalingTask(s): Each downscaling task reduces the size of render target to
431 // ^ half. Also reduce the std deviation to half until the std
432 // | deviation less than 4.0.
433 // |
434 // |
435 // VerticalBlurTask: Apply the separable vertical blur to the primitive.
436 // ^
437 // |
438 // HorizontalBlurTask: Apply the separable horizontal blur to the vertical blur.
439 // |
440 // +---- This is stored as the input task to the primitive shader.
441 //
new_blur( blur_std_deviation: f32, src_task_id: RenderTaskId, render_tasks: &mut RenderTaskTree, target_kind: RenderTargetKind, clear_mode: ClearMode, color: PremultipliedColorF, ) -> Self442 pub fn new_blur(
443 blur_std_deviation: f32,
444 src_task_id: RenderTaskId,
445 render_tasks: &mut RenderTaskTree,
446 target_kind: RenderTargetKind,
447 clear_mode: ClearMode,
448 color: PremultipliedColorF,
449 ) -> Self {
450 // Adjust large std deviation value.
451 let mut adjusted_blur_std_deviation = blur_std_deviation;
452 let blur_target_size = render_tasks[src_task_id].get_dynamic_size();
453 let mut adjusted_blur_target_size = blur_target_size;
454 let mut downscaling_src_task_id = src_task_id;
455 let mut scale_factor = 1.0;
456 while adjusted_blur_std_deviation > MAX_BLUR_STD_DEVIATION {
457 if adjusted_blur_target_size.width < MIN_DOWNSCALING_RT_SIZE ||
458 adjusted_blur_target_size.height < MIN_DOWNSCALING_RT_SIZE {
459 break;
460 }
461 adjusted_blur_std_deviation *= 0.5;
462 scale_factor *= 2.0;
463 adjusted_blur_target_size = (blur_target_size.to_f32() / scale_factor).to_i32();
464 let downscaling_task = RenderTask::new_scaling(
465 target_kind,
466 downscaling_src_task_id,
467 adjusted_blur_target_size,
468 );
469 downscaling_src_task_id = render_tasks.add(downscaling_task);
470 }
471
472 let blur_task_v = RenderTask {
473 children: vec![downscaling_src_task_id],
474 location: RenderTaskLocation::Dynamic(None, adjusted_blur_target_size),
475 kind: RenderTaskKind::VerticalBlur(BlurTask {
476 blur_std_deviation: adjusted_blur_std_deviation,
477 target_kind,
478 color,
479 uv_rect_handle: GpuCacheHandle::new(),
480 }),
481 clear_mode,
482 saved_index: None,
483 };
484
485 let blur_task_v_id = render_tasks.add(blur_task_v);
486
487 let blur_task_h = RenderTask {
488 children: vec![blur_task_v_id],
489 location: RenderTaskLocation::Dynamic(None, adjusted_blur_target_size),
490 kind: RenderTaskKind::HorizontalBlur(BlurTask {
491 blur_std_deviation: adjusted_blur_std_deviation,
492 target_kind,
493 color,
494 uv_rect_handle: GpuCacheHandle::new(),
495 }),
496 clear_mode,
497 saved_index: None,
498 };
499
500 blur_task_h
501 }
502
new_scaling( target_kind: RenderTargetKind, src_task_id: RenderTaskId, target_size: DeviceIntSize, ) -> Self503 pub fn new_scaling(
504 target_kind: RenderTargetKind,
505 src_task_id: RenderTaskId,
506 target_size: DeviceIntSize,
507 ) -> Self {
508 RenderTask {
509 children: vec![src_task_id],
510 location: RenderTaskLocation::Dynamic(None, target_size),
511 kind: RenderTaskKind::Scaling(target_kind),
512 clear_mode: match target_kind {
513 RenderTargetKind::Color => ClearMode::Transparent,
514 RenderTargetKind::Alpha => ClearMode::One,
515 },
516 saved_index: None,
517 }
518 }
519
520 // Write (up to) 8 floats of data specific to the type
521 // of render task that is provided to the GPU shaders
522 // via a vertex texture.
write_task_data(&self) -> RenderTaskData523 pub fn write_task_data(&self) -> RenderTaskData {
524 // NOTE: The ordering and layout of these structures are
525 // required to match both the GPU structures declared
526 // in prim_shared.glsl, and also the uses in submit_batch()
527 // in renderer.rs.
528 // TODO(gw): Maybe there's a way to make this stuff a bit
529 // more type-safe. Although, it will always need
530 // to be kept in sync with the GLSL code anyway.
531
532 let (data1, data2) = match self.kind {
533 RenderTaskKind::Picture(ref task) => {
534 (
535 // Note: has to match `PICTURE_TYPE_*` in shaders
536 // TODO(gw): Instead of using the sign of the picture
537 // type here, we should consider encoding it
538 // as a set of flags that get casted here
539 // and in the shader. This is a bit tidier
540 // and allows for future expansion of flags.
541 match task.content_origin {
542 ContentOrigin::Local(point) => [
543 point.x, point.y, task.pic_type as u32 as f32,
544 ],
545 ContentOrigin::Screen(point) => [
546 point.x as f32, point.y as f32, -(task.pic_type as u32 as f32),
547 ],
548 },
549 task.color.to_array()
550 )
551 }
552 RenderTaskKind::CacheMask(ref task) => {
553 (
554 [
555 task.actual_rect.origin.x as f32,
556 task.actual_rect.origin.y as f32,
557 RasterizationSpace::Screen as i32 as f32,
558 ],
559 [0.0; 4],
560 )
561 }
562 RenderTaskKind::ClipRegion(..) => {
563 (
564 [
565 0.0,
566 0.0,
567 RasterizationSpace::Local as i32 as f32,
568 ],
569 [0.0; 4],
570 )
571 }
572 RenderTaskKind::VerticalBlur(ref task) |
573 RenderTaskKind::HorizontalBlur(ref task) => {
574 (
575 [
576 task.blur_std_deviation,
577 0.0,
578 0.0,
579 ],
580 task.color.to_array()
581 )
582 }
583 RenderTaskKind::Readback(..) |
584 RenderTaskKind::Scaling(..) |
585 RenderTaskKind::Blit(..) => {
586 (
587 [0.0; 3],
588 [0.0; 4],
589 )
590 }
591 };
592
593 let (target_rect, target_index) = self.get_target_rect();
594
595 RenderTaskData {
596 data: [
597 target_rect.origin.x as f32,
598 target_rect.origin.y as f32,
599 target_rect.size.width as f32,
600 target_rect.size.height as f32,
601 target_index.0 as f32,
602 data1[0],
603 data1[1],
604 data1[2],
605 data2[0],
606 data2[1],
607 data2[2],
608 data2[3],
609 ]
610 }
611 }
612
get_texture_handle(&self) -> &GpuCacheHandle613 pub fn get_texture_handle(&self) -> &GpuCacheHandle {
614 match self.kind {
615 RenderTaskKind::Picture(ref info) => {
616 &info.uv_rect_handle
617 }
618 RenderTaskKind::VerticalBlur(ref info) |
619 RenderTaskKind::HorizontalBlur(ref info) => {
620 &info.uv_rect_handle
621 }
622 RenderTaskKind::ClipRegion(..) |
623 RenderTaskKind::Readback(..) |
624 RenderTaskKind::Scaling(..) |
625 RenderTaskKind::Blit(..) |
626 RenderTaskKind::CacheMask(..) => {
627 panic!("texture handle not supported for this task kind");
628 }
629 }
630 }
631
get_dynamic_size(&self) -> DeviceIntSize632 pub fn get_dynamic_size(&self) -> DeviceIntSize {
633 match self.location {
634 RenderTaskLocation::Fixed(..) => DeviceIntSize::zero(),
635 RenderTaskLocation::Dynamic(_, size) => size,
636 RenderTaskLocation::TextureCache(_, _, rect) => rect.size,
637 }
638 }
639
get_target_rect(&self) -> (DeviceIntRect, RenderTargetIndex)640 pub fn get_target_rect(&self) -> (DeviceIntRect, RenderTargetIndex) {
641 match self.location {
642 RenderTaskLocation::Fixed(rect) => {
643 (rect, RenderTargetIndex(0))
644 }
645 // Previously, we only added render tasks after the entire
646 // primitive chain was determined visible. This meant that
647 // we could assert any render task in the list was also
648 // allocated (assigned to passes). Now, we add render
649 // tasks earlier, and the picture they belong to may be
650 // culled out later, so we can't assert that the task
651 // has been allocated.
652 // Render tasks that are created but not assigned to
653 // passes consume a row in the render task texture, but
654 // don't allocate any space in render targets nor
655 // draw any pixels.
656 // TODO(gw): Consider some kind of tag or other method
657 // to mark a task as unused explicitly. This
658 // would allow us to restore this debug check.
659 RenderTaskLocation::Dynamic(Some((origin, target_index)), size) => {
660 (DeviceIntRect::new(origin, size), target_index)
661 }
662 RenderTaskLocation::Dynamic(None, _) => {
663 (DeviceIntRect::zero(), RenderTargetIndex(0))
664 }
665 RenderTaskLocation::TextureCache(_, layer, rect) => {
666 (rect, RenderTargetIndex(layer as usize))
667 }
668 }
669 }
670
target_kind(&self) -> RenderTargetKind671 pub fn target_kind(&self) -> RenderTargetKind {
672 match self.kind {
673 RenderTaskKind::Readback(..) => RenderTargetKind::Color,
674
675 RenderTaskKind::ClipRegion(..) |
676 RenderTaskKind::CacheMask(..) => {
677 RenderTargetKind::Alpha
678 }
679
680 RenderTaskKind::VerticalBlur(ref task_info) |
681 RenderTaskKind::HorizontalBlur(ref task_info) => {
682 task_info.target_kind
683 }
684
685 RenderTaskKind::Scaling(target_kind) => {
686 target_kind
687 }
688
689 RenderTaskKind::Picture(ref task_info) => {
690 task_info.target_kind
691 }
692
693 RenderTaskKind::Blit(..) => {
694 RenderTargetKind::Color
695 }
696 }
697 }
698
699 // Check if this task wants to be made available as an input
700 // to all passes (except the first) in the render task tree.
701 // To qualify for this, the task needs to have no children / dependencies.
702 // Currently, this is only supported for A8 targets, but it can be
703 // trivially extended to also support RGBA8 targets in the future
704 // if we decide that is useful.
is_shared(&self) -> bool705 pub fn is_shared(&self) -> bool {
706 match self.kind {
707 RenderTaskKind::Picture(..) |
708 RenderTaskKind::VerticalBlur(..) |
709 RenderTaskKind::Readback(..) |
710 RenderTaskKind::HorizontalBlur(..) |
711 RenderTaskKind::Scaling(..) |
712 RenderTaskKind::ClipRegion(..) |
713 RenderTaskKind::Blit(..) => false,
714
715 // TODO(gw): For now, we've disabled the shared clip mask
716 // optimization. It's of dubious value in the
717 // future once we start to cache clip tasks anyway.
718 // I have left shared texture support here though,
719 // just in case we want it in the future.
720 RenderTaskKind::CacheMask(..) => false,
721 }
722 }
723
prepare_for_render( &mut self, gpu_cache: &mut GpuCache, )724 pub fn prepare_for_render(
725 &mut self,
726 gpu_cache: &mut GpuCache,
727 ) {
728 let (target_rect, target_index) = self.get_target_rect();
729
730 let (cache_handle, color) = match self.kind {
731 RenderTaskKind::HorizontalBlur(ref mut info) |
732 RenderTaskKind::VerticalBlur(ref mut info) => {
733 (&mut info.uv_rect_handle, info.color)
734 }
735 RenderTaskKind::Picture(ref mut info) => {
736 (&mut info.uv_rect_handle, info.color)
737 }
738 RenderTaskKind::Readback(..) |
739 RenderTaskKind::Scaling(..) |
740 RenderTaskKind::Blit(..) |
741 RenderTaskKind::ClipRegion(..) |
742 RenderTaskKind::CacheMask(..) => {
743 return;
744 }
745 };
746
747 if let Some(mut request) = gpu_cache.request(cache_handle) {
748 let image_source = ImageSource {
749 p0: target_rect.origin.to_f32(),
750 p1: target_rect.bottom_right().to_f32(),
751 color,
752 texture_layer: target_index.0 as f32,
753 user_data: [0.0; 3],
754 };
755 image_source.write_gpu_blocks(&mut request);
756 }
757 }
758
759 #[cfg(feature = "debugger")]
print_with<T: PrintTreePrinter>(&self, pt: &mut T, tree: &RenderTaskTree) -> bool760 pub fn print_with<T: PrintTreePrinter>(&self, pt: &mut T, tree: &RenderTaskTree) -> bool {
761 match self.kind {
762 RenderTaskKind::Picture(ref task) => {
763 pt.new_level(format!("Picture of {:?}", task.prim_index));
764 pt.add_item(format!("kind: {:?}", task.target_kind));
765 }
766 RenderTaskKind::CacheMask(ref task) => {
767 pt.new_level(format!("CacheMask with {} clips", task.clips.len()));
768 pt.add_item(format!("rect: {:?}", task.actual_rect));
769 }
770 RenderTaskKind::ClipRegion(..) => {
771 pt.new_level("ClipRegion".to_owned());
772 }
773 RenderTaskKind::VerticalBlur(ref task) => {
774 pt.new_level("VerticalBlur".to_owned());
775 task.print_with(pt);
776 }
777 RenderTaskKind::HorizontalBlur(ref task) => {
778 pt.new_level("HorizontalBlur".to_owned());
779 task.print_with(pt);
780 }
781 RenderTaskKind::Readback(ref rect) => {
782 pt.new_level("Readback".to_owned());
783 pt.add_item(format!("rect: {:?}", rect));
784 }
785 RenderTaskKind::Scaling(ref kind) => {
786 pt.new_level("Scaling".to_owned());
787 pt.add_item(format!("kind: {:?}", kind));
788 }
789 RenderTaskKind::Blit(ref task) => {
790 pt.new_level("Blit".to_owned());
791 pt.add_item(format!("source: {:?}", task.source));
792 }
793 }
794
795 pt.add_item(format!("clear to: {:?}", self.clear_mode));
796
797 for &child_id in &self.children {
798 if tree[child_id].print_with(pt, tree) {
799 pt.add_item(format!("self: {:?}", child_id))
800 }
801 }
802
803 pt.end_level();
804 true
805 }
806
807 /// Mark this render task for keeping the results alive up until the end of the frame.
mark_for_saving(&mut self)808 pub fn mark_for_saving(&mut self) {
809 match self.location {
810 RenderTaskLocation::Fixed(..) |
811 RenderTaskLocation::Dynamic(..) => {
812 self.saved_index = Some(SavedTargetIndex::PENDING);
813 }
814 RenderTaskLocation::TextureCache(..) => {
815 panic!("Unable to mark a permanently cached task for saving!");
816 }
817 }
818 }
819 }
820
821 #[derive(Debug, Hash, PartialEq, Eq)]
822 #[cfg_attr(feature = "capture", derive(Serialize))]
823 #[cfg_attr(feature = "replay", derive(Deserialize))]
824 pub enum RenderTaskCacheKeyKind {
825 BoxShadow(BoxShadowCacheKey),
826 Image(ImageCacheKey),
827 }
828
829 #[derive(Debug, Hash, PartialEq, Eq)]
830 #[cfg_attr(feature = "capture", derive(Serialize))]
831 #[cfg_attr(feature = "replay", derive(Deserialize))]
832 pub struct RenderTaskCacheKey {
833 pub size: DeviceIntSize,
834 pub kind: RenderTaskCacheKeyKind,
835 }
836
837 #[cfg_attr(feature = "capture", derive(Serialize))]
838 #[cfg_attr(feature = "replay", derive(Deserialize))]
839 struct RenderTaskCacheEntry {
840 handle: TextureCacheHandle,
841 }
842
843 // A cache of render tasks that are stored in the texture
844 // cache for usage across frames.
845 #[cfg_attr(feature = "capture", derive(Serialize))]
846 #[cfg_attr(feature = "replay", derive(Deserialize))]
847 pub struct RenderTaskCache {
848 entries: FastHashMap<RenderTaskCacheKey, RenderTaskCacheEntry>,
849 }
850
851 impl RenderTaskCache {
new() -> Self852 pub fn new() -> Self {
853 RenderTaskCache {
854 entries: FastHashMap::default(),
855 }
856 }
857
clear(&mut self)858 pub fn clear(&mut self) {
859 self.entries.clear();
860 }
861
begin_frame( &mut self, texture_cache: &mut TextureCache, )862 pub fn begin_frame(
863 &mut self,
864 texture_cache: &mut TextureCache,
865 ) {
866 // Drop any items from the cache that have been
867 // evicted from the texture cache.
868 //
869 // This isn't actually necessary for the texture
870 // cache to be able to evict old render tasks.
871 // It will evict render tasks as required, since
872 // the access time in the texture cache entry will
873 // be stale if this task hasn't been requested
874 // for a while.
875 //
876 // Nonetheless, we should remove stale entries
877 // from here so that this hash map doesn't
878 // grow indefinitely!
879 self.entries.retain(|_, value| {
880 texture_cache.is_allocated(&value.handle)
881 });
882 }
883
request_render_task<F>( &mut self, key: RenderTaskCacheKey, texture_cache: &mut TextureCache, gpu_cache: &mut GpuCache, render_tasks: &mut RenderTaskTree, mut f: F, ) -> CacheItem where F: FnMut(&mut RenderTaskTree) -> (RenderTaskId, bool)884 pub fn request_render_task<F>(
885 &mut self,
886 key: RenderTaskCacheKey,
887 texture_cache: &mut TextureCache,
888 gpu_cache: &mut GpuCache,
889 render_tasks: &mut RenderTaskTree,
890 mut f: F,
891 ) -> CacheItem where F: FnMut(&mut RenderTaskTree) -> (RenderTaskId, bool) {
892 // Get the texture cache handle for this cache key,
893 // or create one.
894 let cache_entry = self.entries
895 .entry(key)
896 .or_insert(RenderTaskCacheEntry {
897 handle: TextureCacheHandle::new(),
898 });
899
900 // Check if this texture cache handle is valie.
901 if texture_cache.request(&mut cache_entry.handle, gpu_cache) {
902 // Invoke user closure to get render task chain
903 // to draw this into the texture cache.
904 let (render_task_id, is_opaque) = f(render_tasks);
905 let render_task = &mut render_tasks[render_task_id];
906
907 // Select the right texture page to allocate from.
908 let image_format = match render_task.target_kind() {
909 RenderTargetKind::Color => ImageFormat::BGRA8,
910 RenderTargetKind::Alpha => ImageFormat::R8,
911 };
912
913 // Find out what size to alloc in the texture cache.
914 let size = match render_task.location {
915 RenderTaskLocation::Fixed(..) |
916 RenderTaskLocation::TextureCache(..) => {
917 panic!("BUG: dynamic task was expected");
918 }
919 RenderTaskLocation::Dynamic(_, size) => size,
920 };
921
922 // TODO(gw): Support color tasks in the texture cache,
923 // and perhaps consider if we can determine
924 // if some tasks are opaque as an optimization.
925 let descriptor = ImageDescriptor::new(
926 size.width as u32,
927 size.height as u32,
928 image_format,
929 is_opaque,
930 );
931
932 // Allocate space in the texture cache, but don't supply
933 // and CPU-side data to be uploaded.
934 texture_cache.update(
935 &mut cache_entry.handle,
936 descriptor,
937 TextureFilter::Linear,
938 None,
939 [0.0; 3],
940 None,
941 gpu_cache,
942 );
943
944 // Get the allocation details in the texture cache, and store
945 // this in the render task. The renderer will draw this
946 // task into the appropriate layer and rect of the texture
947 // cache on this frame.
948 let (texture_id, texture_layer, uv_rect) =
949 texture_cache.get_cache_location(&cache_entry.handle);
950
951 render_task.location = RenderTaskLocation::TextureCache(
952 texture_id,
953 texture_layer,
954 uv_rect.to_i32()
955 );
956 }
957
958 // Finally, return the texture cache handle that we know
959 // is now up to date.
960 texture_cache.get(&cache_entry.handle)
961 }
962 }
963
964 // TODO(gw): Rounding the content rect here to device pixels is not
965 // technically correct. Ideally we should ceil() here, and ensure that
966 // the extra part pixel in the case of fractional sizes is correctly
967 // handled. For now, just use rounding which passes the existing
968 // Gecko tests.
969 // Note: zero-square tasks are prohibited in WR task tree, so
970 // we ensure each dimension to be at least the length of 1 after rounding.
to_cache_size(size: DeviceSize) -> DeviceIntSize971 pub fn to_cache_size(size: DeviceSize) -> DeviceIntSize {
972 DeviceIntSize::new(
973 1.max(size.width.round() as i32),
974 1.max(size.height.round() as i32),
975 )
976 }
977