From df6e685f0f0c1625acce8841b9990c0010ccaba8 Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Tue, 6 Feb 2018 14:02:15 +1000 Subject: [PATCH] Support partial rendering of off-screen pictures / render tasks. This allows drawing a render task where the allocated space in the surface cache is smaller than the required size if we were to draw the complete contents of a render task. The primary motivation of this task is to fix some existing bugs. Specifically, if an off-screen render task is intersecting with the main screen rect, we would only allocate enough space in the surface cache texture for the visible portion. Then, the render task would be drawn, and in some cases could extend outside the bounds of the allocated rect for the task. This can result in image corruption - for instance, a long scrolling region which is drawn to an off-screen target (due to a filter) could draw outside its region, affecting other tasks in the same surface cache texture layer. A second motivation is as a performance and power saving optimization. In the future, we may want to render and cache "tiles" that can be cached and provided to the OS compositor interfaces, to avoid work during scrolling. Finally, the implementation also contains the infrastructure necessary to enable us to completely skip texture-cache sub-render tasks when a frame is re-rendered, which is a performance gain in some cases. To achieve this: * Instead of one alpha-batcher per target, there is one per render task. * Each alpha-batcher tracks the task-relative, screen-space bounding rect of primitives added to it. * Once each alpha-batcher is complete, we check if the allocated rect for this task completely contains the combined bounding rect of the primitives inside it. * If it *does* contain it (common case) then we run a batch merging step where this set of batches is merged with any other alpha batch tasks that are also self-contained. This merging step can be very simple, since we know there is no overlap ordering constraints. * If it *doesn't* contain it, then we will submit this set of batches separately to the merged batch list, applying a scissor rect set to the size of the render task allocation. The batching results should be as good or better than previously, except in the genuine case where this fixes a bug, in which case there may be a small number of extra draw calls. Since the batching of each render task is independent, and also only references read-only shared state, we can easily run each batch creation task on a worker thread, if profiling shows any benefit to that. --- webrender/examples/common/boilerplate.rs | 7 - webrender/src/batch.rs | 352 ++++++++++++++--------- webrender/src/debug_server.rs | 2 - webrender/src/device.rs | 13 + webrender/src/frame_builder.rs | 4 +- webrender/src/picture.rs | 24 +- webrender/src/prim_store.rs | 21 +- webrender/src/render_task.rs | 19 +- webrender/src/renderer.rs | 186 ++++++------ webrender/src/tiling.rs | 54 +++- webrender/src/util.rs | 6 - webrender_api/src/api.rs | 2 - wrench/src/main.rs | 4 - 13 files changed, 400 insertions(+), 294 deletions(-) diff --git a/webrender/examples/common/boilerplate.rs b/webrender/examples/common/boilerplate.rs index 732b5ab88d..5894fdec1e 100644 --- a/webrender/examples/common/boilerplate.rs +++ b/webrender/examples/common/boilerplate.rs @@ -223,13 +223,6 @@ pub fn main_wrapper( ) => { renderer.toggle_debug_flags(webrender::DebugFlags::TEXTURE_CACHE_DBG); } - glutin::Event::KeyboardInput( - glutin::ElementState::Pressed, - _, - Some(glutin::VirtualKeyCode::B), - ) => { - renderer.toggle_debug_flags(webrender::DebugFlags::ALPHA_PRIM_DBG); - } glutin::Event::KeyboardInput( glutin::ElementState::Pressed, _, diff --git a/webrender/src/batch.rs b/webrender/src/batch.rs index 80e7eae7d4..b8478f217e 100644 --- a/webrender/src/batch.rs +++ b/webrender/src/batch.rs @@ -4,7 +4,7 @@ use api::{AlphaType, DeviceIntRect, DeviceIntSize, LayerToWorldScale}; use api::{DeviceUintRect, DeviceUintPoint, DeviceUintSize, ExternalImageType, FilterOp, ImageRendering, LayerRect}; -use api::{SubpixelDirection, YuvColorSpace, YuvFormat}; +use api::{DeviceIntPoint, LayerPoint, SubpixelDirection, YuvColorSpace, YuvFormat}; use api::{LayerToWorldTransform, WorldPixel}; use border::{BorderCornerInstance, BorderCornerSide, BorderEdgeKind}; use clip::{ClipSource, ClipStore}; @@ -13,16 +13,16 @@ use euclid::{TypedTransform3D, vec3}; use glyph_rasterizer::GlyphFormat; use gpu_cache::{GpuCache, GpuCacheAddress}; use gpu_types::{BrushImageKind, BrushInstance, ClipChainRectIndex}; -use gpu_types::{ClipMaskInstance, ClipScrollNodeIndex, PictureType}; +use gpu_types::{ClipMaskInstance, ClipScrollNodeIndex}; use gpu_types::{CompositePrimitiveInstance, PrimitiveInstance, SimplePrimitiveInstance}; use internal_types::{FastHashMap, SourceTexture}; -use picture::{PictureCompositeMode, PictureKind, PicturePrimitive, PictureSurface}; +use picture::{ContentOrigin, PictureCompositeMode, PictureKind, PicturePrimitive, PictureSurface}; use plane_split::{BspSplitter, Polygon, Splitter}; use prim_store::{ImageSource, PrimitiveIndex, PrimitiveKind, PrimitiveMetadata, PrimitiveStore}; use prim_store::{BrushPrimitive, BrushKind, DeferredResolve, EdgeAaSegmentMask, PrimitiveRun}; use render_task::{ClipWorkItem}; -use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskKind}; -use render_task::{RenderTaskTree}; +use render_task::{RenderTaskAddress, RenderTaskId}; +use render_task::{RenderTaskKind, RenderTaskTree}; use renderer::{BlendMode, ImageBufferKind}; use renderer::BLOCKS_PER_UV_RECT; use resource_cache::{CacheItem, GlyphFetchResult, ImageRequest, ResourceCache}; @@ -124,42 +124,6 @@ impl BatchTextures { } } -#[derive(Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct AlphaPrimitiveBatch { - pub key: BatchKey, - pub instances: Vec, - pub item_rects: Vec, -} - -impl AlphaPrimitiveBatch { - pub fn new(key: BatchKey) -> AlphaPrimitiveBatch { - AlphaPrimitiveBatch { - key, - instances: Vec::new(), - item_rects: Vec::new(), - } - } -} - -#[derive(Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct OpaquePrimitiveBatch { - pub key: BatchKey, - pub instances: Vec, -} - -impl OpaquePrimitiveBatch { - pub fn new(key: BatchKey) -> OpaquePrimitiveBatch { - OpaquePrimitiveBatch { - key, - instances: Vec::new(), - } - } -} - #[derive(Copy, Clone, Debug)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] @@ -191,23 +155,23 @@ fn textures_compatible(t1: SourceTexture, t2: SourceTexture) -> bool { t1 == SourceTexture::Invalid || t2 == SourceTexture::Invalid || t1 == t2 } -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] pub struct AlphaBatchList { - pub batches: Vec, + pub batches: Vec, + pub item_rects: Vec>, } impl AlphaBatchList { fn new() -> Self { AlphaBatchList { batches: Vec::new(), + item_rects: Vec::new(), } } pub fn get_suitable_batch( &mut self, key: BatchKey, - item_bounding_rect: &DeviceIntRect, + task_relative_bounding_rect: &DeviceIntRect, ) -> &mut Vec { let mut selected_batch_index = None; @@ -224,8 +188,8 @@ impl AlphaBatchList { // Subpixel text is drawn in two passes. Because of this, we need // to check for overlaps with every batch (which is a bit different // than the normal batching below). - for item_rect in &batch.item_rects { - if item_rect.intersects(item_bounding_rect) { + for item_rect in &self.item_rects[batch_index] { + if item_rect.intersects(task_relative_bounding_rect) { break 'outer_text; } } @@ -248,8 +212,8 @@ impl AlphaBatchList { } // check for intersections - for item_rect in &batch.item_rects { - if item_rect.intersects(item_bounding_rect) { + for item_rect in &self.item_rects[batch_index] { + if item_rect.intersects(task_relative_bounding_rect) { break 'outer_default; } } @@ -258,27 +222,25 @@ impl AlphaBatchList { } if selected_batch_index.is_none() { - let new_batch = AlphaPrimitiveBatch::new(key); + let new_batch = PrimitiveBatch::new(key); selected_batch_index = Some(self.batches.len()); self.batches.push(new_batch); + self.item_rects.push(Vec::new()); } - let batch = &mut self.batches[selected_batch_index.unwrap()]; - batch.item_rects.push(*item_bounding_rect); - - &mut batch.instances + let selected_batch_index = selected_batch_index.unwrap(); + self.item_rects[selected_batch_index].push(*task_relative_bounding_rect); + &mut self.batches[selected_batch_index].instances } } -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] pub struct OpaqueBatchList { - pub pixel_area_threshold_for_new_batch: i32, - pub batches: Vec, + pub pixel_area_threshold_for_new_batch: f32, + pub batches: Vec, } impl OpaqueBatchList { - fn new(pixel_area_threshold_for_new_batch: i32) -> Self { + fn new(pixel_area_threshold_for_new_batch: f32) -> Self { OpaqueBatchList { batches: Vec::new(), pixel_area_threshold_for_new_batch, @@ -288,10 +250,10 @@ impl OpaqueBatchList { pub fn get_suitable_batch( &mut self, key: BatchKey, - item_bounding_rect: &DeviceIntRect + task_relative_bounding_rect: &DeviceIntRect ) -> &mut Vec { let mut selected_batch_index = None; - let item_area = item_bounding_rect.size.area(); + let item_area = task_relative_bounding_rect.size.to_f32().area(); // If the area of this primitive is larger than the given threshold, // then it is large enough to warrant breaking a batch for. In this @@ -314,7 +276,7 @@ impl OpaqueBatchList { } if selected_batch_index.is_none() { - let new_batch = OpaquePrimitiveBatch::new(key); + let new_batch = PrimitiveBatch::new(key); selected_batch_index = Some(self.batches.len()); self.batches.push(new_batch); } @@ -337,34 +299,36 @@ impl OpaqueBatchList { } } -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] pub struct BatchList { pub alpha_batch_list: AlphaBatchList, pub opaque_batch_list: OpaqueBatchList, + pub combined_bounding_rect: DeviceIntRect, } impl BatchList { pub fn new(screen_size: DeviceIntSize) -> Self { // The threshold for creating a new batch is // one quarter the screen size. - let batch_area_threshold = screen_size.width * screen_size.height / 4; + let batch_area_threshold = (screen_size.width * screen_size.height) as f32 / 4.0; BatchList { alpha_batch_list: AlphaBatchList::new(), opaque_batch_list: OpaqueBatchList::new(batch_area_threshold), + combined_bounding_rect: DeviceIntRect::zero(), } } pub fn get_suitable_batch( &mut self, key: BatchKey, - item_bounding_rect: &DeviceIntRect, + task_relative_bounding_rect: &DeviceIntRect, ) -> &mut Vec { + self.combined_bounding_rect = self.combined_bounding_rect.union(task_relative_bounding_rect); + match key.blend_mode { BlendMode::None => { self.opaque_batch_list - .get_suitable_batch(key, item_bounding_rect) + .get_suitable_batch(key, task_relative_bounding_rect) } BlendMode::Alpha | BlendMode::PremultipliedAlpha | @@ -374,7 +338,7 @@ impl BatchList { BlendMode::SubpixelWithBgColor | BlendMode::SubpixelDualSource => { self.alpha_batch_list - .get_suitable_batch(key, item_bounding_rect) + .get_suitable_batch(key, task_relative_bounding_rect) } } } @@ -384,61 +348,126 @@ impl BatchList { } } -/// Encapsulates the logic of building batches for items that are blended. #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct AlphaBatcher { - pub batch_list: BatchList, +pub struct PrimitiveBatch { + pub key: BatchKey, + pub instances: Vec, +} + +impl PrimitiveBatch { + fn new(key: BatchKey) -> PrimitiveBatch { + PrimitiveBatch { + key, + instances: Vec::new(), + } + } +} + +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct AlphaBatchContainer { pub text_run_cache_prims: FastHashMap>, - glyph_fetch_buffer: Vec, + pub opaque_batches: Vec, + pub alpha_batches: Vec, + pub target_rect: Option, } -impl AlphaBatcher { - pub fn new(screen_size: DeviceIntSize) -> Self { - AlphaBatcher { - batch_list: BatchList::new(screen_size), - glyph_fetch_buffer: Vec::new(), +impl AlphaBatchContainer { + pub fn new(target_rect: Option) -> AlphaBatchContainer { + AlphaBatchContainer { text_run_cache_prims: FastHashMap::default(), + opaque_batches: Vec::new(), + alpha_batches: Vec::new(), + target_rect, } } - pub fn build( - &mut self, - tasks: &[RenderTaskId], - ctx: &RenderTargetContext, - gpu_cache: &mut GpuCache, - render_tasks: &RenderTaskTree, - deferred_resolves: &mut Vec, - ) { - for &task_id in tasks { - match render_tasks[task_id].kind { - RenderTaskKind::Picture(ref pic_task) => { - let pic_index = ctx.prim_store.cpu_metadata[pic_task.prim_index.0].cpu_prim_index; - let pic = &ctx.prim_store.cpu_pictures[pic_index.0]; - self.add_pic_to_batch( - pic, - task_id, - ctx, - gpu_cache, - render_tasks, - deferred_resolves, - ); + fn merge(&mut self, builder: AlphaBatchBuilder) { + self.text_run_cache_prims.extend(builder.text_run_cache_prims); + + for other_batch in builder.batch_list.opaque_batch_list.batches { + let batch_index = self.opaque_batches.iter().position(|batch| { + batch.key.is_compatible_with(&other_batch.key) + }); + + match batch_index { + Some(batch_index) => { + self.opaque_batches[batch_index].instances.extend(other_batch.instances); } - _ => { - unreachable!(); + None => { + self.opaque_batches.push(other_batch); } } } - self.batch_list.finalize(); + let mut min_batch_index = 0; + + for other_batch in builder.batch_list.alpha_batch_list.batches { + let batch_index = self.alpha_batches.iter().skip(min_batch_index).position(|batch| { + batch.key.is_compatible_with(&other_batch.key) + }); + + match batch_index { + Some(batch_index) => { + let batch_index = batch_index + min_batch_index; + self.alpha_batches[batch_index].instances.extend(other_batch.instances); + min_batch_index = batch_index; + } + None => { + self.alpha_batches.push(other_batch); + min_batch_index = self.alpha_batches.len(); + } + } + } } +} + +/// Encapsulates the logic of building batches for items that are blended. +pub struct AlphaBatchBuilder { + pub batch_list: BatchList, + pub text_run_cache_prims: FastHashMap>, + glyph_fetch_buffer: Vec, + target_rect: DeviceIntRect, +} - pub fn is_empty(&self) -> bool { - self.batch_list.opaque_batch_list.batches.is_empty() && - self.batch_list.alpha_batch_list.batches.is_empty() +impl AlphaBatchBuilder { + pub fn new( + screen_size: DeviceIntSize, + target_rect: DeviceIntRect, + ) -> Self { + AlphaBatchBuilder { + batch_list: BatchList::new(screen_size), + glyph_fetch_buffer: Vec::new(), + text_run_cache_prims: FastHashMap::default(), + target_rect, + } + } + + pub fn build(mut self, merged_batches: &mut AlphaBatchContainer) -> Option { + self.batch_list.finalize(); + + let task_relative_target_rect = DeviceIntRect::new( + DeviceIntPoint::zero(), + self.target_rect.size, + ); + + let can_merge = task_relative_target_rect.contains_rect(&self.batch_list.combined_bounding_rect); + + if can_merge { + merged_batches.merge(self); + None + } else { + Some(AlphaBatchContainer { + alpha_batches: self.batch_list.alpha_batch_list.batches, + opaque_batches: self.batch_list.opaque_batch_list.batches, + target_rect: Some(self.target_rect), + text_run_cache_prims: self.text_run_cache_prims, + }) + } } - fn add_pic_to_batch( + pub fn add_pic_to_batch( &mut self, pic: &PicturePrimitive, task_id: RenderTaskId, @@ -449,6 +478,16 @@ impl AlphaBatcher { ) { let task_address = render_tasks.get_task_address(task_id); + let task = &render_tasks[task_id]; + let content_origin = match task.kind { + RenderTaskKind::Picture(ref pic_task) => { + pic_task.content_origin + } + _ => { + panic!("todo: tidy this up"); + } + }; + // Even though most of the time a splitter isn't used or needed, // they are cheap to construct so we will always pass one down. let mut splitter = BspSplitter::new(); @@ -467,7 +506,8 @@ impl AlphaBatcher { task_address, deferred_resolves, &mut splitter, - pic.picture_type(), + pic, + content_origin, ); } @@ -490,7 +530,7 @@ impl AlphaBatcher { ); let pic_metadata = &ctx.prim_store.cpu_metadata[prim_index.0]; let pic = &ctx.prim_store.cpu_pictures[pic_metadata.cpu_prim_index.0]; - let batch = self.batch_list.get_suitable_batch(key, pic_metadata.screen_rect.as_ref().expect("bug")); + let batch = self.batch_list.get_suitable_batch(key, &pic_metadata.screen_rect.as_ref().expect("bug").clipped); let render_task_id = match pic.surface { Some(PictureSurface::RenderTask(render_task_id)) => render_task_id, @@ -528,7 +568,8 @@ impl AlphaBatcher { task_address: RenderTaskAddress, deferred_resolves: &mut Vec, splitter: &mut BspSplitter, - pic_type: PictureType, + pic: &PicturePrimitive, + content_origin: ContentOrigin, ) { for i in 0 .. run.count { let prim_index = PrimitiveIndex(run.base_prim_index.0 + i); @@ -541,7 +582,12 @@ impl AlphaBatcher { // We currently only support culling on normal (Image) // picture types. // TODO(gw): Support culling on shadow image types. - if pic_type != PictureType::Image || metadata.screen_rect.is_some() { + let is_image = match pic.kind { + PictureKind::Image { .. } => true, + PictureKind::BoxShadow { .. } | PictureKind::TextShadow { .. } => false, + }; + + if !is_image || metadata.screen_rect.is_some() { self.add_prim_to_batch( metadata.clip_chain_rect_index, scroll_id, @@ -553,7 +599,8 @@ impl AlphaBatcher { task_address, deferred_resolves, splitter, - pic_type, + content_origin, + pic, ); } } @@ -593,7 +640,8 @@ impl AlphaBatcher { task_address: RenderTaskAddress, deferred_resolves: &mut Vec, splitter: &mut BspSplitter, - pic_type: PictureType, + content_origin: ContentOrigin, + pic: &PicturePrimitive, ) { let z = prim_index.0 as i32; let prim_metadata = ctx.prim_store.get_metadata(prim_index); @@ -602,13 +650,32 @@ impl AlphaBatcher { // wasteful. We should probably cache this in // the scroll node... let transform_kind = scroll_node.transform.transform_kind(); - let item_bounding_rect = &match prim_metadata.screen_rect { - Some(screen_rect) => screen_rect, - None => { - debug_assert_ne!(pic_type, PictureType::Image); - DeviceIntRect::zero() + + let task_relative_bounding_rect = match content_origin { + ContentOrigin::Screen(point) => { + // translate by content-origin + let screen_rect = prim_metadata.screen_rect.expect("bug"); + DeviceIntRect::new( + DeviceIntPoint::new( + screen_rect.unclipped.origin.x - point.x, + screen_rect.unclipped.origin.y - point.y, + ), + screen_rect.unclipped.size, + ) + } + ContentOrigin::Local(point) => { + // scale local rect by device pixel ratio + let content_rect = LayerRect::new( + LayerPoint::new( + prim_metadata.local_rect.origin.x - point.x, + prim_metadata.local_rect.origin.y - point.y, + ), + prim_metadata.local_rect.size, + ); + (content_rect * LayerToWorldScale::new(1.0) * ctx.device_pixel_scale).round().to_i32() } }; + let prim_cache_address = gpu_cache.get_address(&prim_metadata.gpu_location); let no_textures = BatchTextures::no_texture(); let clip_task_address = prim_metadata @@ -636,7 +703,7 @@ impl AlphaBatcher { batch_key, clip_chain_rect_index, clip_task_address, - item_bounding_rect, + &task_relative_bounding_rect, prim_cache_address, scroll_id, task_address, @@ -665,7 +732,7 @@ impl AlphaBatcher { // Work around borrow ck on borrowing batch_list twice. { let batch = - self.batch_list.get_suitable_batch(corner_key, item_bounding_rect); + self.batch_list.get_suitable_batch(corner_key, &task_relative_bounding_rect); for (i, instance_kind) in border_cpu.corner_instances.iter().enumerate() { let sub_index = i as i32; @@ -694,7 +761,7 @@ impl AlphaBatcher { } } - let batch = self.batch_list.get_suitable_batch(edge_key, item_bounding_rect); + let batch = self.batch_list.get_suitable_batch(edge_key, &task_relative_bounding_rect); for (border_segment, instance_kind) in border_cpu.edges.iter().enumerate() { match *instance_kind { BorderEdgeKind::None => {}, @@ -723,7 +790,7 @@ impl AlphaBatcher { if cache_item.texture_id == SourceTexture::Invalid { warn!("Warnings: skip a PrimitiveKind::Image"); - debug!("at {:?}.", item_bounding_rect); + debug!("at {:?}.", task_relative_bounding_rect); return; } @@ -739,13 +806,16 @@ impl AlphaBatcher { ], }, ); - let batch = self.batch_list.get_suitable_batch(key, item_bounding_rect); + let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect); batch.push(base_instance.build(cache_item.uv_rect_handle.as_int(gpu_cache), 0, 0)); } PrimitiveKind::TextRun => { let text_cpu = &ctx.prim_store.cpu_text_runs[prim_metadata.cpu_prim_index.0]; - let is_shadow = pic_type == PictureType::TextShadow; + let is_shadow = match pic.kind { + PictureKind::TextShadow { .. } => true, + PictureKind::BoxShadow { .. } | PictureKind::Image { .. } => false, + }; // TODO(gw): It probably makes sense to base this decision on the content // origin field in the future (once that's configurable). @@ -819,7 +889,7 @@ impl AlphaBatcher { }; let key = BatchKey::new(kind, blend_mode, textures); - batch_list.get_suitable_batch(key, item_bounding_rect) + batch_list.get_suitable_batch(key, &task_relative_bounding_rect) }; for glyph in glyphs { @@ -861,7 +931,7 @@ impl AlphaBatcher { alpha_batch_key, clip_chain_rect_index, clip_task_address, - item_bounding_rect, + &task_relative_bounding_rect, prim_cache_address, scroll_id, task_address, @@ -885,7 +955,7 @@ impl AlphaBatcher { BrushImageSourceKind::from_render_target_kind(picture.target_kind())), ); let key = BatchKey::new(kind, blend_mode, textures); - let batch = self.batch_list.get_suitable_batch(key, item_bounding_rect); + let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect); let instance = BrushInstance { picture_address: task_address, @@ -949,7 +1019,8 @@ impl AlphaBatcher { BlendMode::PremultipliedAlpha, BatchTextures::render_target_cache(), ); - let batch = self.batch_list.get_suitable_batch(key, &item_bounding_rect); + let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect); + let item_bounding_rect = prim_metadata.screen_rect.expect("bug!!").clipped; let instance = CompositePrimitiveInstance::new( task_address, src_task_address, @@ -983,7 +1054,7 @@ impl AlphaBatcher { }; { - let batch = self.batch_list.get_suitable_batch(key, item_bounding_rect); + let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect); batch.push(PrimitiveInstance::from(instance)); } @@ -1003,7 +1074,7 @@ impl AlphaBatcher { BlendMode::PremultipliedAlpha, secondary_textures, ); - let batch = self.batch_list.get_suitable_batch(key, &item_bounding_rect); + let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect); let content_rect = prim_metadata.local_rect.translate(&-offset); let rect = (content_rect * LayerToWorldScale::new(1.0) * ctx.device_pixel_scale).round() @@ -1045,7 +1116,7 @@ impl AlphaBatcher { }; let amount = (amount * 65535.0).round() as i32; - let batch = self.batch_list.get_suitable_batch(key, &item_bounding_rect); + let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect); let instance = CompositePrimitiveInstance::new( task_address, @@ -1074,7 +1145,7 @@ impl AlphaBatcher { BlendMode::PremultipliedAlpha, BatchTextures::no_texture(), ); - let batch = self.batch_list.get_suitable_batch(key, &item_bounding_rect); + let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect); let backdrop_task_address = render_tasks.get_task_address(backdrop_id); let source_task_address = render_tasks.get_task_address(source_id); @@ -1098,7 +1169,8 @@ impl AlphaBatcher { BlendMode::PremultipliedAlpha, BatchTextures::render_target_cache(), ); - let batch = self.batch_list.get_suitable_batch(key, &item_bounding_rect); + let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect); + let item_bounding_rect = prim_metadata.screen_rect.expect("bug!!").clipped; let instance = CompositePrimitiveInstance::new( task_address, src_task_address, @@ -1138,7 +1210,7 @@ impl AlphaBatcher { TransformBatchKind::AlignedGradient, ); let key = BatchKey::new(kind, blend_mode, no_textures); - let batch = self.batch_list.get_suitable_batch(key, item_bounding_rect); + let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect); for part_index in 0 .. (gradient_cpu.stops_count - 1) { batch.push(base_instance.build(part_index as i32, 0, 0)); } @@ -1149,7 +1221,7 @@ impl AlphaBatcher { TransformBatchKind::AngleGradient, ); let key = BatchKey::new(kind, blend_mode, no_textures); - let batch = self.batch_list.get_suitable_batch(key, item_bounding_rect); + let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect); batch.push(base_instance.build(0, 0, 0)); } PrimitiveKind::RadialGradient => { @@ -1158,7 +1230,7 @@ impl AlphaBatcher { TransformBatchKind::RadialGradient, ); let key = BatchKey::new(kind, blend_mode, no_textures); - let batch = self.batch_list.get_suitable_batch(key, item_bounding_rect); + let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect); batch.push(base_instance.build(0, 0, 0)); } PrimitiveKind::YuvImage => { @@ -1186,7 +1258,7 @@ impl AlphaBatcher { if cache_item.texture_id == SourceTexture::Invalid { warn!("Warnings: skip a PrimitiveKind::YuvImage"); - debug!("at {:?}.", item_bounding_rect); + debug!("at {:?}.", task_relative_bounding_rect); return; } @@ -1211,7 +1283,7 @@ impl AlphaBatcher { ), ); let key = BatchKey::new(kind, blend_mode, textures); - let batch = self.batch_list.get_suitable_batch(key, item_bounding_rect); + let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect); batch.push(base_instance.build( uv_rect_addresses[0], @@ -1229,7 +1301,7 @@ impl AlphaBatcher { batch_key: BatchKey, clip_chain_rect_index: ClipChainRectIndex, clip_task_address: RenderTaskAddress, - item_bounding_rect: &DeviceIntRect, + task_relative_bounding_rect: &DeviceIntRect, prim_cache_address: GpuCacheAddress, scroll_id: ClipScrollNodeIndex, task_address: RenderTaskAddress, @@ -1261,7 +1333,7 @@ impl AlphaBatcher { let alpha_batch = self.batch_list.alpha_batch_list.get_suitable_batch( alpha_batch_key, - item_bounding_rect + task_relative_bounding_rect ); let opaque_batch_key = BatchKey { @@ -1271,7 +1343,7 @@ impl AlphaBatcher { let opaque_batch = self.batch_list.opaque_batch_list.get_suitable_batch( opaque_batch_key, - item_bounding_rect + task_relative_bounding_rect ); for (i, segment) in segment_desc.segments.iter().enumerate() { @@ -1299,7 +1371,7 @@ impl AlphaBatcher { } } None => { - let batch = self.batch_list.get_suitable_batch(batch_key, item_bounding_rect); + let batch = self.batch_list.get_suitable_batch(batch_key, task_relative_bounding_rect); batch.push(PrimitiveInstance::from(base_instance)); } } diff --git a/webrender/src/debug_server.rs b/webrender/src/debug_server.rs index d75226c31a..751394235f 100644 --- a/webrender/src/debug_server.rs +++ b/webrender/src/debug_server.rs @@ -53,8 +53,6 @@ impl ws::Handler for Server { "disable_texture_cache_debug" => DebugCommand::EnableTextureCacheDebug(false), "enable_render_target_debug" => DebugCommand::EnableRenderTargetDebug(true), "disable_render_target_debug" => DebugCommand::EnableRenderTargetDebug(false), - "enable_alpha_rects_debug" => DebugCommand::EnableAlphaRectsDebug(true), - "disable_alpha_rects_debug" => DebugCommand::EnableAlphaRectsDebug(false), "enable_gpu_time_queries" => DebugCommand::EnableGpuTimeQueries(true), "disable_gpu_time_queries" => DebugCommand::EnableGpuTimeQueries(false), "enable_gpu_sample_queries" => DebugCommand::EnableGpuSampleQueries(true), diff --git a/webrender/src/device.rs b/webrender/src/device.rs index 302aa55068..6497ccc7fb 100644 --- a/webrender/src/device.rs +++ b/webrender/src/device.rs @@ -1943,6 +1943,19 @@ impl Device { self.gl.disable(gl::STENCIL_TEST); } + pub fn set_scissor_rect(&self, rect: DeviceIntRect) { + self.gl.scissor( + rect.origin.x, + rect.origin.y, + rect.size.width, + rect.size.height, + ); + } + + pub fn enable_scissor(&self) { + self.gl.enable(gl::SCISSOR_TEST); + } + pub fn disable_scissor(&self) { self.gl.disable(gl::SCISSOR_TEST); } diff --git a/webrender/src/frame_builder.rs b/webrender/src/frame_builder.rs index 02cbccb671..3298dbcfe4 100644 --- a/webrender/src/frame_builder.rs +++ b/webrender/src/frame_builder.rs @@ -30,7 +30,7 @@ use prim_store::{PrimitiveContainer, PrimitiveIndex}; use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu}; use prim_store::{BrushSegmentDescriptor, PrimitiveRun, TextRunPrimitiveCpu}; use profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters}; -use render_task::{ClearMode, ClipChain, RenderTask, RenderTaskId, RenderTaskTree}; +use render_task::{ClearMode, ClipChain, RenderTask, RenderTaskId, RenderTaskLocation, RenderTaskTree}; use resource_cache::{ImageRequest, ResourceCache}; use scene::{ScenePipeline, SceneProperties}; use std::{mem, usize, f32}; @@ -1648,7 +1648,7 @@ impl FrameBuilder { pic.runs = pic_context.prim_runs; let root_render_task = RenderTask::new_picture( - None, + RenderTaskLocation::Fixed(frame_context.screen_rect), PrimitiveIndex(0), RenderTargetKind::Color, ContentOrigin::Screen(DeviceIntPoint::zero()), diff --git a/webrender/src/picture.rs b/webrender/src/picture.rs index 5ebcb8755e..d48bdbf6eb 100644 --- a/webrender/src/picture.rs +++ b/webrender/src/picture.rs @@ -12,7 +12,7 @@ use gpu_cache::GpuDataRequest; use gpu_types::{BrushImageKind, PictureType}; use prim_store::{BrushKind, BrushPrimitive, PrimitiveIndex, PrimitiveRun, PrimitiveRunLocalRect}; use render_task::{ClearMode, RenderTask, RenderTaskCacheKey}; -use render_task::{RenderTaskCacheKeyKind, RenderTaskId}; +use render_task::{RenderTaskCacheKeyKind, RenderTaskId, RenderTaskLocation}; use resource_cache::CacheItem; use scene::{FilterOpHelpers, SceneProperties}; use tiling::RenderTargetKind; @@ -318,14 +318,6 @@ impl PicturePrimitive { } } - pub fn picture_type(&self) -> PictureType { - match self.kind { - PictureKind::Image { .. } => PictureType::Image, - PictureKind::BoxShadow { .. } => PictureType::BoxShadow, - PictureKind::TextShadow { .. } => PictureType::TextShadow, - } - } - pub fn prepare_for_render( &mut self, prim_index: PrimitiveIndex, @@ -348,7 +340,7 @@ impl PicturePrimitive { match composite_mode { Some(PictureCompositeMode::Filter(FilterOp::Blur(blur_radius))) => { let picture_task = RenderTask::new_picture( - Some(prim_screen_rect.size), + RenderTaskLocation::Dynamic(None, prim_screen_rect.size), prim_index, RenderTargetKind::Color, content_origin, @@ -377,7 +369,7 @@ impl PicturePrimitive { Some(PictureCompositeMode::Filter(FilterOp::DropShadow(offset, blur_radius, color))) => { let rect = (prim_local_rect.translate(&-offset) * content_scale).round().to_i32(); let picture_task = RenderTask::new_picture( - Some(rect.size), + RenderTaskLocation::Dynamic(None, rect.size), prim_index, RenderTargetKind::Color, ContentOrigin::Screen(rect.origin), @@ -407,7 +399,7 @@ impl PicturePrimitive { } Some(PictureCompositeMode::MixBlend(..)) => { let picture_task = RenderTask::new_picture( - Some(prim_screen_rect.size), + RenderTaskLocation::Dynamic(None, prim_screen_rect.size), prim_index, RenderTargetKind::Color, content_origin, @@ -437,7 +429,7 @@ impl PicturePrimitive { self.surface = None; } else { let picture_task = RenderTask::new_picture( - Some(prim_screen_rect.size), + RenderTaskLocation::Dynamic(None, prim_screen_rect.size), prim_index, RenderTargetKind::Color, content_origin, @@ -454,7 +446,7 @@ impl PicturePrimitive { } Some(PictureCompositeMode::Blit) => { let picture_task = RenderTask::new_picture( - Some(prim_screen_rect.size), + RenderTaskLocation::Dynamic(None, prim_screen_rect.size), prim_index, RenderTargetKind::Color, content_origin, @@ -494,7 +486,7 @@ impl PicturePrimitive { let blur_std_deviation = device_radius * 0.5; let picture_task = RenderTask::new_picture( - Some(cache_size), + RenderTaskLocation::Dynamic(None, cache_size), prim_index, RenderTargetKind::Color, ContentOrigin::Local(content_rect.origin), @@ -554,7 +546,7 @@ impl PicturePrimitive { }; let picture_task = RenderTask::new_picture( - Some(cache_size), + RenderTaskLocation::Dynamic(None, cache_size), prim_index, RenderTargetKind::Alpha, ContentOrigin::Local(content_rect.origin), diff --git a/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index 5e5b7677f9..8934d77632 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -136,6 +136,12 @@ impl GpuCacheAddress { } } +#[derive(Debug, Copy, Clone)] +pub struct ScreenRect { + pub clipped: DeviceIntRect, + pub unclipped: DeviceIntRect, +} + // TODO(gw): Pack the fields here better! #[derive(Debug)] pub struct PrimitiveMetadata { @@ -153,7 +159,7 @@ pub struct PrimitiveMetadata { pub local_clip_rect: LayerRect, pub clip_chain_rect_index: ClipChainRectIndex, pub is_backface_visible: bool, - pub screen_rect: Option, + pub screen_rect: Option, /// A tag used to identify this primitive outside of WebRender. This is /// used for returning useful data during hit testing. @@ -1153,7 +1159,9 @@ impl PrimitiveStore { self.cpu_pictures[metadata.cpu_prim_index.0] .prepare_for_render( prim_index, - metadata.screen_rect.as_ref().expect("bug: trying to draw an off-screen picture!?"), + &metadata.screen_rect + .expect("bug: trying to draw an off-screen picture!?") + .clipped, &metadata.local_rect, pic_state_for_children, pic_state, @@ -1828,7 +1836,14 @@ impl PrimitiveStore { Some(ref node) => node.combined_outer_screen_rect, None => frame_context.screen_rect, }; - metadata.screen_rect = screen_bounding_rect.intersection(&clip_bounds); + metadata.screen_rect = screen_bounding_rect + .intersection(&clip_bounds) + .map(|clipped| { + ScreenRect { + clipped, + unclipped: screen_bounding_rect, + } + }); if metadata.screen_rect.is_none() && pic_context.perform_culling { return None; diff --git a/webrender/src/render_task.rs b/webrender/src/render_task.rs index 635e4faefc..69356de646 100644 --- a/webrender/src/render_task.rs +++ b/webrender/src/render_task.rs @@ -167,7 +167,7 @@ impl RenderTaskTree { // Sanity check - can be relaxed if needed match task.location { - RenderTaskLocation::Fixed => { + RenderTaskLocation::Fixed(..) => { debug_assert!(pass_index == passes.len() - 1); } RenderTaskLocation::Dynamic(..) | @@ -218,7 +218,7 @@ impl ops::IndexMut for RenderTaskTree { #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub enum RenderTaskLocation { - Fixed, + Fixed(DeviceIntRect), Dynamic(Option<(DeviceIntPoint, RenderTargetIndex)>, DeviceIntSize), TextureCache(SourceTexture, i32, DeviceIntRect), } @@ -336,7 +336,7 @@ pub struct RenderTask { impl RenderTask { pub fn new_picture( - size: Option, + location: RenderTaskLocation, prim_index: PrimitiveIndex, target_kind: RenderTargetKind, content_origin: ContentOrigin, @@ -345,11 +345,6 @@ impl RenderTask { children: Vec, pic_type: PictureType, ) -> Self { - let location = match size { - Some(size) => RenderTaskLocation::Dynamic(None, size), - None => RenderTaskLocation::Fixed, - }; - RenderTask { children, location, @@ -601,7 +596,7 @@ impl RenderTask { pub fn get_dynamic_size(&self) -> DeviceIntSize { match self.location { - RenderTaskLocation::Fixed => DeviceIntSize::zero(), + RenderTaskLocation::Fixed(..) => DeviceIntSize::zero(), RenderTaskLocation::Dynamic(_, size) => size, RenderTaskLocation::TextureCache(_, _, rect) => rect.size, } @@ -609,8 +604,8 @@ impl RenderTask { pub fn get_target_rect(&self) -> (DeviceIntRect, RenderTargetIndex) { match self.location { - RenderTaskLocation::Fixed => { - (DeviceIntRect::zero(), RenderTargetIndex(0)) + RenderTaskLocation::Fixed(rect) => { + (rect, RenderTargetIndex(0)) } // Previously, we only added render tasks after the entire // primitive chain was determined visible. This meant that @@ -824,7 +819,7 @@ impl RenderTaskCache { // Find out what size to alloc in the texture cache. let size = match render_task.location { - RenderTaskLocation::Fixed | + RenderTaskLocation::Fixed(..) | RenderTaskLocation::TextureCache(..) => { panic!("BUG: dynamic task was expected"); } diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index 9839eb912d..ecd2f8a13f 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -262,12 +262,11 @@ bitflags! { const PROFILER_DBG = 1 << 0; const RENDER_TARGET_DBG = 1 << 1; const TEXTURE_CACHE_DBG = 1 << 2; - const ALPHA_PRIM_DBG = 1 << 3; - const GPU_TIME_QUERIES = 1 << 4; - const GPU_SAMPLE_QUERIES= 1 << 5; - const DISABLE_BATCHING = 1 << 6; - const EPOCHS = 1 << 7; - const COMPACT_PROFILER = 1 << 8; + const GPU_TIME_QUERIES = 1 << 3; + const GPU_SAMPLE_QUERIES= 1 << 4; + const DISABLE_BATCHING = 1 << 5; + const EPOCHS = 1 << 6; + const COMPACT_PROFILER = 1 << 7; } } @@ -2566,35 +2565,35 @@ impl Renderer { "Horizontal Blur", target.horizontal_blurs.len(), ); - for (_, batch) in &target.alpha_batcher.text_run_cache_prims { - debug_target.add( - debug_server::BatchKind::Cache, - "Text Shadow", - batch.len(), - ); - } - for batch in target - .alpha_batcher - .batch_list - .opaque_batch_list - .batches - .iter() - .rev() - { - debug_target.add( - debug_server::BatchKind::Opaque, - batch.key.kind.debug_name(), - batch.instances.len(), - ); - } + for alpha_batch_container in &target.alpha_batch_containers { + for (_, batch) in &alpha_batch_container.text_run_cache_prims { + debug_target.add( + debug_server::BatchKind::Cache, + "Text Shadow", + batch.len(), + ); + } - for batch in &target.alpha_batcher.batch_list.alpha_batch_list.batches { - debug_target.add( - debug_server::BatchKind::Alpha, - batch.key.kind.debug_name(), - batch.instances.len(), - ); + for batch in alpha_batch_container + .opaque_batches + .iter() + .rev() { + debug_target.add( + debug_server::BatchKind::Opaque, + batch.key.kind.debug_name(), + batch.instances.len(), + ); + } + + for batch in &alpha_batch_container + .alpha_batches { + debug_target.add( + debug_server::BatchKind::Alpha, + batch.key.kind.debug_name(), + batch.instances.len(), + ); + } } debug_target @@ -2674,9 +2673,6 @@ impl Renderer { DebugCommand::EnableRenderTargetDebug(enable) => { self.set_debug_flag(DebugFlags::RENDER_TARGET_DBG, enable); } - DebugCommand::EnableAlphaRectsDebug(enable) => { - self.set_debug_flag(DebugFlags::ALPHA_PRIM_DBG, enable); - } DebugCommand::EnableGpuTimeQueries(enable) => { self.set_debug_flag(DebugFlags::GPU_TIME_QUERIES, enable); } @@ -3166,6 +3162,7 @@ impl Renderer { render_target: Option<(&Texture, i32)>, framebuffer_size: DeviceUintSize, stats: &mut RendererStats, + scissor_rect: Option, ) { match key.kind { BatchKind::Composite { .. } => { @@ -3302,6 +3299,10 @@ impl Renderer { // Handle special case readback for composites. if let BatchKind::Composite { task_id, source_id, backdrop_id } = key.kind { + if scissor_rect.is_some() { + self.device.disable_scissor(); + } + // composites can't be grouped together because // they may overlap and affect each other. debug_assert_eq!(instances.len(), 1); @@ -3360,6 +3361,10 @@ impl Renderer { // Restore draw target to current pass render target + layer. // Note: leaving the viewport unchanged, it's not a part of FBO state self.device.bind_draw_target(render_target, None); + + if scissor_rect.is_some() { + self.device.enable_scissor(); + } } let _timer = self.gpu_profile.start_timer(key.kind.gpu_sampler_tag()); @@ -3547,45 +3552,46 @@ impl Renderer { // considering using this for (some) other text runs, since // it removes the overhead of submitting many small glyphs // to multiple tiles in the normal text run case. - if !target.alpha_batcher.text_run_cache_prims.is_empty() { - self.device.set_blend(true); - self.device.set_blend_mode_premultiplied_alpha(); + for alpha_batch_container in &target.alpha_batch_containers { + if !alpha_batch_container.text_run_cache_prims.is_empty() { + self.device.set_blend(true); + self.device.set_blend_mode_premultiplied_alpha(); - let _timer = self.gpu_profile.start_timer(GPU_TAG_CACHE_TEXT_RUN); - self.cs_text_run - .bind(&mut self.device, projection, 0, &mut self.renderer_errors); - for (texture_id, instances) in &target.alpha_batcher.text_run_cache_prims { - self.draw_instanced_batch( - instances, - VertexArrayKind::Primitive, - &BatchTextures::color(*texture_id), - stats, - ); + let _timer = self.gpu_profile.start_timer(GPU_TAG_CACHE_TEXT_RUN); + self.cs_text_run + .bind(&mut self.device, projection, 0, &mut self.renderer_errors); + for (texture_id, instances) in &alpha_batch_container.text_run_cache_prims { + self.draw_instanced_batch( + instances, + VertexArrayKind::Primitive, + &BatchTextures::color(*texture_id), + stats, + ); + } } } //TODO: record the pixel count for cached primitives - if !target.alpha_batcher.is_empty() { - let _gl = self.gpu_profile.start_marker("alpha batches"); + if target.needs_depth() { + let _gl = self.gpu_profile.start_marker("opaque batches"); + let opaque_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_OPAQUE); self.device.set_blend(false); - let mut prev_blend_mode = BlendMode::None; - - if target.needs_depth() { - let opaque_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_OPAQUE); - - //Note: depth equality is needed for split planes - self.device.set_depth_func(DepthFunction::LessEqual); - self.device.enable_depth(); - self.device.enable_depth_write(); + //Note: depth equality is needed for split planes + self.device.set_depth_func(DepthFunction::LessEqual); + self.device.enable_depth(); + self.device.enable_depth_write(); + + for alpha_batch_container in &target.alpha_batch_containers { + if let Some(target_rect) = alpha_batch_container.target_rect { + self.device.enable_scissor(); + self.device.set_scissor_rect(target_rect); + } // Draw opaque batches front-to-back for maximum // z-buffer efficiency! - for batch in target - .alpha_batcher - .batch_list - .opaque_batch_list - .batches + for batch in alpha_batch_container + .opaque_batches .iter() .rev() { @@ -3597,32 +3603,31 @@ impl Renderer { render_target, target_size, stats, + alpha_batch_container.target_rect, ); } - self.device.disable_depth_write(); - self.gpu_profile.finish_sampler(opaque_sampler); + if alpha_batch_container.target_rect.is_some() { + self.device.disable_scissor(); + } } - let transparent_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_TRANSPARENT); - - for batch in &target.alpha_batcher.batch_list.alpha_batch_list.batches { - if self.debug_flags.contains(DebugFlags::ALPHA_PRIM_DBG) { - let color = match batch.key.blend_mode { - BlendMode::None => debug_colors::BLACK, - BlendMode::Alpha | - BlendMode::PremultipliedAlpha => debug_colors::GREY, - BlendMode::PremultipliedDestOut => debug_colors::SALMON, - BlendMode::SubpixelConstantTextColor(..) => debug_colors::GREEN, - BlendMode::SubpixelVariableTextColor => debug_colors::RED, - BlendMode::SubpixelWithBgColor => debug_colors::BLUE, - BlendMode::SubpixelDualSource => debug_colors::YELLOW, - }.into(); - for item_rect in &batch.item_rects { - self.debug.add_rect(item_rect, color); - } - } + self.device.disable_depth_write(); + self.gpu_profile.finish_sampler(opaque_sampler); + } + + let _gl = self.gpu_profile.start_marker("alpha batches"); + let transparent_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_TRANSPARENT); + self.device.set_blend(false); + let mut prev_blend_mode = BlendMode::None; + + for alpha_batch_container in &target.alpha_batch_containers { + if let Some(target_rect) = alpha_batch_container.target_rect { + self.device.enable_scissor(); + self.device.set_scissor_rect(target_rect); + } + for batch in &alpha_batch_container.alpha_batches { match batch.key.kind { BatchKind::Transformable(transform_kind, TransformBatchKind::TextRun(glyph_format)) => { // Text run batches are handled by this special case branch. @@ -3836,16 +3841,21 @@ impl Renderer { render_target, target_size, stats, + alpha_batch_container.target_rect, ); } } } - self.device.disable_depth(); - self.device.set_blend(false); - self.gpu_profile.finish_sampler(transparent_sampler); + if alpha_batch_container.target_rect.is_some() { + self.device.disable_scissor(); + } } + self.device.disable_depth(); + self.device.set_blend(false); + self.gpu_profile.finish_sampler(transparent_sampler); + // For any registered image outputs on this render target, // get the texture from caller and blit it. for output in &target.outputs { diff --git a/webrender/src/tiling.rs b/webrender/src/tiling.rs index 977fd43b24..2a4f4c8b14 100644 --- a/webrender/src/tiling.rs +++ b/webrender/src/tiling.rs @@ -6,7 +6,7 @@ use api::{ClipId, ColorF, DeviceIntPoint, DeviceIntRect, DeviceIntSize}; use api::{DevicePixelScale, DeviceUintPoint, DeviceUintRect, DeviceUintSize}; use api::{DocumentLayer, FilterOp, ImageFormat}; use api::{LayerRect, MixBlendMode, PipelineId}; -use batch::{AlphaBatcher, ClipBatcher, resolve_image}; +use batch::{AlphaBatchBuilder, AlphaBatchContainer, ClipBatcher, resolve_image}; use clip::{ClipStore}; use clip_scroll_tree::{ClipScrollTree}; use device::{FrameId, Texture}; @@ -269,7 +269,7 @@ pub struct BlitJob { #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct ColorRenderTarget { - pub alpha_batcher: AlphaBatcher, + pub alpha_batch_containers: Vec, // List of blur operations to apply for this render target. pub vertical_blurs: Vec, pub horizontal_blurs: Vec, @@ -280,6 +280,7 @@ pub struct ColorRenderTarget { pub outputs: Vec, allocator: Option, alpha_tasks: Vec, + screen_size: DeviceIntSize, } impl RenderTarget for ColorRenderTarget { @@ -295,7 +296,7 @@ impl RenderTarget for ColorRenderTarget { screen_size: DeviceIntSize, ) -> Self { ColorRenderTarget { - alpha_batcher: AlphaBatcher::new(screen_size), + alpha_batch_containers: Vec::new(), vertical_blurs: Vec::new(), horizontal_blurs: Vec::new(), readbacks: Vec::new(), @@ -304,6 +305,7 @@ impl RenderTarget for ColorRenderTarget { allocator: size.map(TextureAllocator::new), outputs: Vec::new(), alpha_tasks: Vec::new(), + screen_size, } } @@ -314,13 +316,39 @@ impl RenderTarget for ColorRenderTarget { render_tasks: &mut RenderTaskTree, deferred_resolves: &mut Vec, ) { - self.alpha_batcher.build( - &self.alpha_tasks, - ctx, - gpu_cache, - render_tasks, - deferred_resolves, - ); + let mut merged_batches = AlphaBatchContainer::new(None); + + for task_id in &self.alpha_tasks { + let task = &render_tasks[*task_id]; + + match task.kind { + RenderTaskKind::Picture(ref pic_task) => { + let pic_index = ctx.prim_store.cpu_metadata[pic_task.prim_index.0].cpu_prim_index; + let pic = &ctx.prim_store.cpu_pictures[pic_index.0]; + let (target_rect, _) = task.get_target_rect(); + + let mut batch_builder = AlphaBatchBuilder::new(self.screen_size, target_rect); + + batch_builder.add_pic_to_batch( + pic, + *task_id, + ctx, + gpu_cache, + render_tasks, + deferred_resolves, + ); + + if let Some(batch_container) = batch_builder.build(&mut merged_batches) { + self.alpha_batch_containers.push(batch_container); + } + } + _ => { + unreachable!(); + } + } + } + + self.alpha_batch_containers.push(merged_batches); } fn add_task( @@ -444,7 +472,9 @@ impl RenderTarget for ColorRenderTarget { } fn needs_depth(&self) -> bool { - !self.alpha_batcher.batch_list.opaque_batch_list.batches.is_empty() + self.alpha_batch_containers.iter().any(|ab| { + !ab.opaque_batches.is_empty() + }) } } @@ -791,7 +821,7 @@ impl RenderPass { // correct target kind. (RenderTargetKind::Alpha, Some((texture_id, layer))) } - RenderTaskLocation::Fixed => { + RenderTaskLocation::Fixed(..) => { (RenderTargetKind::Color, None) } RenderTaskLocation::Dynamic(ref mut origin, size) => { diff --git a/webrender/src/util.rs b/webrender/src/util.rs index ab8135b6db..34ced28237 100644 --- a/webrender/src/util.rs +++ b/webrender/src/util.rs @@ -111,17 +111,11 @@ pub trait RectHelpers where Self: Sized, { - fn contains_rect(&self, other: &Self) -> bool; fn from_floats(x0: f32, y0: f32, x1: f32, y1: f32) -> Self; fn is_well_formed_and_nonempty(&self) -> bool; } impl RectHelpers for TypedRect { - fn contains_rect(&self, other: &Self) -> bool { - self.origin.x <= other.origin.x && self.origin.y <= other.origin.y && - self.max_x() >= other.max_x() && self.max_y() >= other.max_y() - } - fn from_floats(x0: f32, y0: f32, x1: f32, y1: f32) -> Self { TypedRect::new( TypedPoint2D::new(x0, y0), diff --git a/webrender_api/src/api.rs b/webrender_api/src/api.rs index 5a201ff219..58bfbe45a4 100644 --- a/webrender_api/src/api.rs +++ b/webrender_api/src/api.rs @@ -477,8 +477,6 @@ pub enum DebugCommand { EnableTextureCacheDebug(bool), /// Display intermediate render targets on screen. EnableRenderTargetDebug(bool), - /// Display alpha primitive rects. - EnableAlphaRectsDebug(bool), /// Display GPU timing results. EnableGpuTimeQueries(bool), /// Display GPU overdraw results diff --git a/wrench/src/main.rs b/wrench/src/main.rs index 1126b4b24c..4bbaf7d602 100644 --- a/wrench/src/main.rs +++ b/wrench/src/main.rs @@ -496,10 +496,6 @@ fn main() { wrench.renderer.toggle_debug_flags(DebugFlags::TEXTURE_CACHE_DBG); do_render = true; } - VirtualKeyCode::B => { - wrench.renderer.toggle_debug_flags(DebugFlags::ALPHA_PRIM_DBG); - do_render = true; - } VirtualKeyCode::S => { wrench.renderer.toggle_debug_flags(DebugFlags::COMPACT_PROFILER); do_render = true;