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
6 use api::units::*;
7 use api::{ColorF, ImageFormat, LineOrientation, BorderStyle};
8 use crate::batch::{AlphaBatchBuilder, AlphaBatchContainer, BatchTextures};
9 use crate::batch::{ClipBatcher, BatchBuilder};
10 use crate::spatial_tree::SpatialTree;
11 use crate::clip::ClipStore;
12 use crate::composite::CompositeState;
13 use crate::frame_builder::{FrameGlobalResources};
14 use crate::gpu_cache::{GpuCache, GpuCacheAddress};
15 use crate::gpu_types::{BorderInstance, SvgFilterInstance, BlurDirection, BlurInstance, PrimitiveHeaders, ScalingInstance};
16 use crate::gpu_types::{TransformPalette, ZBufferIdGenerator};
17 use crate::internal_types::{FastHashMap, TextureSource, CacheTextureId};
18 use crate::picture::{SliceId, SurfaceInfo, ResolvedSurfaceTexture, TileCacheInstance};
19 use crate::prim_store::{PrimitiveInstance, PrimitiveStore, DeferredResolve, PrimitiveScratchBuffer};
20 use crate::prim_store::gradient::{
21 FastLinearGradientInstance, LinearGradientInstance, RadialGradientInstance,
22 ConicGradientInstance,
23 };
24 use crate::render_backend::DataStores;
25 use crate::render_task::{RenderTaskKind, RenderTaskAddress};
26 use crate::render_task::{RenderTask, ScalingTask, SvgFilterInfo};
27 use crate::render_task_graph::{RenderTaskGraph, RenderTaskId};
28 use crate::resource_cache::ResourceCache;
29 use crate::spatial_tree::SpatialNodeIndex;
30
31
32 const STYLE_SOLID: i32 = ((BorderStyle::Solid as i32) << 8) | ((BorderStyle::Solid as i32) << 16);
33 const STYLE_MASK: i32 = 0x00FF_FF00;
34
35 /// A tag used to identify the output format of a `RenderTarget`.
36 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
37 #[cfg_attr(feature = "capture", derive(Serialize))]
38 #[cfg_attr(feature = "replay", derive(Deserialize))]
39 pub enum RenderTargetKind {
40 Color, // RGBA8
41 Alpha, // R8
42 }
43
44 /// Identifies a given `RenderTarget` in a `RenderTargetList`.
45 #[derive(Debug, Copy, Clone)]
46 #[cfg_attr(feature = "capture", derive(Serialize))]
47 #[cfg_attr(feature = "replay", derive(Deserialize))]
48 pub struct RenderTargetIndex(pub usize);
49
50 pub struct RenderTargetContext<'a, 'rc> {
51 pub global_device_pixel_scale: DevicePixelScale,
52 pub prim_store: &'a PrimitiveStore,
53 pub resource_cache: &'rc mut ResourceCache,
54 pub use_dual_source_blending: bool,
55 pub use_advanced_blending: bool,
56 pub break_advanced_blend_batches: bool,
57 pub batch_lookback_count: usize,
58 pub spatial_tree: &'a SpatialTree,
59 pub data_stores: &'a DataStores,
60 pub surfaces: &'a [SurfaceInfo],
61 pub scratch: &'a PrimitiveScratchBuffer,
62 pub screen_world_rect: WorldRect,
63 pub globals: &'a FrameGlobalResources,
64 pub tile_caches: &'a FastHashMap<SliceId, Box<TileCacheInstance>>,
65 pub root_spatial_node_index: SpatialNodeIndex,
66 }
67
68 /// Represents a number of rendering operations on a surface.
69 ///
70 /// In graphics parlance, a "render target" usually means "a surface (texture or
71 /// framebuffer) bound to the output of a shader". This trait has a slightly
72 /// different meaning, in that it represents the operations on that surface
73 /// _before_ it's actually bound and rendered. So a `RenderTarget` is built by
74 /// the `RenderBackend` by inserting tasks, and then shipped over to the
75 /// `Renderer` where a device surface is resolved and the tasks are transformed
76 /// into draw commands on that surface.
77 ///
78 /// We express this as a trait to generalize over color and alpha surfaces.
79 /// a given `RenderTask` will draw to one or the other, depending on its type
80 /// and sometimes on its parameters. See `RenderTask::target_kind`.
81 pub trait RenderTarget {
82 /// Creates a new RenderTarget of the given type.
new( texture_id: CacheTextureId, screen_size: DeviceIntSize, gpu_supports_fast_clears: bool, used_rect: DeviceIntRect, ) -> Self83 fn new(
84 texture_id: CacheTextureId,
85 screen_size: DeviceIntSize,
86 gpu_supports_fast_clears: bool,
87 used_rect: DeviceIntRect,
88 ) -> Self;
89
90 /// Optional hook to provide additional processing for the target at the
91 /// end of the build phase.
build( &mut self, _ctx: &mut RenderTargetContext, _gpu_cache: &mut GpuCache, _render_tasks: &RenderTaskGraph, _deferred_resolves: &mut Vec<DeferredResolve>, _prim_headers: &mut PrimitiveHeaders, _transforms: &mut TransformPalette, _z_generator: &mut ZBufferIdGenerator, _composite_state: &mut CompositeState, _prim_instances: &[PrimitiveInstance], )92 fn build(
93 &mut self,
94 _ctx: &mut RenderTargetContext,
95 _gpu_cache: &mut GpuCache,
96 _render_tasks: &RenderTaskGraph,
97 _deferred_resolves: &mut Vec<DeferredResolve>,
98 _prim_headers: &mut PrimitiveHeaders,
99 _transforms: &mut TransformPalette,
100 _z_generator: &mut ZBufferIdGenerator,
101 _composite_state: &mut CompositeState,
102 _prim_instances: &[PrimitiveInstance],
103 ) {
104 }
105
106 /// Associates a `RenderTask` with this target. That task must be assigned
107 /// to a region returned by invoking `allocate()` on this target.
108 ///
109 /// TODO(gw): It's a bit odd that we need the deferred resolves and mutable
110 /// GPU cache here. They are typically used by the build step above. They
111 /// are used for the blit jobs to allow resolve_image to be called. It's a
112 /// bit of extra overhead to store the image key here and the resolve them
113 /// in the build step separately. BUT: if/when we add more texture cache
114 /// target jobs, we might want to tidy this up.
add_task( &mut self, task_id: RenderTaskId, ctx: &RenderTargetContext, gpu_cache: &mut GpuCache, render_tasks: &RenderTaskGraph, clip_store: &ClipStore, transforms: &mut TransformPalette, )115 fn add_task(
116 &mut self,
117 task_id: RenderTaskId,
118 ctx: &RenderTargetContext,
119 gpu_cache: &mut GpuCache,
120 render_tasks: &RenderTaskGraph,
121 clip_store: &ClipStore,
122 transforms: &mut TransformPalette,
123 );
124
needs_depth(&self) -> bool125 fn needs_depth(&self) -> bool;
texture_id(&self) -> CacheTextureId126 fn texture_id(&self) -> CacheTextureId;
127 }
128
129 /// A series of `RenderTarget` instances, serving as the high-level container
130 /// into which `RenderTasks` are assigned.
131 ///
132 /// During the build phase, we iterate over the tasks in each `RenderPass`. For
133 /// each task, we invoke `allocate()` on the `RenderTargetList`, which in turn
134 /// attempts to allocate an output region in the last `RenderTarget` in the
135 /// list. If allocation fails (or if the list is empty), a new `RenderTarget` is
136 /// created and appended to the list. The build phase then assign the task into
137 /// the target associated with the final allocation.
138 ///
139 /// The result is that each `RenderPass` is associated with one or two
140 /// `RenderTargetLists`, depending on whether we have all our tasks have the
141 /// same `RenderTargetKind`. The lists are then shipped to the `Renderer`, which
142 /// allocates a device texture array, with one slice per render target in the
143 /// list.
144 ///
145 /// The upshot of this scheme is that it maximizes batching. In a given pass,
146 /// we need to do a separate batch for each individual render target. But with
147 /// the texture array, we can expose the entirety of the previous pass to each
148 /// task in the current pass in a single batch, which generally allows each
149 /// task to be drawn in a single batch regardless of how many results from the
150 /// previous pass it depends on.
151 ///
152 /// Note that in some cases (like drop-shadows), we can depend on the output of
153 /// a pass earlier than the immediately-preceding pass.
154 #[cfg_attr(feature = "capture", derive(Serialize))]
155 #[cfg_attr(feature = "replay", derive(Deserialize))]
156 pub struct RenderTargetList<T> {
157 pub format: ImageFormat,
158 pub targets: Vec<T>,
159 }
160
161 impl<T: RenderTarget> RenderTargetList<T> {
new( format: ImageFormat, ) -> Self162 pub fn new(
163 format: ImageFormat,
164 ) -> Self {
165 RenderTargetList {
166 format,
167 targets: Vec::new(),
168 }
169 }
170
build( &mut self, ctx: &mut RenderTargetContext, gpu_cache: &mut GpuCache, render_tasks: &RenderTaskGraph, deferred_resolves: &mut Vec<DeferredResolve>, prim_headers: &mut PrimitiveHeaders, transforms: &mut TransformPalette, z_generator: &mut ZBufferIdGenerator, composite_state: &mut CompositeState, prim_instances: &[PrimitiveInstance], )171 pub fn build(
172 &mut self,
173 ctx: &mut RenderTargetContext,
174 gpu_cache: &mut GpuCache,
175 render_tasks: &RenderTaskGraph,
176 deferred_resolves: &mut Vec<DeferredResolve>,
177 prim_headers: &mut PrimitiveHeaders,
178 transforms: &mut TransformPalette,
179 z_generator: &mut ZBufferIdGenerator,
180 composite_state: &mut CompositeState,
181 prim_instances: &[PrimitiveInstance],
182 ) {
183 if self.targets.is_empty() {
184 return;
185 }
186
187 for target in &mut self.targets {
188 target.build(
189 ctx,
190 gpu_cache,
191 render_tasks,
192 deferred_resolves,
193 prim_headers,
194 transforms,
195 z_generator,
196 composite_state,
197 prim_instances,
198 );
199 }
200 }
201
needs_depth(&self) -> bool202 pub fn needs_depth(&self) -> bool {
203 self.targets.iter().any(|target| target.needs_depth())
204 }
205 }
206
207
208 /// Contains the work (in the form of instance arrays) needed to fill a color
209 /// color output surface (RGBA8).
210 ///
211 /// See `RenderTarget`.
212 #[cfg_attr(feature = "capture", derive(Serialize))]
213 #[cfg_attr(feature = "replay", derive(Deserialize))]
214 pub struct ColorRenderTarget {
215 pub alpha_batch_containers: Vec<AlphaBatchContainer>,
216 // List of blur operations to apply for this render target.
217 pub vertical_blurs: FastHashMap<TextureSource, Vec<BlurInstance>>,
218 pub horizontal_blurs: FastHashMap<TextureSource, Vec<BlurInstance>>,
219 pub scalings: FastHashMap<TextureSource, Vec<ScalingInstance>>,
220 pub svg_filters: Vec<(BatchTextures, Vec<SvgFilterInstance>)>,
221 pub blits: Vec<BlitJob>,
222 alpha_tasks: Vec<RenderTaskId>,
223 screen_size: DeviceIntSize,
224 pub texture_id: CacheTextureId,
225 // Track the used rect of the render target, so that
226 // we can set a scissor rect and only clear to the
227 // used portion of the target as an optimization.
228 pub used_rect: DeviceIntRect,
229 }
230
231 impl RenderTarget for ColorRenderTarget {
new( texture_id: CacheTextureId, screen_size: DeviceIntSize, _: bool, used_rect: DeviceIntRect, ) -> Self232 fn new(
233 texture_id: CacheTextureId,
234 screen_size: DeviceIntSize,
235 _: bool,
236 used_rect: DeviceIntRect,
237 ) -> Self {
238 ColorRenderTarget {
239 alpha_batch_containers: Vec::new(),
240 vertical_blurs: FastHashMap::default(),
241 horizontal_blurs: FastHashMap::default(),
242 scalings: FastHashMap::default(),
243 svg_filters: Vec::new(),
244 blits: Vec::new(),
245 alpha_tasks: Vec::new(),
246 screen_size,
247 texture_id,
248 used_rect,
249 }
250 }
251
build( &mut self, ctx: &mut RenderTargetContext, gpu_cache: &mut GpuCache, render_tasks: &RenderTaskGraph, deferred_resolves: &mut Vec<DeferredResolve>, prim_headers: &mut PrimitiveHeaders, transforms: &mut TransformPalette, z_generator: &mut ZBufferIdGenerator, composite_state: &mut CompositeState, prim_instances: &[PrimitiveInstance], )252 fn build(
253 &mut self,
254 ctx: &mut RenderTargetContext,
255 gpu_cache: &mut GpuCache,
256 render_tasks: &RenderTaskGraph,
257 deferred_resolves: &mut Vec<DeferredResolve>,
258 prim_headers: &mut PrimitiveHeaders,
259 transforms: &mut TransformPalette,
260 z_generator: &mut ZBufferIdGenerator,
261 composite_state: &mut CompositeState,
262 prim_instances: &[PrimitiveInstance],
263 ) {
264 profile_scope!("build");
265 let mut merged_batches = AlphaBatchContainer::new(None);
266
267 for task_id in &self.alpha_tasks {
268 profile_scope!("alpha_task");
269 let task = &render_tasks[*task_id];
270
271 match task.kind {
272 RenderTaskKind::Picture(ref pic_task) => {
273 let pic = &ctx.prim_store.pictures[pic_task.pic_index.0];
274
275 let raster_spatial_node_index = match pic.raster_config {
276 Some(ref raster_config) => {
277 let surface = &ctx.surfaces[raster_config.surface_index.0];
278 surface.raster_spatial_node_index
279 }
280 None => {
281 // This must be the main framebuffer
282 ctx.root_spatial_node_index
283 }
284 };
285
286 let target_rect = task.get_target_rect();
287
288 let scissor_rect = if pic_task.can_merge {
289 None
290 } else {
291 Some(target_rect)
292 };
293
294 // Typical workloads have a single or a few batch builders with a
295 // large number of batches (regular pictres) and a higher number
296 // of batch builders with only a single or two batches (for example
297 // rendering isolated primitives to compute their shadows).
298 // We can easily guess which category we are in for each picture
299 // by checking whether it has multiple clusters.
300 let prealloc_batch_count = if pic.prim_list.clusters.len() > 1 {
301 128
302 } else {
303 0
304 };
305
306 // TODO(gw): The type names of AlphaBatchBuilder and BatchBuilder
307 // are still confusing. Once more of the picture caching
308 // improvement code lands, the AlphaBatchBuilder and
309 // AlphaBatchList types will be collapsed into one, which
310 // should simplify coming up with better type names.
311 let alpha_batch_builder = AlphaBatchBuilder::new(
312 self.screen_size,
313 ctx.break_advanced_blend_batches,
314 ctx.batch_lookback_count,
315 *task_id,
316 (*task_id).into(),
317 None,
318 prealloc_batch_count,
319 );
320
321 let mut batch_builder = BatchBuilder::new(
322 vec![alpha_batch_builder],
323 );
324
325 batch_builder.add_pic_to_batch(
326 pic,
327 ctx,
328 gpu_cache,
329 render_tasks,
330 deferred_resolves,
331 prim_headers,
332 transforms,
333 raster_spatial_node_index,
334 pic_task.surface_spatial_node_index,
335 z_generator,
336 composite_state,
337 prim_instances,
338 );
339
340 let alpha_batch_builders = batch_builder.finalize();
341
342 for batcher in alpha_batch_builders {
343 batcher.build(
344 &mut self.alpha_batch_containers,
345 &mut merged_batches,
346 target_rect,
347 scissor_rect,
348 );
349 }
350 }
351 _ => {
352 unreachable!();
353 }
354 }
355 }
356
357 if !merged_batches.is_empty() {
358 self.alpha_batch_containers.push(merged_batches);
359 }
360 }
361
texture_id(&self) -> CacheTextureId362 fn texture_id(&self) -> CacheTextureId {
363 self.texture_id
364 }
365
add_task( &mut self, task_id: RenderTaskId, _ctx: &RenderTargetContext, gpu_cache: &mut GpuCache, render_tasks: &RenderTaskGraph, _: &ClipStore, _: &mut TransformPalette, )366 fn add_task(
367 &mut self,
368 task_id: RenderTaskId,
369 _ctx: &RenderTargetContext,
370 gpu_cache: &mut GpuCache,
371 render_tasks: &RenderTaskGraph,
372 _: &ClipStore,
373 _: &mut TransformPalette,
374 ) {
375 profile_scope!("add_task");
376 let task = &render_tasks[task_id];
377
378 match task.kind {
379 RenderTaskKind::VerticalBlur(..) => {
380 add_blur_instances(
381 &mut self.vertical_blurs,
382 BlurDirection::Vertical,
383 task_id.into(),
384 task.children[0],
385 render_tasks,
386 );
387 }
388 RenderTaskKind::HorizontalBlur(..) => {
389 add_blur_instances(
390 &mut self.horizontal_blurs,
391 BlurDirection::Horizontal,
392 task_id.into(),
393 task.children[0],
394 render_tasks,
395 );
396 }
397 RenderTaskKind::Picture(..) => {
398 self.alpha_tasks.push(task_id);
399 }
400 RenderTaskKind::SvgFilter(ref task_info) => {
401 add_svg_filter_instances(
402 &mut self.svg_filters,
403 render_tasks,
404 &task_info.info,
405 task_id,
406 task.children.get(0).cloned(),
407 task.children.get(1).cloned(),
408 task_info.extra_gpu_cache_handle.map(|handle| gpu_cache.get_address(&handle)),
409 )
410 }
411 RenderTaskKind::Image(..) |
412 RenderTaskKind::Cached(..) |
413 RenderTaskKind::ClipRegion(..) |
414 RenderTaskKind::Border(..) |
415 RenderTaskKind::CacheMask(..) |
416 RenderTaskKind::FastLinearGradient(..) |
417 RenderTaskKind::LinearGradient(..) |
418 RenderTaskKind::RadialGradient(..) |
419 RenderTaskKind::ConicGradient(..) |
420 RenderTaskKind::LineDecoration(..) => {
421 panic!("Should not be added to color target!");
422 }
423 RenderTaskKind::Readback(..) => {}
424 RenderTaskKind::Scaling(ref info) => {
425 add_scaling_instances(
426 info,
427 &mut self.scalings,
428 task,
429 task.children.first().map(|&child| &render_tasks[child]),
430 );
431 }
432 RenderTaskKind::Blit(ref task_info) => {
433 let target_rect = task
434 .get_target_rect();
435 self.blits.push(BlitJob {
436 source: task_info.source,
437 target_rect,
438 });
439 }
440 #[cfg(test)]
441 RenderTaskKind::Test(..) => {}
442 }
443 }
444
needs_depth(&self) -> bool445 fn needs_depth(&self) -> bool {
446 self.alpha_batch_containers.iter().any(|ab| {
447 !ab.opaque_batches.is_empty()
448 })
449 }
450 }
451
452 /// Contains the work (in the form of instance arrays) needed to fill an alpha
453 /// output surface (R8).
454 ///
455 /// See `RenderTarget`.
456 #[cfg_attr(feature = "capture", derive(Serialize))]
457 #[cfg_attr(feature = "replay", derive(Deserialize))]
458 pub struct AlphaRenderTarget {
459 pub clip_batcher: ClipBatcher,
460 // List of blur operations to apply for this render target.
461 pub vertical_blurs: FastHashMap<TextureSource, Vec<BlurInstance>>,
462 pub horizontal_blurs: FastHashMap<TextureSource, Vec<BlurInstance>>,
463 pub scalings: FastHashMap<TextureSource, Vec<ScalingInstance>>,
464 pub zero_clears: Vec<RenderTaskId>,
465 pub one_clears: Vec<RenderTaskId>,
466 pub texture_id: CacheTextureId,
467 }
468
469 impl RenderTarget for AlphaRenderTarget {
new( texture_id: CacheTextureId, _: DeviceIntSize, gpu_supports_fast_clears: bool, _: DeviceIntRect, ) -> Self470 fn new(
471 texture_id: CacheTextureId,
472 _: DeviceIntSize,
473 gpu_supports_fast_clears: bool,
474 _: DeviceIntRect,
475 ) -> Self {
476 AlphaRenderTarget {
477 clip_batcher: ClipBatcher::new(gpu_supports_fast_clears),
478 vertical_blurs: FastHashMap::default(),
479 horizontal_blurs: FastHashMap::default(),
480 scalings: FastHashMap::default(),
481 zero_clears: Vec::new(),
482 one_clears: Vec::new(),
483 texture_id,
484 }
485 }
486
texture_id(&self) -> CacheTextureId487 fn texture_id(&self) -> CacheTextureId {
488 self.texture_id
489 }
490
add_task( &mut self, task_id: RenderTaskId, ctx: &RenderTargetContext, gpu_cache: &mut GpuCache, render_tasks: &RenderTaskGraph, clip_store: &ClipStore, transforms: &mut TransformPalette, )491 fn add_task(
492 &mut self,
493 task_id: RenderTaskId,
494 ctx: &RenderTargetContext,
495 gpu_cache: &mut GpuCache,
496 render_tasks: &RenderTaskGraph,
497 clip_store: &ClipStore,
498 transforms: &mut TransformPalette,
499 ) {
500 profile_scope!("add_task");
501 let task = &render_tasks[task_id];
502 let target_rect = task.get_target_rect();
503
504 match task.kind {
505 RenderTaskKind::Image(..) |
506 RenderTaskKind::Cached(..) |
507 RenderTaskKind::Readback(..) |
508 RenderTaskKind::Picture(..) |
509 RenderTaskKind::Blit(..) |
510 RenderTaskKind::Border(..) |
511 RenderTaskKind::LineDecoration(..) |
512 RenderTaskKind::FastLinearGradient(..) |
513 RenderTaskKind::LinearGradient(..) |
514 RenderTaskKind::RadialGradient(..) |
515 RenderTaskKind::ConicGradient(..) |
516 RenderTaskKind::SvgFilter(..) => {
517 panic!("BUG: should not be added to alpha target!");
518 }
519 RenderTaskKind::VerticalBlur(..) => {
520 self.zero_clears.push(task_id);
521 add_blur_instances(
522 &mut self.vertical_blurs,
523 BlurDirection::Vertical,
524 task_id.into(),
525 task.children[0],
526 render_tasks,
527 );
528 }
529 RenderTaskKind::HorizontalBlur(..) => {
530 self.zero_clears.push(task_id);
531 add_blur_instances(
532 &mut self.horizontal_blurs,
533 BlurDirection::Horizontal,
534 task_id.into(),
535 task.children[0],
536 render_tasks,
537 );
538 }
539 RenderTaskKind::CacheMask(ref task_info) => {
540 let clear_to_one = self.clip_batcher.add(
541 task_info.clip_node_range,
542 task_info.root_spatial_node_index,
543 render_tasks,
544 gpu_cache,
545 clip_store,
546 transforms,
547 task_info.actual_rect,
548 task_info.device_pixel_scale,
549 target_rect.min.to_f32(),
550 task_info.actual_rect.min,
551 ctx,
552 );
553 if task_info.clear_to_one || clear_to_one {
554 self.one_clears.push(task_id);
555 }
556 }
557 RenderTaskKind::ClipRegion(ref region_task) => {
558 if region_task.clear_to_one {
559 self.one_clears.push(task_id);
560 }
561 let device_rect = DeviceRect::from_size(
562 target_rect.size().to_f32(),
563 );
564 self.clip_batcher.add_clip_region(
565 region_task.local_pos,
566 device_rect,
567 region_task.clip_data.clone(),
568 target_rect.min.to_f32(),
569 DevicePoint::zero(),
570 region_task.device_pixel_scale.0,
571 );
572 }
573 RenderTaskKind::Scaling(ref info) => {
574 add_scaling_instances(
575 info,
576 &mut self.scalings,
577 task,
578 task.children.first().map(|&child| &render_tasks[child]),
579 );
580 }
581 #[cfg(test)]
582 RenderTaskKind::Test(..) => {}
583 }
584 }
585
needs_depth(&self) -> bool586 fn needs_depth(&self) -> bool {
587 false
588 }
589 }
590
591 #[cfg_attr(feature = "capture", derive(Serialize))]
592 #[cfg_attr(feature = "replay", derive(Deserialize))]
593 pub struct PictureCacheTarget {
594 pub surface: ResolvedSurfaceTexture,
595 pub alpha_batch_container: AlphaBatchContainer,
596 pub clear_color: Option<ColorF>,
597 pub dirty_rect: DeviceIntRect,
598 pub valid_rect: DeviceIntRect,
599 }
600
601 #[cfg_attr(feature = "capture", derive(Serialize))]
602 #[cfg_attr(feature = "replay", derive(Deserialize))]
603 pub struct TextureCacheRenderTarget {
604 pub target_kind: RenderTargetKind,
605 pub horizontal_blurs: FastHashMap<TextureSource, Vec<BlurInstance>>,
606 pub blits: Vec<BlitJob>,
607 pub border_segments_complex: Vec<BorderInstance>,
608 pub border_segments_solid: Vec<BorderInstance>,
609 pub clears: Vec<DeviceIntRect>,
610 pub line_decorations: Vec<LineDecorationJob>,
611 pub fast_linear_gradients: Vec<FastLinearGradientInstance>,
612 pub linear_gradients: Vec<LinearGradientInstance>,
613 pub radial_gradients: Vec<RadialGradientInstance>,
614 pub conic_gradients: Vec<ConicGradientInstance>,
615 }
616
617 impl TextureCacheRenderTarget {
new(target_kind: RenderTargetKind) -> Self618 pub fn new(target_kind: RenderTargetKind) -> Self {
619 TextureCacheRenderTarget {
620 target_kind,
621 horizontal_blurs: FastHashMap::default(),
622 blits: vec![],
623 border_segments_complex: vec![],
624 border_segments_solid: vec![],
625 clears: vec![],
626 line_decorations: vec![],
627 fast_linear_gradients: vec![],
628 linear_gradients: vec![],
629 radial_gradients: vec![],
630 conic_gradients: vec![],
631 }
632 }
633
add_task( &mut self, task_id: RenderTaskId, render_tasks: &RenderTaskGraph, gpu_cache: &mut GpuCache, )634 pub fn add_task(
635 &mut self,
636 task_id: RenderTaskId,
637 render_tasks: &RenderTaskGraph,
638 gpu_cache: &mut GpuCache,
639 ) {
640 profile_scope!("add_task");
641 let task_address = task_id.into();
642
643 let task = &render_tasks[task_id];
644 let target_rect = task.get_target_rect();
645
646 match task.kind {
647 RenderTaskKind::LineDecoration(ref info) => {
648 self.clears.push(target_rect);
649
650 self.line_decorations.push(LineDecorationJob {
651 task_rect: target_rect.to_f32(),
652 local_size: info.local_size,
653 style: info.style as i32,
654 axis_select: match info.orientation {
655 LineOrientation::Horizontal => 0.0,
656 LineOrientation::Vertical => 1.0,
657 },
658 wavy_line_thickness: info.wavy_line_thickness,
659 });
660 }
661 RenderTaskKind::HorizontalBlur(..) => {
662 add_blur_instances(
663 &mut self.horizontal_blurs,
664 BlurDirection::Horizontal,
665 task_address,
666 task.children[0],
667 render_tasks,
668 );
669 }
670 RenderTaskKind::Blit(ref task_info) => {
671 // Add a blit job to copy from an existing render
672 // task to this target.
673 self.blits.push(BlitJob {
674 source: task_info.source,
675 target_rect,
676 });
677 }
678 RenderTaskKind::Border(ref task_info) => {
679 self.clears.push(target_rect);
680
681 let task_origin = target_rect.min.to_f32();
682 // TODO(gw): Clone here instead of a move of this vec, since the frame
683 // graph is immutable by this point. It's rare that borders
684 // are drawn since they are persisted in the texture cache,
685 // but perhaps this could be improved in future.
686 let instances = task_info.instances.clone();
687 for mut instance in instances {
688 // TODO(gw): It may be better to store the task origin in
689 // the render task data instead of per instance.
690 instance.task_origin = task_origin;
691 if instance.flags & STYLE_MASK == STYLE_SOLID {
692 self.border_segments_solid.push(instance);
693 } else {
694 self.border_segments_complex.push(instance);
695 }
696 }
697 }
698 RenderTaskKind::FastLinearGradient(ref task_info) => {
699 self.fast_linear_gradients.push(task_info.to_instance(&target_rect));
700 }
701 RenderTaskKind::LinearGradient(ref task_info) => {
702 self.linear_gradients.push(task_info.to_instance(&target_rect, gpu_cache));
703 }
704 RenderTaskKind::RadialGradient(ref task_info) => {
705 self.radial_gradients.push(task_info.to_instance(&target_rect, gpu_cache));
706 }
707 RenderTaskKind::ConicGradient(ref task_info) => {
708 self.conic_gradients.push(task_info.to_instance(&target_rect, gpu_cache));
709 }
710 RenderTaskKind::Image(..) |
711 RenderTaskKind::Cached(..) |
712 RenderTaskKind::VerticalBlur(..) |
713 RenderTaskKind::Picture(..) |
714 RenderTaskKind::ClipRegion(..) |
715 RenderTaskKind::CacheMask(..) |
716 RenderTaskKind::Readback(..) |
717 RenderTaskKind::Scaling(..) |
718 RenderTaskKind::SvgFilter(..) => {
719 panic!("BUG: unexpected task kind for texture cache target");
720 }
721 #[cfg(test)]
722 RenderTaskKind::Test(..) => {}
723 }
724 }
725 }
726
add_blur_instances( instances: &mut FastHashMap<TextureSource, Vec<BlurInstance>>, blur_direction: BlurDirection, task_address: RenderTaskAddress, src_task_id: RenderTaskId, render_tasks: &RenderTaskGraph, )727 fn add_blur_instances(
728 instances: &mut FastHashMap<TextureSource, Vec<BlurInstance>>,
729 blur_direction: BlurDirection,
730 task_address: RenderTaskAddress,
731 src_task_id: RenderTaskId,
732 render_tasks: &RenderTaskGraph,
733 ) {
734 let source = render_tasks[src_task_id].get_texture_source();
735
736 let instance = BlurInstance {
737 task_address,
738 src_task_address: src_task_id.into(),
739 blur_direction,
740 };
741
742 instances
743 .entry(source)
744 .or_insert(Vec::new())
745 .push(instance);
746 }
747
add_scaling_instances( task: &ScalingTask, instances: &mut FastHashMap<TextureSource, Vec<ScalingInstance>>, target_task: &RenderTask, source_task: Option<&RenderTask>, )748 fn add_scaling_instances(
749 task: &ScalingTask,
750 instances: &mut FastHashMap<TextureSource, Vec<ScalingInstance>>,
751 target_task: &RenderTask,
752 source_task: Option<&RenderTask>,
753 ) {
754 let target_rect = target_task
755 .get_target_rect()
756 .inner_box(task.padding)
757 .to_f32();
758
759 let source = source_task.unwrap().get_texture_source();
760
761 let source_rect = source_task.unwrap().get_target_rect().to_f32();
762
763 instances
764 .entry(source)
765 .or_insert(Vec::new())
766 .push(ScalingInstance {
767 target_rect,
768 source_rect,
769 });
770 }
771
add_svg_filter_instances( instances: &mut Vec<(BatchTextures, Vec<SvgFilterInstance>)>, render_tasks: &RenderTaskGraph, filter: &SvgFilterInfo, task_id: RenderTaskId, input_1_task: Option<RenderTaskId>, input_2_task: Option<RenderTaskId>, extra_data_address: Option<GpuCacheAddress>, )772 fn add_svg_filter_instances(
773 instances: &mut Vec<(BatchTextures, Vec<SvgFilterInstance>)>,
774 render_tasks: &RenderTaskGraph,
775 filter: &SvgFilterInfo,
776 task_id: RenderTaskId,
777 input_1_task: Option<RenderTaskId>,
778 input_2_task: Option<RenderTaskId>,
779 extra_data_address: Option<GpuCacheAddress>,
780 ) {
781 let mut textures = BatchTextures::empty();
782
783 if let Some(id) = input_1_task {
784 textures.input.colors[0] = render_tasks[id].get_texture_source();
785 }
786
787 if let Some(id) = input_2_task {
788 textures.input.colors[1] = render_tasks[id].get_texture_source();
789 }
790
791 let kind = match filter {
792 SvgFilterInfo::Blend(..) => 0,
793 SvgFilterInfo::Flood(..) => 1,
794 SvgFilterInfo::LinearToSrgb => 2,
795 SvgFilterInfo::SrgbToLinear => 3,
796 SvgFilterInfo::Opacity(..) => 4,
797 SvgFilterInfo::ColorMatrix(..) => 5,
798 SvgFilterInfo::DropShadow(..) => 6,
799 SvgFilterInfo::Offset(..) => 7,
800 SvgFilterInfo::ComponentTransfer(..) => 8,
801 SvgFilterInfo::Identity => 9,
802 SvgFilterInfo::Composite(..) => 10,
803 };
804
805 let input_count = match filter {
806 SvgFilterInfo::Flood(..) => 0,
807
808 SvgFilterInfo::LinearToSrgb |
809 SvgFilterInfo::SrgbToLinear |
810 SvgFilterInfo::Opacity(..) |
811 SvgFilterInfo::ColorMatrix(..) |
812 SvgFilterInfo::Offset(..) |
813 SvgFilterInfo::ComponentTransfer(..) |
814 SvgFilterInfo::Identity => 1,
815
816 // Not techincally a 2 input filter, but we have 2 inputs here: original content & blurred content.
817 SvgFilterInfo::DropShadow(..) |
818 SvgFilterInfo::Blend(..) |
819 SvgFilterInfo::Composite(..) => 2,
820 };
821
822 let generic_int = match filter {
823 SvgFilterInfo::Blend(mode) => *mode as u16,
824 SvgFilterInfo::ComponentTransfer(data) =>
825 ((data.r_func.to_int() << 12 |
826 data.g_func.to_int() << 8 |
827 data.b_func.to_int() << 4 |
828 data.a_func.to_int()) as u16),
829 SvgFilterInfo::Composite(operator) =>
830 operator.as_int() as u16,
831 SvgFilterInfo::LinearToSrgb |
832 SvgFilterInfo::SrgbToLinear |
833 SvgFilterInfo::Flood(..) |
834 SvgFilterInfo::Opacity(..) |
835 SvgFilterInfo::ColorMatrix(..) |
836 SvgFilterInfo::DropShadow(..) |
837 SvgFilterInfo::Offset(..) |
838 SvgFilterInfo::Identity => 0,
839 };
840
841 let instance = SvgFilterInstance {
842 task_address: task_id.into(),
843 input_1_task_address: input_1_task.map(|id| id.into()).unwrap_or(RenderTaskAddress(0)),
844 input_2_task_address: input_2_task.map(|id| id.into()).unwrap_or(RenderTaskAddress(0)),
845 kind,
846 input_count,
847 generic_int,
848 extra_data_address: extra_data_address.unwrap_or(GpuCacheAddress::INVALID),
849 };
850
851 for (ref mut batch_textures, ref mut batch) in instances.iter_mut() {
852 if let Some(combined_textures) = batch_textures.combine_textures(textures) {
853 batch.push(instance);
854 // Update the batch textures to the newly combined batch textures
855 *batch_textures = combined_textures;
856 return;
857 }
858 }
859
860 instances.push((textures, vec![instance]));
861 }
862
863 // Information required to do a blit from a source to a target.
864 #[cfg_attr(feature = "capture", derive(Serialize))]
865 #[cfg_attr(feature = "replay", derive(Deserialize))]
866 pub struct BlitJob {
867 pub source: RenderTaskId,
868 pub target_rect: DeviceIntRect,
869 }
870
871 #[cfg_attr(feature = "capture", derive(Serialize))]
872 #[cfg_attr(feature = "replay", derive(Deserialize))]
873 #[derive(Clone, Debug)]
874 pub struct LineDecorationJob {
875 pub task_rect: DeviceRect,
876 pub local_size: LayoutSize,
877 pub wavy_line_thickness: f32,
878 pub style: i32,
879 pub axis_select: f32,
880 }
881