From acf6b152ffe5f594c4bdd653ffbae0e20d839c51 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Tue, 16 Jan 2018 12:28:30 -0500 Subject: [PATCH] Bug 1429806 - Update webrender to commit eb9e1702df4b6dc036b649b3dd32ccc4bfbe43bf. r=jrmuizel MozReview-Commit-ID: 7m0P5kgiWZF --HG-- extra : rebase_source : 4543ca28be94c735c24120892b5eb254afabd677 --- gfx/doc/README.webrender | 2 +- gfx/webrender/Cargo.toml | 2 +- gfx/webrender/examples/alpha_perf.rs | 86 ++++++ gfx/webrender/examples/basic.rs | 2 +- gfx/webrender/examples/blob.rs | 4 +- gfx/webrender/examples/common/boilerplate.rs | 11 + gfx/webrender/examples/frame_output.rs | 1 + gfx/webrender/examples/image_resize.rs | 2 + .../examples/texture_cache_stress.rs | 3 + gfx/webrender/examples/yuv.rs | 6 +- gfx/webrender/res/brush.glsl | 6 +- gfx/webrender/res/brush_image.glsl | 48 ++- gfx/webrender/res/cs_blur.glsl | 8 - gfx/webrender/res/prim_shared.glsl | 5 +- gfx/webrender/res/shared_border.glsl | 2 +- gfx/webrender/src/batch.rs | 139 +++++---- gfx/webrender/src/border.rs | 15 +- gfx/webrender/src/box_shadow.rs | 9 +- gfx/webrender/src/clip_scroll_node.rs | 47 +-- gfx/webrender/src/clip_scroll_tree.rs | 70 ++++- gfx/webrender/src/debug_render.rs | 2 +- gfx/webrender/src/device.rs | 87 +++--- gfx/webrender/src/frame.rs | 24 ++ gfx/webrender/src/frame_builder.rs | 64 ++-- gfx/webrender/src/glyph_rasterizer.rs | 18 +- gfx/webrender/src/gpu_types.rs | 7 +- gfx/webrender/src/picture.rs | 196 +++++++------ gfx/webrender/src/platform/macos/font.rs | 29 +- gfx/webrender/src/platform/unix/font.rs | 70 ++++- gfx/webrender/src/platform/windows/font.rs | 28 +- gfx/webrender/src/prim_store.rs | 204 ++++++------- gfx/webrender/src/profiler.rs | 1 + gfx/webrender/src/render_backend.rs | 8 +- gfx/webrender/src/render_task.rs | 275 ++++++++++++------ gfx/webrender/src/renderer.rs | 128 +++++--- gfx/webrender/src/resource_cache.rs | 33 ++- gfx/webrender/src/texture_cache.rs | 90 ++++-- gfx/webrender/src/tiling.rs | 158 ++++++---- gfx/webrender_api/src/display_item.rs | 21 ++ gfx/webrender_api/src/display_list.rs | 130 ++++++--- gfx/webrender_api/src/font.rs | 13 + gfx/webrender_api/src/image.rs | 4 +- 42 files changed, 1362 insertions(+), 696 deletions(-) create mode 100644 gfx/webrender/examples/alpha_perf.rs diff --git a/gfx/doc/README.webrender b/gfx/doc/README.webrender index 8aabc259b328..95c47a15a782 100644 --- a/gfx/doc/README.webrender +++ b/gfx/doc/README.webrender @@ -175,4 +175,4 @@ Troubleshooting tips: ------------------------------------------------------------------------------- The version of WebRender currently in the tree is: -722e8b104bf99d29d61ecb1fe456eebe89ad81fd +eb9e1702df4b6dc036b649b3dd32ccc4bfbe43bf diff --git a/gfx/webrender/Cargo.toml b/gfx/webrender/Cargo.toml index 0898e385086b..e3e36bdf9484 100644 --- a/gfx/webrender/Cargo.toml +++ b/gfx/webrender/Cargo.toml @@ -35,7 +35,7 @@ serde_json = { optional = true, version = "1.0" } serde = { optional = true, version = "1.0", features = ["serde_derive"] } image = { optional = true, version = "0.17" } base64 = { optional = true, version = "0.3.0" } -ron = { optional = true, version = "0.1.3" } +ron = { optional = true, version = "0.1.5" } [dev-dependencies] angle = {git = "https://github.com/servo/angle", branch = "servo"} diff --git a/gfx/webrender/examples/alpha_perf.rs b/gfx/webrender/examples/alpha_perf.rs new file mode 100644 index 000000000000..9a338323589b --- /dev/null +++ b/gfx/webrender/examples/alpha_perf.rs @@ -0,0 +1,86 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +extern crate euclid; +extern crate gleam; +extern crate glutin; +extern crate webrender; + +#[path = "common/boilerplate.rs"] +mod boilerplate; + +use boilerplate::{Example, HandyDandyRectBuilder}; +use std::cmp; +use webrender::api::*; + +struct App { + rect_count: usize, +} + +impl Example for App { + fn render( + &mut self, + _api: &RenderApi, + builder: &mut DisplayListBuilder, + _resources: &mut ResourceUpdates, + _framebuffer_size: DeviceUintSize, + _pipeline_id: PipelineId, + _document_id: DocumentId, + ) { + let bounds = (0, 0).to(1920, 1080); + let info = LayoutPrimitiveInfo { + local_clip: LocalClip::Rect(bounds), + .. LayoutPrimitiveInfo::new(bounds) + }; + + builder.push_stacking_context( + &info, + ScrollPolicy::Scrollable, + None, + TransformStyle::Flat, + None, + MixBlendMode::Normal, + Vec::new(), + ); + + for _ in 0 .. self.rect_count { + builder.push_rect(&info, ColorF::new(1.0, 1.0, 1.0, 0.05)); + } + + builder.pop_stacking_context(); + } + + fn on_event( + &mut self, + event: glutin::Event, + _api: &RenderApi, + _document_id: DocumentId + ) -> bool { + match event { + glutin::Event::KeyboardInput(glutin::ElementState::Pressed, _, Some(key)) => { + match key { + glutin::VirtualKeyCode::Right => { + self.rect_count += 1; + println!("rects = {}", self.rect_count); + } + glutin::VirtualKeyCode::Left => { + self.rect_count = cmp::max(self.rect_count, 1) - 1; + println!("rects = {}", self.rect_count); + } + _ => {} + }; + } + _ => (), + } + + true + } +} + +fn main() { + let mut app = App { + rect_count: 1, + }; + boilerplate::main_wrapper(&mut app, None); +} diff --git a/gfx/webrender/examples/basic.rs b/gfx/webrender/examples/basic.rs index 3b1a2a23356d..f985df56d6bc 100644 --- a/gfx/webrender/examples/basic.rs +++ b/gfx/webrender/examples/basic.rs @@ -212,7 +212,7 @@ impl Example for App { let image_mask_key = api.generate_image_key(); resources.add_image( image_mask_key, - ImageDescriptor::new(2, 2, ImageFormat::A8, true), + ImageDescriptor::new(2, 2, ImageFormat::R8, true), ImageData::new(vec![0, 80, 180, 255]), None, ); diff --git a/gfx/webrender/examples/blob.rs b/gfx/webrender/examples/blob.rs index d30600f800e1..64073a2ff546 100644 --- a/gfx/webrender/examples/blob.rs +++ b/gfx/webrender/examples/blob.rs @@ -85,7 +85,7 @@ fn render_blob( texels.push(color.r * checker + tc); texels.push(color.a * checker + tc); } - api::ImageFormat::A8 => { + api::ImageFormat::R8 => { texels.push(color.a * checker + tc); } _ => { @@ -265,6 +265,7 @@ impl Example for App { api::LayoutSize::new(500.0, 500.0), api::LayoutSize::new(0.0, 0.0), api::ImageRendering::Auto, + api::AlphaType::PremultipliedAlpha, blob_img1, ); @@ -274,6 +275,7 @@ impl Example for App { api::LayoutSize::new(200.0, 200.0), api::LayoutSize::new(0.0, 0.0), api::ImageRendering::Auto, + api::AlphaType::PremultipliedAlpha, blob_img2, ); diff --git a/gfx/webrender/examples/common/boilerplate.rs b/gfx/webrender/examples/common/boilerplate.rs index 2e0e8f9560d5..c0edce9f6f03 100644 --- a/gfx/webrender/examples/common/boilerplate.rs +++ b/gfx/webrender/examples/common/boilerplate.rs @@ -64,6 +64,9 @@ impl HandyDandyRectBuilder for (i32, i32) { pub trait Example { const TITLE: &'static str = "WebRender Sample App"; const PRECACHE_SHADERS: bool = false; + const WIDTH: u32 = 1920; + const HEIGHT: u32 = 1080; + fn render( &mut self, api: &RenderApi, @@ -103,6 +106,7 @@ pub fn main_wrapper( let window = glutin::WindowBuilder::new() .with_title(E::TITLE) .with_multitouch() + .with_dimensions(E::WIDTH, E::HEIGHT) .with_gl(glutin::GlRequest::GlThenGles { opengl_version: (3, 2), opengles_version: (3, 0), @@ -224,6 +228,13 @@ pub fn main_wrapper( ) => { renderer.toggle_debug_flags(webrender::DebugFlags::ALPHA_PRIM_DBG); } + glutin::Event::KeyboardInput( + glutin::ElementState::Pressed, + _, + Some(glutin::VirtualKeyCode::S), + ) => { + renderer.toggle_debug_flags(webrender::DebugFlags::COMPACT_PROFILER); + } glutin::Event::KeyboardInput( glutin::ElementState::Pressed, _, diff --git a/gfx/webrender/examples/frame_output.rs b/gfx/webrender/examples/frame_output.rs index 4532ce51119a..317ca1c77c26 100644 --- a/gfx/webrender/examples/frame_output.rs +++ b/gfx/webrender/examples/frame_output.rs @@ -164,6 +164,7 @@ impl Example for App { info.rect.size, LayoutSize::zero(), ImageRendering::Auto, + AlphaType::PremultipliedAlpha, self.external_image_key.unwrap() ); diff --git a/gfx/webrender/examples/image_resize.rs b/gfx/webrender/examples/image_resize.rs index 8aa427e8b953..d1099b70f728 100644 --- a/gfx/webrender/examples/image_resize.rs +++ b/gfx/webrender/examples/image_resize.rs @@ -59,6 +59,7 @@ impl Example for App { image_size, LayoutSize::zero(), ImageRendering::Auto, + AlphaType::PremultipliedAlpha, self.image_key, ); @@ -71,6 +72,7 @@ impl Example for App { image_size, LayoutSize::zero(), ImageRendering::Pixelated, + AlphaType::PremultipliedAlpha, self.image_key, ); diff --git a/gfx/webrender/examples/texture_cache_stress.rs b/gfx/webrender/examples/texture_cache_stress.rs index f2f9422179e4..8813c54dfc08 100644 --- a/gfx/webrender/examples/texture_cache_stress.rs +++ b/gfx/webrender/examples/texture_cache_stress.rs @@ -147,6 +147,7 @@ impl Example for App { image_size, LayoutSize::zero(), ImageRendering::Auto, + AlphaType::PremultipliedAlpha, *key, ); } @@ -162,6 +163,7 @@ impl Example for App { image_size, LayoutSize::zero(), ImageRendering::Auto, + AlphaType::PremultipliedAlpha, image_key, ); } @@ -177,6 +179,7 @@ impl Example for App { image_size, LayoutSize::zero(), ImageRendering::Auto, + AlphaType::PremultipliedAlpha, swap_key, ); self.swap_index = 1 - self.swap_index; diff --git a/gfx/webrender/examples/yuv.rs b/gfx/webrender/examples/yuv.rs index ffb31a903cab..9330e11c0911 100644 --- a/gfx/webrender/examples/yuv.rs +++ b/gfx/webrender/examples/yuv.rs @@ -192,7 +192,7 @@ impl Example for App { let yuv_chanel3 = api.generate_image_key(); resources.add_image( yuv_chanel1, - ImageDescriptor::new(100, 100, ImageFormat::A8, true), + ImageDescriptor::new(100, 100, ImageFormat::R8, true), ImageData::new(vec![127; 100 * 100]), None, ); @@ -204,13 +204,13 @@ impl Example for App { ); resources.add_image( yuv_chanel2_1, - ImageDescriptor::new(100, 100, ImageFormat::A8, true), + ImageDescriptor::new(100, 100, ImageFormat::R8, true), ImageData::new(vec![127; 100 * 100]), None, ); resources.add_image( yuv_chanel3, - ImageDescriptor::new(100, 100, ImageFormat::A8, true), + ImageDescriptor::new(100, 100, ImageFormat::R8, true), ImageData::new(vec![127; 100 * 100]), None, ); diff --git a/gfx/webrender/res/brush.glsl b/gfx/webrender/res/brush.glsl index 77023ef3a364..c06a7e6e34d6 100644 --- a/gfx/webrender/res/brush.glsl +++ b/gfx/webrender/res/brush.glsl @@ -23,6 +23,7 @@ struct BrushInstance { int clip_address; int z; int segment_index; + int edge_mask; ivec2 user_data; }; @@ -35,7 +36,8 @@ BrushInstance load_brush() { bi.scroll_node_id = aData0.z % 65536; bi.clip_address = aData0.w; bi.z = aData1.x; - bi.segment_index = aData1.y; + bi.segment_index = aData1.y & 0xffff; + bi.edge_mask = aData1.y >> 16; bi.user_data = aData1.zw; return bi; @@ -125,7 +127,7 @@ void main(void) { vLocalBounds = vec4(vec2(-1000000.0), vec2(1000000.0)); #endif } else { - bvec4 edge_mask = notEqual(int(segment_data[1].x) & ivec4(1, 2, 4, 8), ivec4(0)); + bvec4 edge_mask = notEqual(brush.edge_mask & ivec4(1, 2, 4, 8), ivec4(0)); vi = write_transform_vertex( local_segment_rect, brush_prim.local_rect, diff --git a/gfx/webrender/res/brush_image.glsl b/gfx/webrender/res/brush_image.glsl index ca10f3c7cbc3..379fd997f3f8 100644 --- a/gfx/webrender/res/brush_image.glsl +++ b/gfx/webrender/res/brush_image.glsl @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#define VECS_PER_SPECIFIC_BRUSH 0 +#define VECS_PER_SPECIFIC_BRUSH 1 #include shared,prim_shared,brush @@ -25,6 +25,16 @@ flat varying vec4 vColor; #define BRUSH_IMAGE_MIRROR 2 #ifdef WR_VERTEX_SHADER + +struct Picture { + vec4 color; +}; + +Picture fetch_picture(int address) { + vec4 data = fetch_from_resource_cache_1(address); + return Picture(data); +} + void brush_vs( int prim_address, vec2 local_pos, @@ -32,29 +42,35 @@ void brush_vs( ivec2 user_data, PictureTask pic_task ) { - // TODO(gw): For now, this brush_image shader is only - // being used to draw items from the intermediate - // surface cache (render tasks). In the future - // we can expand this to support items from - // the normal texture cache and unify this - // with the normal image shader. - BlurTask blur_task = fetch_blur_task(user_data.x); - vUv.z = blur_task.common_data.texture_layer_index; vImageKind = user_data.y; -#if defined WR_FEATURE_COLOR_TARGET + // TODO(gw): There's quite a bit of code duplication here, + // depending on which variation of brush image + // this is being used for. This is because only + // box-shadow pictures are currently supported + // as texture cacheable items. Once we port the + // drop-shadows and text-shadows to be cacheable, + // most of this code can be merged together. +#if defined WR_FEATURE_COLOR_TARGET || defined WR_FEATURE_COLOR_TARGET_ALPHA_MASK + BlurTask blur_task = fetch_blur_task(user_data.x); + vUv.z = blur_task.common_data.texture_layer_index; vec2 texture_size = vec2(textureSize(sColor0, 0).xy); -#elif defined WR_FEATURE_COLOR_TARGET_ALPHA_MASK - vec2 texture_size = vec2(textureSize(sColor0, 0).xy); - vColor = blur_task.color; -#else - vec2 texture_size = vec2(textureSize(sColor1, 0).xy); +#if defined WR_FEATURE_COLOR_TARGET_ALPHA_MASK vColor = blur_task.color; #endif - vec2 uv0 = blur_task.common_data.task_rect.p0; vec2 src_size = blur_task.common_data.task_rect.size * blur_task.scale_factor; vec2 uv1 = uv0 + blur_task.common_data.task_rect.size; +#else + Picture pic = fetch_picture(prim_address); + ImageResource uv_rect = fetch_image_resource(user_data.x); + vec2 texture_size = vec2(textureSize(sColor1, 0).xy); + vColor = pic.color; + vec2 uv0 = uv_rect.uv_rect.xy; + vec2 uv1 = uv_rect.uv_rect.zw; + vec2 src_size = (uv1 - uv0) * uv_rect.user_data.x; + vUv.z = uv_rect.layer; +#endif // TODO(gw): In the future we'll probably draw these as segments // with the brush shader. When that occurs, we can diff --git a/gfx/webrender/res/cs_blur.glsl b/gfx/webrender/res/cs_blur.glsl index fabc548536b0..830c529f1da5 100644 --- a/gfx/webrender/res/cs_blur.glsl +++ b/gfx/webrender/res/cs_blur.glsl @@ -20,7 +20,6 @@ flat varying int vBlurRadius; in int aBlurRenderTaskAddress; in int aBlurSourceTaskAddress; in int aBlurDirection; -in vec4 aBlurRegion; void main(void) { BlurTask blur_task = fetch_blur_task(aBlurRenderTaskAddress); @@ -51,13 +50,6 @@ void main(void) { src_rect.p0 + src_rect.size - vec2(0.5)); vUvRect /= texture_size.xyxy; - if (aBlurRegion.z > 0.0) { - vec4 blur_region = aBlurRegion * uDevicePixelRatio; - src_rect = RectWithSize(src_rect.p0 + blur_region.xy, blur_region.zw); - target_rect.p0 = target_rect.p0 + blur_region.xy; - target_rect.size = blur_region.zw; - } - vec2 pos = target_rect.p0 + target_rect.size * aPosition.xy; vec2 uv0 = src_rect.p0 / texture_size; diff --git a/gfx/webrender/res/prim_shared.glsl b/gfx/webrender/res/prim_shared.glsl index 14ca38a9330f..ca1565a3b0aa 100644 --- a/gfx/webrender/res/prim_shared.glsl +++ b/gfx/webrender/res/prim_shared.glsl @@ -694,17 +694,18 @@ GlyphResource fetch_glyph_resource(int address) { struct ImageResource { vec4 uv_rect; float layer; + vec3 user_data; }; ImageResource fetch_image_resource(int address) { //Note: number of blocks has to match `renderer::BLOCKS_PER_UV_RECT` vec4 data[2] = fetch_from_resource_cache_2(address); - return ImageResource(data[0], data[1].x); + return ImageResource(data[0], data[1].x, data[1].yzw); } ImageResource fetch_image_resource_direct(ivec2 address) { vec4 data[2] = fetch_from_resource_cache_2_direct(address); - return ImageResource(data[0], data[1].x); + return ImageResource(data[0], data[1].x, data[1].yzw); } struct TextRun { diff --git a/gfx/webrender/res/shared_border.glsl b/gfx/webrender/res/shared_border.glsl index adabb1ea7cd7..c1ab34e312f5 100644 --- a/gfx/webrender/res/shared_border.glsl +++ b/gfx/webrender/res/shared_border.glsl @@ -47,7 +47,7 @@ vec4 get_effective_border_widths(Border border, int style) { // for now - we can adjust this if we find other browsers pick // different values in some cases. // SEE: https://drafts.csswg.org/css-backgrounds-3/#double - return floor(0.5 + border.widths / 3.0); + return max(floor(0.5 + border.widths / 3.0), 1.0); case BORDER_STYLE_GROOVE: case BORDER_STYLE_RIDGE: return floor(0.5 + border.widths * 0.5); diff --git a/gfx/webrender/src/batch.rs b/gfx/webrender/src/batch.rs index 0f158dc79341..be7c10dbe3a5 100644 --- a/gfx/webrender/src/batch.rs +++ b/gfx/webrender/src/batch.rs @@ -2,11 +2,11 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{DeviceIntRect, DeviceIntSize, ImageKey, LayerToWorldScale}; +use api::{AlphaType, DeviceIntRect, DeviceIntSize, ImageKey, LayerToWorldScale}; use api::{ExternalImageType, FilterOp, ImageRendering, LayerRect}; use api::{SubpixelDirection, TileOffset, YuvColorSpace, YuvFormat}; use api::{LayerToWorldTransform, WorldPixel}; -use border::{BorderCornerInstance, BorderCornerSide}; +use border::{BorderCornerInstance, BorderCornerSide, BorderEdgeKind}; use clip::{ClipSource, ClipStore}; use clip_scroll_tree::{CoordinateSystemId}; use euclid::{TypedTransform3D, vec3}; @@ -16,10 +16,10 @@ use gpu_types::{BrushImageKind, BrushInstance, ClipChainRectIndex}; use gpu_types::{ClipMaskInstance, ClipScrollNodeIndex, PictureType}; use gpu_types::{CompositePrimitiveInstance, PrimitiveInstance, SimplePrimitiveInstance}; use internal_types::{FastHashMap, SourceTexture}; -use picture::{PictureCompositeMode, PictureKind, PicturePrimitive}; +use picture::{PictureCompositeMode, PictureKind, PicturePrimitive, PictureSurface}; use plane_split::{BspSplitter, Polygon, Splitter}; use prim_store::{PrimitiveIndex, PrimitiveKind, PrimitiveMetadata, PrimitiveStore}; -use prim_store::{BrushPrimitive, BrushKind, DeferredResolve, PrimitiveRun}; +use prim_store::{BrushPrimitive, BrushKind, DeferredResolve, EdgeAaSegmentMask, PrimitiveRun}; use render_task::{ClipWorkItem}; use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskKind}; use render_task::{RenderTaskTree}; @@ -109,7 +109,7 @@ impl BatchTextures { pub fn color(texture: SourceTexture) -> Self { BatchTextures { - colors: [texture, SourceTexture::Invalid, SourceTexture::Invalid], + colors: [texture, texture, SourceTexture::Invalid], } } } @@ -344,6 +344,7 @@ impl BatchList { self.opaque_batch_list .get_suitable_batch(key, item_bounding_rect) } + BlendMode::Alpha | BlendMode::PremultipliedAlpha | BlendMode::PremultipliedDestOut | BlendMode::SubpixelConstantTextColor(..) | @@ -365,7 +366,6 @@ impl BatchList { pub struct AlphaBatcher { pub batch_list: BatchList, pub text_run_cache_prims: FastHashMap>, - pub line_cache_prims: Vec, glyph_fetch_buffer: Vec, } @@ -375,7 +375,6 @@ impl AlphaBatcher { batch_list: BatchList::new(screen_size), glyph_fetch_buffer: Vec::new(), text_run_cache_prims: FastHashMap::default(), - line_cache_prims: Vec::new(), } } @@ -468,7 +467,12 @@ 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 source_task_address = render_tasks.get_task_address(pic.render_task_id.expect("bug")); + + let render_task_id = match pic.surface { + Some(PictureSurface::RenderTask(render_task_id)) => render_task_id, + Some(PictureSurface::TextureCache(..)) | None => panic!("BUG: unexpected surface in splitting"), + }; + let source_task_address = render_tasks.get_task_address(render_task_id); let gpu_address = gpu_handle.as_int(gpu_cache); let instance = CompositePrimitiveInstance::new( @@ -646,37 +650,13 @@ impl AlphaBatcher { } let batch = self.batch_list.get_suitable_batch(edge_key, item_bounding_rect); - for border_segment in 0 .. 4 { - batch.push(base_instance.build(border_segment, 0, 0)); - } - } - PrimitiveKind::Line => { - let base_instance = BrushInstance { - picture_address: task_address, - prim_address: prim_cache_address, - clip_chain_rect_index, - scroll_id, - clip_task_address, - z, - segment_index: 0, - user_data0: 0, - user_data1: 0, - }; - - let instance = PrimitiveInstance::from(base_instance); - - match pic_type { - PictureType::TextShadow => { - self.line_cache_prims.push(instance); + for (border_segment, instance_kind) in border_cpu.edges.iter().enumerate() { + match *instance_kind { + BorderEdgeKind::None => {}, + _ => { + batch.push(base_instance.build(border_segment as i32, 0, 0)); + } } - PictureType::Image => { - let kind = - BatchKind::Brush(BrushBatchKind::Line); - let key = BatchKey::new(kind, blend_mode, no_textures); - let batch = self.batch_list.get_suitable_batch(key, item_bounding_rect); - batch.push(instance); - } - PictureType::BoxShadow => unreachable!(), } } PrimitiveKind::Image => { @@ -806,7 +786,7 @@ impl AlphaBatcher { } else if ctx.use_dual_source_blending { BlendMode::SubpixelDualSource } else { - BlendMode::SubpixelConstantTextColor(text_cpu.font.color.into()) + BlendMode::SubpixelConstantTextColor(text_cpu.get_color()) } } GlyphFormat::Alpha | @@ -833,8 +813,39 @@ impl AlphaBatcher { let picture = &ctx.prim_store.cpu_pictures[prim_metadata.cpu_prim_index.0]; - match picture.render_task_id { - Some(cache_task_id) => { + match picture.surface { + Some(PictureSurface::TextureCache(ref cache_item)) => { + match picture.kind { + PictureKind::TextShadow { .. } | + PictureKind::Image { .. } => { + panic!("BUG: only supported as render tasks for now"); + } + PictureKind::BoxShadow { image_kind, .. } => { + let textures = BatchTextures::color(cache_item.texture_id); + let kind = BatchKind::Brush( + BrushBatchKind::Image( + 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 instance = BrushInstance { + picture_address: task_address, + prim_address: prim_cache_address, + clip_chain_rect_index, + scroll_id, + clip_task_address, + z, + segment_index: 0, + edge_flags: EdgeAaSegmentMask::empty(), + user_data0: cache_item.uv_rect_handle.as_int(gpu_cache), + user_data1: image_kind as i32, + }; + batch.push(PrimitiveInstance::from(instance)); + } + } + } + Some(PictureSurface::RenderTask(cache_task_id)) => { let cache_task_address = render_tasks.get_task_address(cache_task_id); let textures = BatchTextures::render_target_cache(); @@ -855,31 +866,14 @@ impl AlphaBatcher { clip_task_address, z, segment_index: 0, + edge_flags: EdgeAaSegmentMask::empty(), user_data0: cache_task_address.0 as i32, user_data1: BrushImageKind::Simple as i32, }; batch.push(PrimitiveInstance::from(instance)); } - PictureKind::BoxShadow { image_kind, .. } => { - let kind = BatchKind::Brush( - BrushBatchKind::Image( - 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 instance = BrushInstance { - picture_address: task_address, - prim_address: prim_cache_address, - clip_chain_rect_index, - scroll_id, - clip_task_address, - z, - segment_index: 0, - user_data0: cache_task_address.0 as i32, - user_data1: image_kind as i32, - }; - batch.push(PrimitiveInstance::from(instance)); + PictureKind::BoxShadow { .. } => { + panic!("BUG: should be handled as a texture cache surface"); } PictureKind::Image { composite_mode, @@ -914,7 +908,7 @@ impl AlphaBatcher { // This will allow us to unify some of the shaders, apply clip masks // when compositing pictures, and also correctly apply pixel snapping // to picture compositing operations. - let source_id = picture.render_task_id.expect("no source!?"); + let source_id = cache_task_id; match composite_mode.expect("bug: only composites here") { PictureCompositeMode::Filter(filter) => { @@ -954,6 +948,7 @@ impl AlphaBatcher { clip_task_address, z, segment_index: 0, + edge_flags: EdgeAaSegmentMask::empty(), user_data0: cache_task_address.0 as i32, user_data1: BrushImageKind::Simple as i32, }; @@ -1244,6 +1239,7 @@ impl AlphaBatcher { clip_task_address, z, segment_index: 0, + edge_flags: EdgeAaSegmentMask::empty(), user_data0: 0, user_data1: 0, }; @@ -1275,6 +1271,7 @@ impl AlphaBatcher { let instance = PrimitiveInstance::from(BrushInstance { segment_index: i as i32, + edge_flags: segment.edge_flags, clip_task_address, ..base_instance }); @@ -1297,6 +1294,13 @@ impl AlphaBatcher { impl BrushPrimitive { fn get_batch_key(&self, blend_mode: BlendMode) -> BatchKey { match self.kind { + BrushKind::Line { .. } => { + BatchKey::new( + BatchKind::Brush(BrushBatchKind::Line), + blend_mode, + BatchTextures::no_texture(), + ) + } BrushKind::Solid { .. } => { BatchKey::new( BatchKind::Brush(BrushBatchKind::Solid), @@ -1339,18 +1343,25 @@ impl AlphaBatchHelpers for PrimitiveStore { // Can only resolve the TextRun's blend mode once glyphs are fetched. PrimitiveKind::TextRun => BlendMode::PremultipliedAlpha, PrimitiveKind::Border | - PrimitiveKind::Image | PrimitiveKind::YuvImage | PrimitiveKind::AlignedGradient | PrimitiveKind::AngleGradient | PrimitiveKind::RadialGradient | - PrimitiveKind::Line | PrimitiveKind::Brush | PrimitiveKind::Picture => if needs_blending { BlendMode::PremultipliedAlpha } else { BlendMode::None }, + PrimitiveKind::Image => if needs_blending { + let image_cpu = &self.cpu_images[metadata.cpu_prim_index.0]; + match image_cpu.alpha_type { + AlphaType::PremultipliedAlpha => BlendMode::PremultipliedAlpha, + AlphaType::Alpha => BlendMode::Alpha, + } + } else { + BlendMode::None + }, } } } diff --git a/gfx/webrender/src/border.rs b/gfx/webrender/src/border.rs index 9c4b1b168858..c641b538aab0 100644 --- a/gfx/webrender/src/border.rs +++ b/gfx/webrender/src/border.rs @@ -149,6 +149,14 @@ impl NormalBorderHelpers for NormalBorder { BorderCornerKind::None } + // If one of the edges is none or hidden, we just draw one style. + (BorderStyle::None, _) | + (_, BorderStyle::None) | + (BorderStyle::Hidden, _) | + (_, BorderStyle::Hidden) => { + BorderCornerKind::Clip(BorderCornerInstance::Single) + } + // If both borders are solid, we can draw them with a simple rectangle if // both the colors match and there is no radius. (BorderStyle::Solid, BorderStyle::Solid) => { @@ -237,12 +245,12 @@ pub fn ensure_no_corner_overlap( let bottom_right_radius = &mut radius.bottom_right; let bottom_left_radius = &mut radius.bottom_left; - let sum = top_left_radius.width + bottom_left_radius.width; + let sum = top_left_radius.width + top_right_radius.width; if rect.size.width < sum { ratio = f32::min(ratio, rect.size.width / sum); } - let sum = top_right_radius.width + bottom_right_radius.width; + let sum = bottom_left_radius.width + bottom_right_radius.width; if rect.size.width < sum { ratio = f32::min(ratio, rect.size.width / sum); } @@ -280,6 +288,7 @@ impl FrameBuilder { widths: &BorderWidths, clip_and_scroll: ClipAndScrollInfo, corner_instances: [BorderCornerInstance; 4], + edges: [BorderEdgeKind; 4], clip_sources: Vec, ) { let radius = &border.radius; @@ -296,6 +305,7 @@ impl FrameBuilder { let prim_cpu = BorderPrimitiveCpu { corner_instances, + edges, // TODO(gw): In the future, we will build these on demand // from the deserialized display list, rather @@ -536,6 +546,7 @@ impl FrameBuilder { widths, clip_and_scroll, corner_instances, + edges, extra_clips, ); } diff --git a/gfx/webrender/src/box_shadow.rs b/gfx/webrender/src/box_shadow.rs index 2a3a06601db9..4462d85c4101 100644 --- a/gfx/webrender/src/box_shadow.rs +++ b/gfx/webrender/src/box_shadow.rs @@ -83,6 +83,9 @@ impl FrameBuilder { .inflate(spread_amount, spread_amount); if blur_radius == 0.0 { + if box_offset.x == 0.0 && box_offset.y == 0.0 && spread_amount == 0.0 { + return; + } let mut clips = Vec::new(); let fast_info = match clip_mode { @@ -242,7 +245,6 @@ impl FrameBuilder { let mut pic_prim = PicturePrimitive::new_box_shadow( blur_radius, *color, - Vec::new(), clip_mode, image_kind, cache_key, @@ -301,7 +303,7 @@ impl FrameBuilder { inflate_size *= 2.0; } - let brush_rect = brush_rect.inflate(inflate_size, inflate_size); + let brush_rect = brush_rect.inflate(inflate_size + box_offset.x.abs(), inflate_size + box_offset.y.abs()); let brush_prim = BrushPrimitive::new( BrushKind::Mask { clip_mode: brush_clip_mode, @@ -321,7 +323,6 @@ impl FrameBuilder { let mut pic_prim = PicturePrimitive::new_box_shadow( blur_radius, *color, - Vec::new(), BoxShadowClipMode::Inset, // TODO(gw): Make use of optimization for inset. BrushImageKind::NinePatch, @@ -337,7 +338,7 @@ impl FrameBuilder { // rect to account for the inflate above. This // extra edge will be clipped by the local clip // rect set below. - let pic_rect = prim_info.rect.inflate(inflate_size, inflate_size); + let pic_rect = prim_info.rect.inflate(inflate_size + box_offset.x.abs(), inflate_size + box_offset.y.abs()); let pic_info = LayerPrimitiveInfo::with_clip_rect( pic_rect, prim_info.rect diff --git a/gfx/webrender/src/clip_scroll_node.rs b/gfx/webrender/src/clip_scroll_node.rs index 6b67f04a59e0..12fa6bc1a337 100644 --- a/gfx/webrender/src/clip_scroll_node.rs +++ b/gfx/webrender/src/clip_scroll_node.rs @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{ClipId, DeviceIntRect, DevicePixelScale, LayerPixel, LayerPoint, LayerRect, LayerSize}; +use api::{ClipId, DevicePixelScale, LayerPixel, LayerPoint, LayerRect, LayerSize}; use api::{LayerToWorldTransform, LayerTransform, LayerVector2D, LayoutTransform, LayoutVector2D}; use api::{PipelineId, PropertyBinding, ScrollClamping, ScrollEventPhase, ScrollLocation}; use api::{ScrollSensitivity, StickyOffsetBounds, WorldPoint}; @@ -12,11 +12,10 @@ use euclid::SideOffsets2D; use geometry::ray_intersects_rect; use gpu_cache::GpuCache; use gpu_types::{ClipScrollNodeIndex, ClipScrollNodeData}; -use render_task::{ClipChain, ClipChainNode, ClipWorkItem}; +use render_task::{ClipChain, ClipWorkItem}; use resource_cache::ResourceCache; use scene::SceneProperties; use spring::{DAMPING, STIFFNESS, Spring}; -use std::rc::Rc; use util::{MatrixHelpers, TransformOrOffset, TransformedRectKind}; #[cfg(target_os = "macos")] @@ -107,10 +106,8 @@ pub struct ClipScrollNode { /// The type of this node and any data associated with that node type. pub node_type: NodeType, - /// The node in the chain of clips that are necessary to clip display items - /// that have this ClipScrollNode as their clip parent. This will be used to - /// generate clip tasks. - pub clip_chain_node: ClipChain, + /// The ClipChain that will be used if this node is used as the 'clipping node.' + pub clip_chain: Option, /// True if this node is transformed by an invertible transform. If not, display items /// transformed by this node will not be displayed and display items not transformed by this @@ -145,7 +142,7 @@ impl ClipScrollNode { children: Vec::new(), pipeline_id, node_type: node_type, - clip_chain_node: None, + clip_chain: None, invertible: true, coordinate_system_id: CoordinateSystemId(0), coordinate_system_relative_transform: TransformOrOffset::zero(), @@ -272,7 +269,7 @@ impl ClipScrollNode { self.invertible = false; self.world_content_transform = LayerToWorldTransform::identity(); self.world_viewport_transform = LayerToWorldTransform::identity(); - self.clip_chain_node = None; + self.clip_chain = None; } pub fn push_gpu_node_data(&mut self, node_data: &mut Vec) { @@ -300,7 +297,6 @@ impl ClipScrollNode { &mut self, state: &mut TransformUpdateState, next_coordinate_system_id: &mut CoordinateSystemId, - screen_rect: &DeviceIntRect, device_pixel_scale: DevicePixelScale, clip_store: &mut ClipStore, resource_cache: &mut ResourceCache, @@ -329,7 +325,6 @@ impl ClipScrollNode { self.update_clip_work_item( state, - screen_rect, device_pixel_scale, clip_store, resource_cache, @@ -340,21 +335,15 @@ impl ClipScrollNode { pub fn update_clip_work_item( &mut self, state: &mut TransformUpdateState, - screen_rect: &DeviceIntRect, device_pixel_scale: DevicePixelScale, clip_store: &mut ClipStore, resource_cache: &mut ResourceCache, gpu_cache: &mut GpuCache, ) { - let current_clip_chain = state.parent_clip_chain.clone(); - let combined_outer_screen_rect = current_clip_chain.as_ref().map_or( - *screen_rect, |clip| clip.combined_outer_screen_rect, - ); - let clip_sources_handle = match self.node_type { NodeType::Clip(ref handle) => handle, _ => { - self.clip_chain_node = current_clip_chain.clone(); + self.clip_chain = Some(state.parent_clip_chain.clone()); self.invertible = true; return; } @@ -376,8 +365,8 @@ impl ClipScrollNode { // If this clip's inner rectangle completely surrounds the existing clip // chain's outer rectangle, we can discard this clip entirely since it isn't // going to affect anything. - if screen_inner_rect.contains_rect(&combined_outer_screen_rect) { - self.clip_chain_node = current_clip_chain; + if screen_inner_rect.contains_rect(&state.parent_clip_chain.combined_outer_screen_rect) { + self.clip_chain = Some(state.parent_clip_chain.clone()); return; } @@ -387,16 +376,15 @@ impl ClipScrollNode { coordinate_system_id: state.current_coordinate_system_id, }; - let clip_chain_node = ClipChainNode::new( + let clip_chain = state.parent_clip_chain.new_with_added_node( work_item, self.coordinate_system_relative_transform.apply(&local_outer_rect), screen_outer_rect, screen_inner_rect, - current_clip_chain ); - self.clip_chain_node = Some(Rc::new(clip_chain_node)); - state.parent_clip_chain = self.clip_chain_node.clone(); + self.clip_chain = Some(clip_chain.clone()); + state.parent_clip_chain = clip_chain; } pub fn update_transform( @@ -613,7 +601,6 @@ impl ClipScrollNode { pub fn prepare_state_for_children(&self, state: &mut TransformUpdateState) { if !self.invertible { state.invertible = false; - state.parent_clip_chain = None; return; } @@ -778,16 +765,6 @@ impl ClipScrollNode { _ => false, } } - - pub fn is_visible(&self) -> bool { - if !self.invertible { - return false; - } - match self.clip_chain_node { - Some(ref node) if node.combined_outer_screen_rect.is_empty() => false, - _ => true, - } - } } #[derive(Copy, Clone, Debug)] diff --git a/gfx/webrender/src/clip_scroll_tree.rs b/gfx/webrender/src/clip_scroll_tree.rs index 71936c972daa..058dfc5a9137 100644 --- a/gfx/webrender/src/clip_scroll_tree.rs +++ b/gfx/webrender/src/clip_scroll_tree.rs @@ -2,9 +2,9 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{ClipId, DeviceIntRect, DevicePixelScale, LayerPoint, LayerRect}; -use api::{LayerToWorldTransform, LayerVector2D, PipelineId, ScrollClamping, ScrollEventPhase}; -use api::{PropertyBinding, LayoutTransform, ScrollLayerState, ScrollLocation, WorldPoint}; +use api::{ClipId, ClipChainId, DeviceIntRect, DevicePixelScale, LayerPoint, LayerRect}; +use api::{LayerToWorldTransform, LayerVector2D, LayoutTransform, PipelineId, PropertyBinding}; +use api::{ScrollClamping, ScrollEventPhase, ScrollLayerState, ScrollLocation, WorldPoint}; use clip::ClipStore; use clip_scroll_node::{ClipScrollNode, NodeType, ScrollingState, StickyFrameInfo}; use gpu_cache::GpuCache; @@ -40,8 +40,23 @@ impl CoordinateSystemId { } } +struct ClipChainDescriptor { + id: ClipChainId, + parent: Option, + clips: Vec, +} + pub struct ClipScrollTree { pub nodes: FastHashMap, + + /// A Vec of all descriptors that describe ClipChains in the order in which they are + /// encountered during display list flattening. ClipChains are expected to never be + /// the children of ClipChains later in the list. + clip_chains_descriptors: Vec, + + /// A HashMap of built ClipChains that are described by `clip_chains_descriptors`. + pub clip_chains: FastHashMap, + pub pending_scroll_offsets: FastHashMap, /// The ClipId of the currently scrolling node. Used to allow the same @@ -94,6 +109,8 @@ impl ClipScrollTree { let dummy_pipeline = PipelineId::dummy(); ClipScrollTree { nodes: FastHashMap::default(), + clip_chains_descriptors: Vec::new(), + clip_chains: FastHashMap::default(), pending_scroll_offsets: FastHashMap::default(), currently_scrolling_node_id: None, root_reference_frame_id: ClipId::root_reference_frame(dummy_pipeline), @@ -243,6 +260,8 @@ impl ClipScrollTree { } self.pipelines_to_discard.clear(); + self.clip_chains.clear(); + self.clip_chains_descriptors.clear(); scroll_states } @@ -359,7 +378,7 @@ impl ClipScrollTree { parent_accumulated_scroll_offset: LayerVector2D::zero(), nearest_scrolling_ancestor_offset: LayerVector2D::zero(), nearest_scrolling_ancestor_viewport: LayerRect::zero(), - parent_clip_chain: None, + parent_clip_chain: ClipChain::empty(screen_rect), current_coordinate_system_id: CoordinateSystemId::root(), coordinate_system_relative_transform: TransformOrOffset::zero(), invertible: true, @@ -369,7 +388,6 @@ impl ClipScrollTree { root_reference_frame_id, &mut state, &mut next_coordinate_system_id, - screen_rect, device_pixel_scale, clip_store, resource_cache, @@ -377,6 +395,8 @@ impl ClipScrollTree { node_data, scene_properties, ); + + self.build_clip_chains(screen_rect); } fn update_node( @@ -384,7 +404,6 @@ impl ClipScrollTree { layer_id: ClipId, state: &mut TransformUpdateState, next_coordinate_system_id: &mut CoordinateSystemId, - screen_rect: &DeviceIntRect, device_pixel_scale: DevicePixelScale, clip_store: &mut ClipStore, resource_cache: &mut ResourceCache, @@ -407,7 +426,6 @@ impl ClipScrollTree { node.update( &mut state, next_coordinate_system_id, - screen_rect, device_pixel_scale, clip_store, resource_cache, @@ -421,7 +439,6 @@ impl ClipScrollTree { return; } - node.prepare_state_for_children(&mut state); node.children.clone() }; @@ -431,7 +448,6 @@ impl ClipScrollTree { child_node_id, &mut state, next_coordinate_system_id, - screen_rect, device_pixel_scale, clip_store, resource_cache, @@ -442,6 +458,25 @@ impl ClipScrollTree { } } + pub fn build_clip_chains(&mut self, screen_rect: &DeviceIntRect) { + for descriptor in &self.clip_chains_descriptors { + let mut chain = match descriptor.parent { + Some(id) => self.clip_chains[&id].clone(), + None => ClipChain::empty(screen_rect), + }; + + for clip_id in &descriptor.clips { + if let Some(ref node_chain) = self.nodes[&clip_id].clip_chain { + if let Some(ref nodes) = node_chain.nodes { + chain.add_node((**nodes).clone()); + } + } + } + + self.clip_chains.insert(descriptor.id, chain); + } + } + pub fn tick_scrolling_bounce_animations(&mut self) { for (_, node) in &mut self.nodes { node.tick_scrolling_bounce_animation() @@ -512,6 +547,15 @@ impl ClipScrollTree { self.add_node(node, id); } + pub fn add_clip_chain_descriptor( + &mut self, + id: ClipChainId, + parent: Option, + clips: Vec + ) { + self.clip_chains_descriptors.push(ClipChainDescriptor { id, parent, clips }); + } + pub fn add_node(&mut self, node: ClipScrollNode, id: ClipId) { // When the parent node is None this means we are adding the root. match node.parent { @@ -612,4 +656,12 @@ impl ClipScrollTree { .unwrap_or_else(|| WorldPoint::new(point.x, point.y)) } + + pub fn get_clip_chain(&self, id: &ClipId) -> Option<&ClipChain> { + match id { + &ClipId::ClipChain(clip_chain_id) => Some(&self.clip_chains[&clip_chain_id]), + _ => self.nodes[id].clip_chain.as_ref(), + } + } + } diff --git a/gfx/webrender/src/debug_render.rs b/gfx/webrender/src/debug_render.rs index 96463245e60c..5b5c8cafbeed 100644 --- a/gfx/webrender/src/debug_render.rs +++ b/gfx/webrender/src/debug_render.rs @@ -121,7 +121,7 @@ impl DebugRenderer { &mut font_texture, debug_font_data::BMP_WIDTH, debug_font_data::BMP_HEIGHT, - ImageFormat::A8, + ImageFormat::R8, TextureFilter::Linear, None, 1, diff --git a/gfx/webrender/src/device.rs b/gfx/webrender/src/device.rs index 0444ff32a346..4cc28c121c31 100644 --- a/gfx/webrender/src/device.rs +++ b/gfx/webrender/src/device.rs @@ -12,7 +12,6 @@ use smallvec::SmallVec; use std::cell::RefCell; use std::fs::File; use std::io::Read; -use std::iter::repeat; use std::marker::PhantomData; use std::mem; use std::ops::Add; @@ -21,6 +20,11 @@ use std::ptr; use std::rc::Rc; use std::thread; +// Apparently, in some cases calling `glTexImage3D` with +// similar parameters that the texture already has confuses +// Angle when running with optimizations. +const WORK_AROUND_TEX_IMAGE: bool = cfg!(windows); + #[derive(Debug, Copy, Clone, PartialEq, Ord, Eq, PartialOrd)] pub struct FrameId(usize); @@ -38,12 +42,6 @@ impl Add for FrameId { } } -#[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] -const GL_FORMAT_A: gl::GLuint = gl::RED; - -#[cfg(any(target_arch = "arm", target_arch = "aarch64"))] -const GL_FORMAT_A: gl::GLuint = gl::ALPHA; - const GL_FORMAT_BGRA_GL: gl::GLuint = gl::BGRA; const GL_FORMAT_BGRA_GLES: gl::GLuint = gl::BGRA_EXT; @@ -457,7 +455,7 @@ impl Texture { pub fn get_bpp(&self) -> u32 { match self.format { - ImageFormat::A8 => 1, + ImageFormat::R8 => 1, ImageFormat::BGRA8 => 4, ImageFormat::RG8 => 2, ImageFormat::RGBAF32 => 16, @@ -468,6 +466,10 @@ impl Texture { pub fn has_depth(&self) -> bool { self.depth_rb.is_some() } + + pub fn get_rt_info(&self) -> Option<&RenderTargetInfo> { + self.render_target.as_ref() + } } impl Drop for Texture { @@ -962,7 +964,7 @@ impl Device { self.bind_texture(DEFAULT_TEXTURE, texture); self.set_texture_parameters(texture.target, texture.filter); - self.update_texture_storage(texture, &rt_info, true); + self.update_texture_storage(texture, &rt_info, true, false); let rect = DeviceIntRect::new(DeviceIntPoint::zero(), old_size.to_i32()); for (read_fbo, &draw_fbo) in old_fbos.into_iter().zip(&texture.fbo_ids) { @@ -984,13 +986,13 @@ impl Device { filter: TextureFilter, render_target: Option, layer_count: i32, + pixels: Option<&[u8]>, ) { debug_assert!(self.inside_frame); - let resized = texture.width != width || - texture.height != height || - texture.format != format; + let is_resized = texture.width != width || texture.height != height; + let is_format_changed = texture.format != format; texture.format = format; texture.width = width; @@ -1005,26 +1007,12 @@ impl Device { match render_target { Some(info) => { assert!(pixels.is_none()); - self.update_texture_storage(texture, &info, resized); + self.update_texture_storage(texture, &info, is_resized, is_format_changed); } None => { let (internal_format, gl_format) = gl_texture_formats_for_image_format(self.gl(), format); let type_ = gl_type_for_texture_format(format); - let expanded_data: Vec; - let actual_pixels = if pixels.is_some() && format == ImageFormat::A8 && - cfg!(any(target_arch = "arm", target_arch = "aarch64")) - { - expanded_data = pixels - .unwrap() - .iter() - .flat_map(|&byte| repeat(byte).take(4)) - .collect(); - Some(expanded_data.as_slice()) - } else { - pixels - }; - match texture.target { gl::TEXTURE_2D_ARRAY => { self.gl.tex_image_3d( @@ -1037,7 +1025,7 @@ impl Device { 0, gl_format, type_, - actual_pixels, + pixels, ); } gl::TEXTURE_2D | gl::TEXTURE_RECTANGLE | gl::TEXTURE_EXTERNAL_OES => { @@ -1050,7 +1038,7 @@ impl Device { 0, gl_format, type_, - actual_pixels, + pixels, ); } _ => panic!("BUG: Unexpected texture target!"), @@ -1065,11 +1053,12 @@ impl Device { texture: &mut Texture, rt_info: &RenderTargetInfo, is_resized: bool, + is_format_changed: bool, ) { assert!(texture.layer_count > 0); let needed_layer_count = texture.layer_count - texture.fbo_ids.len() as i32; - let allocate_color = needed_layer_count != 0 || is_resized; + let allocate_color = needed_layer_count != 0 || is_resized || is_format_changed; if allocate_color { let (internal_format, gl_format) = @@ -1078,6 +1067,19 @@ impl Device { match texture.target { gl::TEXTURE_2D_ARRAY => { + if WORK_AROUND_TEX_IMAGE { + // reset the contents before resizing + self.gl.tex_image_3d( + texture.target, + 0, + gl::RGBA32F as _, + 2, 2, 1, + 0, + gl::RGBA, + gl::FLOAT, + None, + ) + } self.gl.tex_image_3d( texture.target, 0, @@ -1138,8 +1140,8 @@ impl Device { self.gl.renderbuffer_storage( gl::RENDERBUFFER, gl::DEPTH_COMPONENT24, - texture.width as gl::GLsizei, - texture.height as gl::GLsizei, + texture.width as _, + texture.height as _, ); } else { self.gl.delete_renderbuffers(&[depth_rb]); @@ -1149,6 +1151,7 @@ impl Device { } if allocate_color || allocate_depth { + let original_bound_fbo = self.bound_draw_fbo; for (fbo_index, &fbo_id) in texture.fbo_ids.iter().enumerate() { self.bind_external_draw_target(fbo_id); match texture.target { @@ -1158,7 +1161,7 @@ impl Device { gl::COLOR_ATTACHMENT0, texture.id, 0, - fbo_index as gl::GLint, + fbo_index as _, ) } _ => { @@ -1180,9 +1183,7 @@ impl Device { depth_rb, ); } - // restore the previous FBO - let bound_fbo = self.bound_draw_fbo; - self.bind_external_draw_target(bound_fbo); + self.bind_external_draw_target(original_bound_fbo); } } @@ -1859,6 +1860,12 @@ impl Device { } } + pub fn set_blend_mode_alpha(&self) { + self.gl.blend_func_separate(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA, + gl::ONE, gl::ONE); + self.gl.blend_equation(gl::FUNC_ADD); + } + pub fn set_blend_mode_premultiplied_alpha(&self) { self.gl.blend_func(gl::ONE, gl::ONE_MINUS_SRC_ALPHA); self.gl.blend_equation(gl::FUNC_ADD); @@ -1924,11 +1931,7 @@ fn gl_texture_formats_for_image_format( format: ImageFormat, ) -> (gl::GLint, gl::GLuint) { match format { - ImageFormat::A8 => if cfg!(any(target_arch = "arm", target_arch = "aarch64")) { - (get_gl_format_bgra(gl) as gl::GLint, get_gl_format_bgra(gl)) - } else { - (GL_FORMAT_A as gl::GLint, GL_FORMAT_A) - }, + ImageFormat::R8 => (gl::RED as gl::GLint, gl::RED), ImageFormat::BGRA8 => match gl.get_type() { gl::GlType::Gl => (gl::RGBA as gl::GLint, get_gl_format_bgra(gl)), gl::GlType::Gles => (get_gl_format_bgra(gl) as gl::GLint, get_gl_format_bgra(gl)), @@ -2053,7 +2056,7 @@ impl<'a, T> TextureUploader<'a, T> { impl<'a> UploadTarget<'a> { fn update_impl(&mut self, chunk: UploadChunk) { let (gl_format, bpp, data_type) = match self.texture.format { - ImageFormat::A8 => (GL_FORMAT_A, 1, gl::UNSIGNED_BYTE), + ImageFormat::R8 => (gl::RED, 1, gl::UNSIGNED_BYTE), ImageFormat::BGRA8 => (get_gl_format_bgra(self.gl), 4, gl::UNSIGNED_BYTE), ImageFormat::RG8 => (gl::RG, 2, gl::UNSIGNED_BYTE), ImageFormat::RGBAF32 => (gl::RGBA, 16, gl::FLOAT), diff --git a/gfx/webrender/src/frame.rs b/gfx/webrender/src/frame.rs index 31610f86c323..ab45111db9a7 100644 --- a/gfx/webrender/src/frame.rs +++ b/gfx/webrender/src/frame.rs @@ -74,6 +74,24 @@ impl<'a> FlattenContext<'a> { .collect() } + fn get_clip_chain_items( + &self, + pipeline_id: PipelineId, + items: ItemRange, + ) -> Vec { + if items.is_empty() { + return vec![]; + } + + self.scene + .pipelines + .get(&pipeline_id) + .expect("No display list?") + .display_list + .get(items) + .collect() + } + fn flatten_root( &mut self, traversal: &mut BuiltDisplayListIter<'a>, @@ -405,6 +423,7 @@ impl<'a> FlattenContext<'a> { None, info.image_key, info.image_rendering, + info.alpha_type, None, ); } @@ -558,6 +577,10 @@ impl<'a> FlattenContext<'a> { clip_region, ); } + SpecificDisplayItem::ClipChain(ref info) => { + let items = self.get_clip_chain_items(pipeline_id, item.clip_chain_items()); + self.clip_scroll_tree.add_clip_chain_descriptor(info.id, info.parent, items); + }, SpecificDisplayItem::ScrollFrame(ref info) => { let complex_clips = self.get_complex_clips(pipeline_id, item.complex_clip().0); let clip_region = ClipRegion::create_for_clip_node( @@ -915,6 +938,7 @@ impl<'a> FlattenContext<'a> { None, info.image_key, info.image_rendering, + info.alpha_type, Some(tile_offset), ); } diff --git a/gfx/webrender/src/frame_builder.rs b/gfx/webrender/src/frame_builder.rs index 2907929249b1..28fc18339f8d 100644 --- a/gfx/webrender/src/frame_builder.rs +++ b/gfx/webrender/src/frame_builder.rs @@ -2,8 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{BorderDetails, BorderDisplayItem, BuiltDisplayList, ClipAndScrollInfo, ClipId, ColorF}; -use api::{ColorU, DeviceIntPoint, DevicePixelScale, DeviceUintPoint, DeviceUintRect}; +use api::{AlphaType, BorderDetails, BorderDisplayItem, BuiltDisplayList, ClipAndScrollInfo, ClipId}; +use api::{ColorF, ColorU, DeviceIntPoint, DevicePixelScale, DeviceUintPoint, DeviceUintRect}; use api::{DeviceUintSize, DocumentLayer, ExtendMode, FontRenderMode, GlyphInstance, GlyphOptions}; use api::{GradientStop, HitTestFlags, HitTestItem, HitTestResult, ImageKey, ImageRendering}; use api::{ItemRange, ItemTag, LayerPoint, LayerPrimitiveInfo, LayerRect, LayerSize}; @@ -22,19 +22,18 @@ use glyph_rasterizer::FontInstance; use gpu_cache::GpuCache; use gpu_types::{ClipScrollNodeData, PictureType}; use internal_types::{FastHashMap, FastHashSet, RenderPassIndex}; -use picture::{ContentOrigin, PictureCompositeMode, PictureKind, PicturePrimitive}; +use picture::{ContentOrigin, PictureCompositeMode, PictureKind, PicturePrimitive, PictureSurface}; use prim_store::{BrushKind, BrushPrimitive, TexelRect, YuvImagePrimitiveCpu}; -use prim_store::{GradientPrimitiveCpu, ImagePrimitiveCpu, LinePrimitive, PrimitiveKind}; +use prim_store::{GradientPrimitiveCpu, ImagePrimitiveCpu, PrimitiveKind}; use prim_store::{PrimitiveContainer, PrimitiveIndex, SpecificPrimitiveIndex}; use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu}; use prim_store::{BrushSegmentDescriptor, TextRunPrimitiveCpu}; use profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters}; -use render_task::{ClearMode, RenderTask, RenderTaskId, RenderTaskTree}; +use render_task::{ClearMode, ClipChain, RenderTask, RenderTaskId, RenderTaskTree}; use resource_cache::ResourceCache; use scene::{ScenePipeline, SceneProperties}; use std::{mem, usize, f32}; -use tiling::{CompositeOps, Frame}; -use tiling::{RenderPass, RenderTargetKind}; +use tiling::{CompositeOps, Frame, RenderPass, RenderTargetKind}; use tiling::{RenderTargetContext, ScrollbarPrimitive}; use util::{self, MaxRect, pack_as_float, RectHelpers, recycle_vec}; @@ -129,7 +128,7 @@ pub struct FrameBuilder { pub struct PrimitiveContext<'a> { pub device_pixel_scale: DevicePixelScale, pub display_list: &'a BuiltDisplayList, - pub clip_node: &'a ClipScrollNode, + pub clip_chain: Option<&'a ClipChain>, pub scroll_node: &'a ClipScrollNode, } @@ -137,13 +136,13 @@ impl<'a> PrimitiveContext<'a> { pub fn new( device_pixel_scale: DevicePixelScale, display_list: &'a BuiltDisplayList, - clip_node: &'a ClipScrollNode, + clip_chain: Option<&'a ClipChain>, scroll_node: &'a ClipScrollNode, ) -> Self { PrimitiveContext { device_pixel_scale, display_list, - clip_node, + clip_chain, scroll_node, } } @@ -819,12 +818,15 @@ impl FrameBuilder { line_color: &ColorF, style: LineStyle, ) { - let line = LinePrimitive { - wavy_line_thickness, - color: line_color.premultiplied(), - style, - orientation, - }; + let line = BrushPrimitive::new( + BrushKind::Line { + wavy_line_thickness, + color: line_color.premultiplied(), + style, + orientation, + }, + None, + ); let mut fast_shadow_prims = Vec::new(); for (idx, &(shadow_prim_index, _)) in self.shadow_prim_stack.iter().enumerate() { @@ -839,14 +841,21 @@ impl FrameBuilder { } for (idx, shadow_offset, shadow_color) in fast_shadow_prims { - let mut line = line.clone(); - line.color = shadow_color.premultiplied(); + let line = BrushPrimitive::new( + BrushKind::Line { + wavy_line_thickness, + color: shadow_color.premultiplied(), + style, + orientation, + }, + None, + ); let mut info = info.clone(); info.rect = info.rect.translate(&shadow_offset); let prim_index = self.create_primitive( &info, Vec::new(), - PrimitiveContainer::Line(line), + PrimitiveContainer::Brush(line), ); self.shadow_prim_stack[idx].1.push((prim_index, clip_and_scroll)); } @@ -854,7 +863,7 @@ impl FrameBuilder { let prim_index = self.create_primitive( &info, Vec::new(), - PrimitiveContainer::Line(line), + PrimitiveContainer::Brush(line), ); if line_color.a > 0.0 { @@ -1088,6 +1097,7 @@ impl FrameBuilder { Some(segment.sub_rect), border.image_key, ImageRendering::Auto, + AlphaType::PremultipliedAlpha, None, ); } @@ -1283,8 +1293,10 @@ impl FrameBuilder { let mut render_mode = self.config .default_font_render_mode .limit_by(font.render_mode); + let mut flags = font.flags; if let Some(options) = glyph_options { render_mode = render_mode.limit_by(options.render_mode); + flags |= options.flags; } // There are some conditions under which we can't use @@ -1309,7 +1321,7 @@ impl FrameBuilder { font.bg_color, render_mode, font.subpx_dir, - font.flags, + flags, font.platform_options, font.variations.clone(), ); @@ -1411,6 +1423,7 @@ impl FrameBuilder { sub_rect: Option, image_key: ImageKey, image_rendering: ImageRendering, + alpha_type: AlphaType, tile: Option, ) { let sub_rect_block = sub_rect.unwrap_or(TexelRect::invalid()).into(); @@ -1430,6 +1443,7 @@ impl FrameBuilder { image_rendering, tile_offset: tile, tile_spacing, + alpha_type, gpu_blocks: [ [ stretch_size.width, @@ -1582,7 +1596,7 @@ impl FrameBuilder { let root_prim_context = PrimitiveContext::new( device_pixel_scale, display_list, - root_clip_scroll_node, + root_clip_scroll_node.clip_chain.as_ref(), root_clip_scroll_node, ); @@ -1620,12 +1634,12 @@ impl FrameBuilder { PremultipliedColorF::TRANSPARENT, ClearMode::Transparent, child_tasks, - None, PictureType::Image, ); - pic.render_task_id = Some(render_tasks.add(root_render_task)); - pic.render_task_id + let render_task_id = render_tasks.add(root_render_task); + pic.surface = Some(PictureSurface::RenderTask(render_task_id)); + Some(render_task_id) } fn update_scroll_bars(&mut self, clip_scroll_tree: &ClipScrollTree, gpu_cache: &mut GpuCache) { diff --git a/gfx/webrender/src/glyph_rasterizer.rs b/gfx/webrender/src/glyph_rasterizer.rs index 9bf21bf7a7b7..2d34698cb918 100644 --- a/gfx/webrender/src/glyph_rasterizer.rs +++ b/gfx/webrender/src/glyph_rasterizer.rs @@ -114,6 +114,18 @@ impl FontTransform { self.scale_y - self.skew_y * skew_factor, ) } + + pub fn swap_xy(&self) -> Self { + FontTransform::new(self.skew_x, self.scale_x, self.scale_y, self.skew_y) + } + + pub fn flip_x(&self) -> Self { + FontTransform::new(-self.scale_x, self.skew_x, -self.skew_y, self.scale_y) + } + + pub fn flip_y(&self) -> Self { + FontTransform::new(self.scale_x, -self.skew_y, self.skew_y, -self.scale_y) + } } impl<'a> From<&'a LayerToWorldTransform> for FontTransform { @@ -220,8 +232,6 @@ pub enum GlyphFormat { impl GlyphFormat { pub fn ignore_color(self) -> Self { match self { - GlyphFormat::Subpixel => GlyphFormat::Alpha, - GlyphFormat::TransformedSubpixel => GlyphFormat::TransformedAlpha, GlyphFormat::ColorBitmap => GlyphFormat::Bitmap, _ => self, } @@ -400,7 +410,7 @@ impl GlyphRasterizer { offset: 0, }, TextureFilter::Linear, - ImageData::Raw(glyph_info.glyph_bytes.clone()), + Some(ImageData::Raw(glyph_info.glyph_bytes.clone())), [glyph_info.offset.x, glyph_info.offset.y, glyph_info.scale], None, gpu_cache, @@ -523,7 +533,7 @@ impl GlyphRasterizer { offset: 0, }, TextureFilter::Linear, - ImageData::Raw(glyph_bytes.clone()), + Some(ImageData::Raw(glyph_bytes.clone())), [glyph.left, -glyph.top, glyph.scale], None, gpu_cache, diff --git a/gfx/webrender/src/gpu_types.rs b/gfx/webrender/src/gpu_types.rs index 5116f58f95ef..b7cd97f024ef 100644 --- a/gfx/webrender/src/gpu_types.rs +++ b/gfx/webrender/src/gpu_types.rs @@ -2,8 +2,9 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{LayerRect, LayerToWorldTransform}; +use api::{LayerToWorldTransform}; use gpu_cache::GpuCacheAddress; +use prim_store::EdgeAaSegmentMask; use render_task::RenderTaskAddress; // Contains type that must exactly match the same structures declared in GLSL. @@ -21,7 +22,6 @@ pub struct BlurInstance { pub task_address: RenderTaskAddress, pub src_task_address: RenderTaskAddress, pub blur_direction: BlurDirection, - pub region: LayerRect, } /// A clipping primitive drawn into the clipping mask. @@ -155,6 +155,7 @@ pub struct BrushInstance { pub clip_task_address: RenderTaskAddress, pub z: i32, pub segment_index: i32, + pub edge_flags: EdgeAaSegmentMask, pub user_data0: i32, pub user_data1: i32, } @@ -168,7 +169,7 @@ impl From for PrimitiveInstance { ((instance.clip_chain_rect_index.0 as i32) << 16) | instance.scroll_id.0 as i32, instance.clip_task_address.0 as i32, instance.z, - instance.segment_index, + instance.segment_index | ((instance.edge_flags.bits() as i32) << 16), instance.user_data0, instance.user_data1, ] diff --git a/gfx/webrender/src/picture.rs b/gfx/webrender/src/picture.rs index 9d47a50d76dc..099fc6af411b 100644 --- a/gfx/webrender/src/picture.rs +++ b/gfx/webrender/src/picture.rs @@ -8,10 +8,12 @@ use api::{BoxShadowClipMode, LayerPoint, LayerRect, LayerVector2D, Shadow}; use api::{ClipId, PremultipliedColorF}; use box_shadow::{BLUR_SAMPLE_SCALE, BoxShadowCacheKey}; use frame_builder::PrimitiveContext; -use gpu_cache::GpuDataRequest; +use gpu_cache::{GpuCache, GpuDataRequest}; use gpu_types::{BrushImageKind, PictureType}; use prim_store::{PrimitiveIndex, PrimitiveRun, PrimitiveRunLocalRect}; -use render_task::{ClearMode, RenderTask, RenderTaskId, RenderTaskTree}; +use render_task::{ClearMode, RenderTask, RenderTaskCacheKey}; +use render_task::{RenderTaskCacheKeyKind, RenderTaskId, RenderTaskTree}; +use resource_cache::{CacheItem, ResourceCache}; use scene::{FilterOpHelpers, SceneProperties}; use tiling::RenderTargetKind; @@ -57,7 +59,6 @@ pub enum PictureKind { BoxShadow { blur_radius: f32, color: ColorF, - blur_regions: Vec, clip_mode: BoxShadowClipMode, image_kind: BrushImageKind, content_rect: LayerRect, @@ -88,11 +89,21 @@ pub enum PictureKind { }, } +// The type of surface that a picture can be drawn to. +// RenderTask surfaces are not retained across frames. +// TextureCache surfaces are stored across frames, and +// also shared between display lists. +#[derive(Debug)] +pub enum PictureSurface { + RenderTask(RenderTaskId), + TextureCache(CacheItem), +} + #[derive(Debug)] pub struct PicturePrimitive { // If this picture is drawn to an intermediate surface, - // the associated render task. - pub render_task_id: Option, + // the associated target information. + pub surface: Option, // Details specific to this type of picture. pub kind: PictureKind, @@ -113,7 +124,7 @@ impl PicturePrimitive { pub fn new_text_shadow(shadow: Shadow, pipeline_id: PipelineId) -> Self { PicturePrimitive { runs: Vec::new(), - render_task_id: None, + surface: None, kind: PictureKind::TextShadow { offset: shadow.offset, color: shadow.color, @@ -149,7 +160,6 @@ impl PicturePrimitive { pub fn new_box_shadow( blur_radius: f32, color: ColorF, - blur_regions: Vec, clip_mode: BoxShadowClipMode, image_kind: BrushImageKind, cache_key: BoxShadowCacheKey, @@ -157,11 +167,10 @@ impl PicturePrimitive { ) -> Self { PicturePrimitive { runs: Vec::new(), - render_task_id: None, + surface: None, kind: PictureKind::BoxShadow { blur_radius, color, - blur_regions, clip_mode, image_kind, content_rect: LayerRect::zero(), @@ -181,7 +190,7 @@ impl PicturePrimitive { ) -> Self { PicturePrimitive { runs: Vec::new(), - render_task_id: None, + surface: None, kind: PictureKind::Image { secondary_render_task_id: None, composite_mode, @@ -303,6 +312,8 @@ impl PicturePrimitive { prim_local_rect: &LayerRect, child_tasks: Vec, parent_tasks: &mut Vec, + resource_cache: &mut ResourceCache, + gpu_cache: &mut GpuCache, ) { let content_scale = LayerToWorldScale::new(1.0) * prim_context.device_pixel_scale; @@ -323,26 +334,24 @@ impl PicturePrimitive { PremultipliedColorF::TRANSPARENT, ClearMode::Transparent, child_tasks, - None, PictureType::Image, ); let blur_std_deviation = blur_radius * prim_context.device_pixel_scale.0; let picture_task_id = render_tasks.add(picture_task); - let blur_render_task = RenderTask::new_blur( + let (blur_render_task, _) = RenderTask::new_blur( blur_std_deviation, picture_task_id, render_tasks, RenderTargetKind::Color, - &[], ClearMode::Transparent, PremultipliedColorF::TRANSPARENT, - None, ); - let blur_render_task_id = render_tasks.add(blur_render_task); - self.render_task_id = Some(blur_render_task_id); + let render_task_id = render_tasks.add(blur_render_task); + parent_tasks.push(render_task_id); + self.surface = Some(PictureSurface::RenderTask(render_task_id)); } Some(PictureCompositeMode::Filter(FilterOp::DropShadow(offset, blur_radius, color))) => { let rect = (prim_local_rect.translate(&-offset) * content_scale).round().to_i32(); @@ -354,26 +363,26 @@ impl PicturePrimitive { PremultipliedColorF::TRANSPARENT, ClearMode::Transparent, child_tasks, - None, PictureType::Image, ); let blur_std_deviation = blur_radius * prim_context.device_pixel_scale.0; let picture_task_id = render_tasks.add(picture_task); - let blur_render_task = RenderTask::new_blur( + let (blur_render_task, _) = RenderTask::new_blur( blur_std_deviation.round(), picture_task_id, render_tasks, RenderTargetKind::Color, - &[], ClearMode::Transparent, color.premultiplied(), - None, ); *secondary_render_task_id = Some(picture_task_id); - self.render_task_id = Some(render_tasks.add(blur_render_task)); + + let render_task_id = render_tasks.add(blur_render_task); + parent_tasks.push(render_task_id); + self.surface = Some(PictureSurface::RenderTask(render_task_id)); } Some(PictureCompositeMode::MixBlend(..)) => { let picture_task = RenderTask::new_picture( @@ -384,7 +393,6 @@ impl PicturePrimitive { PremultipliedColorF::TRANSPARENT, ClearMode::Transparent, child_tasks, - None, PictureType::Image, ); @@ -393,7 +401,9 @@ impl PicturePrimitive { *secondary_render_task_id = Some(readback_task_id); parent_tasks.push(readback_task_id); - self.render_task_id = Some(render_tasks.add(picture_task)); + let render_task_id = render_tasks.add(picture_task); + parent_tasks.push(render_task_id); + self.surface = Some(PictureSurface::RenderTask(render_task_id)); } Some(PictureCompositeMode::Filter(filter)) => { // If this filter is not currently going to affect @@ -403,7 +413,7 @@ impl PicturePrimitive { // filters and be a significant performance win. if filter.is_noop() { parent_tasks.extend(child_tasks); - self.render_task_id = None; + self.surface = None; } else { let picture_task = RenderTask::new_picture( Some(prim_screen_rect.size), @@ -413,11 +423,12 @@ impl PicturePrimitive { PremultipliedColorF::TRANSPARENT, ClearMode::Transparent, child_tasks, - None, PictureType::Image, ); - self.render_task_id = Some(render_tasks.add(picture_task)); + let render_task_id = render_tasks.add(picture_task); + parent_tasks.push(render_task_id); + self.surface = Some(PictureSurface::RenderTask(render_task_id)); } } Some(PictureCompositeMode::Blit) => { @@ -429,15 +440,16 @@ impl PicturePrimitive { PremultipliedColorF::TRANSPARENT, ClearMode::Transparent, child_tasks, - None, PictureType::Image, ); - self.render_task_id = Some(render_tasks.add(picture_task)); + let render_task_id = render_tasks.add(picture_task); + parent_tasks.push(render_task_id); + self.surface = Some(PictureSurface::RenderTask(render_task_id)); } None => { parent_tasks.extend(child_tasks); - self.render_task_id = None; + self.surface = None; } } } @@ -468,26 +480,25 @@ impl PicturePrimitive { color.premultiplied(), ClearMode::Transparent, Vec::new(), - None, PictureType::TextShadow, ); let picture_task_id = render_tasks.add(picture_task); - let render_task = RenderTask::new_blur( + let (blur_render_task, _) = RenderTask::new_blur( blur_std_deviation, picture_task_id, render_tasks, RenderTargetKind::Color, - &[], ClearMode::Transparent, color.premultiplied(), - None, ); - self.render_task_id = Some(render_tasks.add(render_task)); + let render_task_id = render_tasks.add(blur_render_task); + parent_tasks.push(render_task_id); + self.surface = Some(PictureSurface::RenderTask(render_task_id)); } - PictureKind::BoxShadow { blur_radius, clip_mode, ref blur_regions, color, content_rect, cache_key, .. } => { + PictureKind::BoxShadow { blur_radius, clip_mode, color, content_rect, cache_key, .. } => { // TODO(gw): Rounding the content rect here to device pixels is not // technically correct. Ideally we should ceil() here, and ensure that // the extra part pixel in the case of fractional sizes is correctly @@ -495,59 +506,82 @@ impl PicturePrimitive { // Gecko tests. let cache_size = (content_rect.size * content_scale).round().to_i32(); - // Quote from https://drafts.csswg.org/css-backgrounds-3/#shadow-blur - // "the image that would be generated by applying to the shadow a - // Gaussian blur with a standard deviation equal to half the blur radius." - let device_radius = (blur_radius * prim_context.device_pixel_scale.0).round(); - let blur_std_deviation = device_radius * 0.5; - - let blur_clear_mode = match clip_mode { - BoxShadowClipMode::Outset => { - ClearMode::One - } - BoxShadowClipMode::Inset => { - ClearMode::Zero - } - }; - - let picture_task = RenderTask::new_picture( - Some(cache_size), - prim_index, - RenderTargetKind::Alpha, - ContentOrigin::Local(content_rect.origin), - color.premultiplied(), - ClearMode::Zero, - Vec::new(), - Some(cache_key), - PictureType::BoxShadow, - ); - - let picture_task_id = render_tasks.add(picture_task); - - let render_task = RenderTask::new_blur( - blur_std_deviation, - picture_task_id, + // Request the texture cache item for this box-shadow key. If it + // doesn't exist in the cache, the closure is invoked to build + // a render task chain to draw the cacheable result. + let cache_item = resource_cache.request_render_task( + RenderTaskCacheKey { + size: cache_size, + kind: RenderTaskCacheKeyKind::BoxShadow(cache_key), + }, + gpu_cache, render_tasks, - RenderTargetKind::Alpha, - blur_regions, - blur_clear_mode, - color.premultiplied(), - Some(cache_key), + |render_tasks| { + // Quote from https://drafts.csswg.org/css-backgrounds-3/#shadow-blur + // "the image that would be generated by applying to the shadow a + // Gaussian blur with a standard deviation equal to half the blur radius." + let device_radius = (blur_radius * prim_context.device_pixel_scale.0).round(); + let blur_std_deviation = device_radius * 0.5; + + let blur_clear_mode = match clip_mode { + BoxShadowClipMode::Outset => { + ClearMode::One + } + BoxShadowClipMode::Inset => { + ClearMode::Zero + } + }; + + let picture_task = RenderTask::new_picture( + Some(cache_size), + prim_index, + RenderTargetKind::Alpha, + ContentOrigin::Local(content_rect.origin), + color.premultiplied(), + ClearMode::Zero, + Vec::new(), + PictureType::BoxShadow, + ); + + let picture_task_id = render_tasks.add(picture_task); + + let (blur_render_task, scale_factor) = RenderTask::new_blur( + blur_std_deviation, + picture_task_id, + render_tasks, + RenderTargetKind::Alpha, + blur_clear_mode, + color.premultiplied(), + ); + + let root_task_id = render_tasks.add(blur_render_task); + parent_tasks.push(root_task_id); + + // TODO(gw): Remove the nastiness with having to pass + // the scale factor through the texture cache + // item user data. This will disappear once + // the brush_image shader is updated to draw + // segments, since the scale factor will not + // be used at all then during drawing. + (root_task_id, [scale_factor, 0.0, 0.0]) + } ); - self.render_task_id = Some(render_tasks.add(render_task)); + self.surface = Some(PictureSurface::TextureCache(cache_item)); } } - - if let Some(render_task_id) = self.render_task_id { - parent_tasks.push(render_task_id); - } } - pub fn write_gpu_blocks(&self, _request: &mut GpuDataRequest) { - // TODO(gw): We'll need to write the GPU blocks - // here specific to a brush primitive - // once we start drawing pictures as brushes! + pub fn write_gpu_blocks(&self, request: &mut GpuDataRequest) { + match self.kind { + PictureKind::TextShadow { .. } | + PictureKind::Image { .. } => { + request.push([0.0; 4]); + } + PictureKind::BoxShadow { color, .. } => { + request.push(color.premultiplied()); + } + } } pub fn target_kind(&self) -> RenderTargetKind { diff --git a/gfx/webrender/src/platform/macos/font.rs b/gfx/webrender/src/platform/macos/font.rs index 9d3d70658cb7..ba0c3c95946a 100644 --- a/gfx/webrender/src/platform/macos/font.rs +++ b/gfx/webrender/src/platform/macos/font.rs @@ -117,7 +117,6 @@ fn get_glyph_metrics( if let Some(transform) = transform { bounds = bounds.apply_transform(transform); - advance = advance.apply_transform(transform); } // First round out to pixel boundaries @@ -361,8 +360,23 @@ impl FontContext { let glyph = key.index as CGGlyph; let bitmap = is_bitmap_font(ct_font); let (x_offset, y_offset) = if bitmap { (0.0, 0.0) } else { font.get_subpx_offset(key) }; - let transform = if font.flags.contains(FontInstanceFlags::SYNTHETIC_ITALICS) { - let shape = FontTransform::identity().synthesize_italics(OBLIQUE_SKEW_FACTOR); + let transform = if font.flags.intersects(FontInstanceFlags::SYNTHETIC_ITALICS | + FontInstanceFlags::TRANSPOSE | + FontInstanceFlags::FLIP_X | + FontInstanceFlags::FLIP_Y) { + let mut shape = FontTransform::identity(); + if font.flags.contains(FontInstanceFlags::FLIP_X) { + shape = shape.flip_x(); + } + if font.flags.contains(FontInstanceFlags::FLIP_Y) { + shape = shape.flip_y(); + } + if font.flags.contains(FontInstanceFlags::TRANSPOSE) { + shape = shape.swap_xy(); + } + if font.flags.contains(FontInstanceFlags::SYNTHETIC_ITALICS) { + shape = shape.synthesize_italics(OBLIQUE_SKEW_FACTOR); + } Some(CGAffineTransform { a: shape.scale_x as f64, b: -shape.skew_y as f64, @@ -483,6 +497,15 @@ impl FontContext { } else { (font.transform.invert_scale(y_scale, y_scale), font.get_subpx_offset(key)) }; + if font.flags.contains(FontInstanceFlags::FLIP_X) { + shape = shape.flip_x(); + } + if font.flags.contains(FontInstanceFlags::FLIP_Y) { + shape = shape.flip_y(); + } + if font.flags.contains(FontInstanceFlags::TRANSPOSE) { + shape = shape.swap_xy(); + } if font.flags.contains(FontInstanceFlags::SYNTHETIC_ITALICS) { shape = shape.synthesize_italics(OBLIQUE_SKEW_FACTOR); } diff --git a/gfx/webrender/src/platform/unix/font.rs b/gfx/webrender/src/platform/unix/font.rs index 814466183543..43b942fdbc4b 100644 --- a/gfx/webrender/src/platform/unix/font.rs +++ b/gfx/webrender/src/platform/unix/font.rs @@ -105,6 +105,38 @@ fn skew_bitmap(bitmap: &[u8], width: usize, height: usize, left: i32, top: i32) (skew_buffer, skew_width, left + skew_min as i32) } +fn transpose_bitmap(bitmap: &[u8], width: usize, height: usize) -> Vec { + let mut transposed = vec![0u8; width * height * 4]; + for (y, row) in bitmap.chunks(width * 4).enumerate() { + let mut offset = y * 4; + for src in row.chunks(4) { + transposed[offset .. offset + 4].copy_from_slice(src); + offset += height * 4; + } + } + transposed +} + +fn flip_bitmap_x(bitmap: &mut [u8], width: usize, height: usize) { + assert!(bitmap.len() == width * height * 4); + let pixels = unsafe { slice::from_raw_parts_mut(bitmap.as_mut_ptr() as *mut u32, width * height) }; + for row in pixels.chunks_mut(width) { + row.reverse(); + } +} + +fn flip_bitmap_y(bitmap: &mut [u8], width: usize, height: usize) { + assert!(bitmap.len() == width * height * 4); + let pixels = unsafe { slice::from_raw_parts_mut(bitmap.as_mut_ptr() as *mut u32, width * height) }; + for y in 0 .. height / 2 { + let low_row = y * width; + let high_row = (height - 1 - y) * width; + for x in 0 .. width { + pixels.swap(low_row + x, high_row + x); + } + } +} + impl FontContext { pub fn new() -> FontContext { let mut lib: FT_Library = ptr::null_mut(); @@ -250,6 +282,15 @@ impl FontContext { self.choose_bitmap_size(face.face, req_size * y_scale) } else { let mut shape = font.transform.invert_scale(x_scale, y_scale); + if font.flags.contains(FontInstanceFlags::FLIP_X) { + shape = shape.flip_x(); + } + if font.flags.contains(FontInstanceFlags::FLIP_Y) { + shape = shape.flip_y(); + } + if font.flags.contains(FontInstanceFlags::TRANSPOSE) { + shape = shape.swap_xy(); + } if font.flags.contains(FontInstanceFlags::SYNTHETIC_ITALICS) { shape = shape.synthesize_italics(OBLIQUE_SKEW_FACTOR); }; @@ -391,6 +432,18 @@ impl FontContext { left += skew_min as i32; width += (skew_max - skew_min) as u32; } + if font.flags.contains(FontInstanceFlags::TRANSPOSE) { + mem::swap(&mut width, &mut height); + mem::swap(&mut left, &mut top); + left -= width as i32; + top += height as i32; + } + if font.flags.contains(FontInstanceFlags::FLIP_X) { + left = -(left + width as i32); + } + if font.flags.contains(FontInstanceFlags::FLIP_Y) { + top = -(top - height as i32); + } } Some(GlyphDimensions { left, @@ -570,7 +623,7 @@ impl FontContext { let bitmap = unsafe { &(*slot).bitmap }; let pixel_mode = unsafe { mem::transmute(bitmap.pixel_mode as u32) }; - let (mut actual_width, actual_height) = match pixel_mode { + let (mut actual_width, mut actual_height) = match pixel_mode { FT_Pixel_Mode::FT_PIXEL_MODE_LCD => { assert!(bitmap.width % 3 == 0); ((bitmap.width / 3) as usize, bitmap.rows as usize) @@ -677,6 +730,21 @@ impl FontContext { actual_width = skew_width; left = skew_left; } + if font.flags.contains(FontInstanceFlags::TRANSPOSE) { + final_buffer = transpose_bitmap(&final_buffer, actual_width, actual_height); + mem::swap(&mut actual_width, &mut actual_height); + mem::swap(&mut left, &mut top); + left -= actual_width as i32; + top += actual_height as i32; + } + if font.flags.contains(FontInstanceFlags::FLIP_X) { + flip_bitmap_x(&mut final_buffer, actual_width, actual_height); + left = -(left + actual_width as i32); + } + if font.flags.contains(FontInstanceFlags::FLIP_Y) { + flip_bitmap_y(&mut final_buffer, actual_width, actual_height); + top = -(top - actual_height as i32); + } } FT_Glyph_Format::FT_GLYPH_FORMAT_OUTLINE => { unsafe { diff --git a/gfx/webrender/src/platform/windows/font.rs b/gfx/webrender/src/platform/windows/font.rs index 326800280445..1ca58e16706a 100644 --- a/gfx/webrender/src/platform/windows/font.rs +++ b/gfx/webrender/src/platform/windows/font.rs @@ -236,8 +236,23 @@ impl FontContext { ) -> Option { let size = font.size.to_f32_px(); let bitmaps = is_bitmap_font(font); - let transform = if font.flags.contains(FontInstanceFlags::SYNTHETIC_ITALICS) { - let shape = FontTransform::identity().synthesize_italics(OBLIQUE_SKEW_FACTOR); + let transform = if font.flags.intersects(FontInstanceFlags::SYNTHETIC_ITALICS | + FontInstanceFlags::TRANSPOSE | + FontInstanceFlags::FLIP_X | + FontInstanceFlags::FLIP_Y) { + let mut shape = FontTransform::identity(); + if font.flags.contains(FontInstanceFlags::FLIP_X) { + shape = shape.flip_x(); + } + if font.flags.contains(FontInstanceFlags::FLIP_Y) { + shape = shape.flip_y(); + } + if font.flags.contains(FontInstanceFlags::TRANSPOSE) { + shape = shape.swap_xy(); + } + if font.flags.contains(FontInstanceFlags::SYNTHETIC_ITALICS) { + shape = shape.synthesize_italics(OBLIQUE_SKEW_FACTOR); + } Some(dwrote::DWRITE_MATRIX { m11: shape.scale_x, m12: shape.skew_y, @@ -359,6 +374,15 @@ impl FontContext { } else { (font.transform.invert_scale(y_scale, y_scale), font.get_subpx_offset(key)) }; + if font.flags.contains(FontInstanceFlags::FLIP_X) { + shape = shape.flip_x(); + } + if font.flags.contains(FontInstanceFlags::FLIP_Y) { + shape = shape.flip_y(); + } + if font.flags.contains(FontInstanceFlags::TRANSPOSE) { + shape = shape.swap_xy(); + } if font.flags.contains(FontInstanceFlags::SYNTHETIC_ITALICS) { shape = shape.synthesize_italics(OBLIQUE_SKEW_FACTOR); } diff --git a/gfx/webrender/src/prim_store.rs b/gfx/webrender/src/prim_store.rs index 121a5373e7aa..39ec17c401c4 100644 --- a/gfx/webrender/src/prim_store.rs +++ b/gfx/webrender/src/prim_store.rs @@ -2,14 +2,14 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{BorderRadius, BuiltDisplayList, ClipAndScrollInfo, ClipId, ClipMode, ColorF, ColorU}; -use api::{DeviceIntRect, DevicePixelScale, DevicePoint}; +use api::{AlphaType, BorderRadius, BuiltDisplayList, ClipAndScrollInfo, ClipId, ClipMode}; +use api::{ColorF, ColorU, DeviceIntRect, DevicePixelScale, DevicePoint}; use api::{ComplexClipRegion, ExtendMode, FontRenderMode}; use api::{GlyphInstance, GlyphKey, GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag}; use api::{LayerPoint, LayerRect, LayerSize, LayerToWorldTransform, LayerVector2D, LineOrientation}; use api::{LineStyle, PipelineId, PremultipliedColorF, TileOffset, WorldToLayerTransform}; use api::{YuvColorSpace, YuvFormat}; -use border::BorderCornerInstance; +use border::{BorderCornerInstance, BorderEdgeKind}; use clip_scroll_tree::{CoordinateSystemId, ClipScrollTree}; use clip_scroll_node::ClipScrollNode; use clip::{ClipSource, ClipSourcesHandle, ClipStore}; @@ -21,8 +21,8 @@ use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuData use gpu_types::{ClipChainRectIndex, ClipScrollNodeData}; use picture::{PictureKind, PicturePrimitive}; use profiler::FrameProfileCounters; -use render_task::{ClipChain, ClipChainNode, ClipChainNodeIter, ClipWorkItem, RenderTask}; -use render_task::{RenderTaskId, RenderTaskTree}; +use render_task::{ClipChain, ClipChainNode, ClipChainNodeIter, ClipChainNodeRef, ClipWorkItem}; +use render_task::{RenderTask, RenderTaskId, RenderTaskTree}; use renderer::{BLOCKS_PER_UV_RECT, MAX_VERTEX_TEXTURE_WIDTH}; use resource_cache::{ImageProperties, ResourceCache}; use scene::{ScenePipeline, SceneProperties}; @@ -151,7 +151,6 @@ pub enum PrimitiveKind { AlignedGradient, AngleGradient, RadialGradient, - Line, Picture, Brush, } @@ -212,6 +211,12 @@ pub enum BrushKind { color: ColorF, }, Clear, + Line { + color: PremultipliedColorF, + wavy_line_thickness: f32, + style: LineStyle, + orientation: LineOrientation, + } } impl BrushKind { @@ -331,37 +336,26 @@ impl BrushPrimitive { radii.bottom_left.height, ]); } + BrushKind::Line { color, wavy_line_thickness, style, orientation } => { + request.push(color); + request.push([ + wavy_line_thickness, + pack_as_float(style as u32), + pack_as_float(orientation as u32), + 0.0, + ]); + } } } } -#[derive(Debug, Clone)] -#[repr(C)] -pub struct LinePrimitive { - pub color: PremultipliedColorF, - pub wavy_line_thickness: f32, - pub style: LineStyle, - pub orientation: LineOrientation, -} - -impl LinePrimitive { - fn write_gpu_blocks(&self, request: &mut GpuDataRequest) { - request.push(self.color); - request.push([ - self.wavy_line_thickness, - pack_as_float(self.style as u32), - pack_as_float(self.orientation as u32), - 0.0, - ]); - } -} - #[derive(Debug)] pub struct ImagePrimitiveCpu { pub image_key: ImageKey, pub image_rendering: ImageRendering, pub tile_offset: Option, pub tile_spacing: LayerSize, + pub alpha_type: AlphaType, // TODO(gw): Build on demand pub gpu_blocks: [GpuBlockData; BLOCKS_PER_UV_RECT], } @@ -393,6 +387,7 @@ impl ToGpuBlocks for YuvImagePrimitiveCpu { #[derive(Debug)] pub struct BorderPrimitiveCpu { pub corner_instances: [BorderCornerInstance; 4], + pub edges: [BorderEdgeKind; 4], pub gpu_blocks: [GpuBlockData; 8], } @@ -679,6 +674,14 @@ impl TextRunPrimitiveCpu { self.shadow_color.a != 0 } + pub fn get_color(&self) -> ColorF { + ColorF::from(if self.is_shadow() { + self.shadow_color + } else { + self.font.color + }) + } + fn prepare_for_render( &mut self, resource_cache: &mut ResourceCache, @@ -728,14 +731,9 @@ impl TextRunPrimitiveCpu { } fn write_gpu_blocks(&self, request: &mut GpuDataRequest) { - let bg_color = ColorF::from(self.font.bg_color); - let color = ColorF::from(if self.is_shadow() { - self.shadow_color - } else { - self.font.color - }); - request.push(color.premultiplied()); + request.push(self.get_color().premultiplied()); // this is the only case where we need to provide plain color to GPU + let bg_color = ColorF::from(self.font.bg_color); request.extend_from_slice(&[ GpuBlockData { data: [bg_color.r, bg_color.g, bg_color.b, 1.0] } ]); @@ -944,7 +942,6 @@ pub enum PrimitiveContainer { AngleGradient(GradientPrimitiveCpu), RadialGradient(RadialGradientPrimitiveCpu), Picture(PicturePrimitive), - Line(LinePrimitive), Brush(BrushPrimitive), } @@ -959,7 +956,6 @@ pub struct PrimitiveStore { pub cpu_radial_gradients: Vec, pub cpu_metadata: Vec, pub cpu_borders: Vec, - pub cpu_lines: Vec, } impl PrimitiveStore { @@ -974,7 +970,6 @@ impl PrimitiveStore { cpu_gradients: Vec::new(), cpu_radial_gradients: Vec::new(), cpu_borders: Vec::new(), - cpu_lines: Vec::new(), } } @@ -989,7 +984,6 @@ impl PrimitiveStore { cpu_gradients: recycle_vec(self.cpu_gradients), cpu_radial_gradients: recycle_vec(self.cpu_radial_gradients), cpu_borders: recycle_vec(self.cpu_borders), - cpu_lines: recycle_vec(self.cpu_lines), } } @@ -1025,6 +1019,7 @@ impl PrimitiveStore { BrushKind::Clear => PrimitiveOpacity::translucent(), BrushKind::Solid { ref color } => PrimitiveOpacity::from_alpha(color.a), BrushKind::Mask { .. } => PrimitiveOpacity::translucent(), + BrushKind::Line { .. } => PrimitiveOpacity::translucent(), }; let metadata = PrimitiveMetadata { @@ -1038,17 +1033,6 @@ impl PrimitiveStore { metadata } - PrimitiveContainer::Line(line) => { - let metadata = PrimitiveMetadata { - opacity: PrimitiveOpacity::translucent(), - prim_kind: PrimitiveKind::Line, - cpu_prim_index: SpecificPrimitiveIndex(self.cpu_lines.len()), - ..base_metadata - }; - - self.cpu_lines.push(line); - metadata - } PrimitiveContainer::TextRun(text_cpu) => { let metadata = PrimitiveMetadata { opacity: PrimitiveOpacity::translucent(), @@ -1167,7 +1151,7 @@ impl PrimitiveStore { ) { let metadata = &mut self.cpu_metadata[prim_index.0]; match metadata.prim_kind { - PrimitiveKind::Border | PrimitiveKind::Line => {} + PrimitiveKind::Border => {} PrimitiveKind::Picture => { self.cpu_pictures[metadata.cpu_prim_index.0] .prepare_for_render( @@ -1178,6 +1162,8 @@ impl PrimitiveStore { &metadata.local_rect, child_tasks, parent_tasks, + resource_cache, + gpu_cache, ); } PrimitiveKind::TextRun => { @@ -1248,25 +1234,6 @@ impl PrimitiveStore { request.push(metadata.local_clip_rect); match metadata.prim_kind { - PrimitiveKind::Line => { - let line = &self.cpu_lines[metadata.cpu_prim_index.0]; - line.write_gpu_blocks(&mut request); - - // TODO(gw): This is a bit of a hack. The Line type - // is drawn by the brush_line shader, so the - // layout here needs to conform to the same - // BrushPrimitive layout. We should tidy this - // up in the future so it's enforced that these - // types use a shared function to write out the - // GPU blocks... - request.push(metadata.local_rect); - request.push([ - EdgeAaSegmentMask::empty().bits() as f32, - 0.0, - 0.0, - 0.0 - ]); - } PrimitiveKind::Border => { let border = &self.cpu_borders[metadata.cpu_prim_index.0]; border.write_gpu_blocks(request); @@ -1306,13 +1273,7 @@ impl PrimitiveStore { // up in the future so it's enforced that these // types use a shared function to write out the // GPU blocks... - request.push(metadata.local_rect); - request.push([ - EdgeAaSegmentMask::empty().bits() as f32, - 0.0, - 0.0, - 0.0 - ]); + request.write_segment(metadata.local_rect); } PrimitiveKind::Brush => { let brush = &self.cpu_brushes[metadata.cpu_prim_index.0]; @@ -1321,23 +1282,11 @@ impl PrimitiveStore { Some(ref segment_desc) => { for segment in &segment_desc.segments { // has to match VECS_PER_SEGMENT - request.push(segment.local_rect); - request.push([ - segment.edge_flags.bits() as f32, - 0.0, - 0.0, - 0.0 - ]); + request.write_segment(segment.local_rect); } } None => { - request.push(metadata.local_rect); - request.push([ - EdgeAaSegmentMask::empty().bits() as f32, - 0.0, - 0.0, - 0.0 - ]); + request.write_segment(metadata.local_rect); } } } @@ -1513,7 +1462,6 @@ impl PrimitiveStore { combined_outer_rect.intersection(&segment_screen_rect).map(|bounds| { let clip_task = RenderTask::new_mask( - None, bounds, clips.clone(), prim_context.scroll_node.coordinate_system_id, @@ -1555,12 +1503,13 @@ impl PrimitiveStore { } }; - let clip_chain = prim_context.clip_node.clip_chain_node.clone(); - let mut combined_outer_rect = match clip_chain { - Some(ref node) => prim_screen_rect.intersection(&node.combined_outer_screen_rect), + let mut combined_outer_rect = match prim_context.clip_chain { + Some(ref chain) => prim_screen_rect.intersection(&chain.combined_outer_screen_rect), None => Some(prim_screen_rect), }; + let clip_chain = prim_context.clip_chain.map_or(None, |x| x.nodes.clone()); + let prim_coordinate_system_id = prim_context.scroll_node.coordinate_system_id; let transform = &prim_context.scroll_node.world_content_transform; let extra_clip = { @@ -1588,9 +1537,6 @@ impl PrimitiveStore { local_clip_rect: LayerRect::zero(), screen_inner_rect, screen_outer_rect: screen_outer_rect.unwrap_or(prim_screen_rect), - combined_outer_screen_rect: - combined_outer_rect.unwrap_or_else(DeviceIntRect::zero), - combined_inner_screen_rect: DeviceIntRect::zero(), prev: None, })) } else { @@ -1653,7 +1599,6 @@ impl PrimitiveStore { } let clip_task = RenderTask::new_mask( - None, combined_outer_rect, clips, prim_coordinate_system_id, @@ -1788,7 +1733,7 @@ impl PrimitiveStore { prim_context.device_pixel_scale, ); - let clip_bounds = match prim_context.clip_node.clip_chain_node { + let clip_bounds = match prim_context.clip_chain { Some(ref node) => node.combined_outer_screen_rect, None => *screen_rect, }; @@ -1871,13 +1816,24 @@ impl PrimitiveStore { // a new primitive context for every run (if the hash // lookups ever show up in a profile). let scroll_node = &clip_scroll_tree.nodes[&run.clip_and_scroll.scroll_node_id]; - let clip_node = &clip_scroll_tree.nodes[&run.clip_and_scroll.clip_node_id()]; + let clip_chain = clip_scroll_tree.get_clip_chain(&run.clip_and_scroll.clip_node_id()); - if perform_culling && !clip_node.is_visible() { - debug!("{:?} of clipped out {:?}", run.base_prim_index, pipeline_id); - continue; + if perform_culling { + if !scroll_node.invertible { + debug!("{:?} {:?}: position not invertible", run.base_prim_index, pipeline_id); + continue; + } + + match clip_chain { + Some(ref chain) if chain.combined_outer_screen_rect.is_empty() => { + debug!("{:?} {:?}: clipped out", run.base_prim_index, pipeline_id); + continue; + } + _ => {}, + } } + let parent_relative_transform = parent_prim_context .scroll_node .world_content_transform @@ -1905,13 +1861,13 @@ impl PrimitiveStore { let child_prim_context = PrimitiveContext::new( parent_prim_context.device_pixel_scale, display_list, - clip_node, + clip_chain, scroll_node, ); let clip_chain_rect = match perform_culling { - true => get_local_clip_rect_for_nodes(scroll_node, clip_node), + true => get_local_clip_rect_for_nodes(scroll_node, clip_chain), false => None, }; @@ -1994,17 +1950,14 @@ impl InsideTest for ComplexClipRegion { } fn convert_clip_chain_to_clip_vector( - clip_chain: ClipChain, - extra_clip: ClipChain, + clip_chain_nodes: ClipChainNodeRef, + extra_clip: ClipChainNodeRef, combined_outer_rect: &DeviceIntRect, combined_inner_rect: &mut DeviceIntRect, ) -> Vec { // Filter out all the clip instances that don't contribute to the result. ClipChainNodeIter { current: extra_clip } - .chain(ClipChainNodeIter { current: clip_chain }) - .take_while(|node| { - !node.combined_inner_screen_rect.contains_rect(&combined_outer_rect) - }) + .chain(ClipChainNodeIter { current: clip_chain_nodes }) .filter_map(|node| { *combined_inner_rect = if !node.screen_inner_rect.is_empty() { // If this clip's inner area contains the area of the primitive clipped @@ -2025,9 +1978,14 @@ fn convert_clip_chain_to_clip_vector( fn get_local_clip_rect_for_nodes( scroll_node: &ClipScrollNode, - clip_node: &ClipScrollNode, + clip_chain: Option<&ClipChain>, ) -> Option { - let local_rect = ClipChainNodeIter { current: clip_node.clip_chain_node.clone() }.fold( + let clip_chain_nodes = match clip_chain { + Some(ref clip_chain) => clip_chain.nodes.clone(), + None => return None, + }; + + let local_rect = ClipChainNodeIter { current: clip_chain_nodes }.fold( None, |combined_local_clip_rect: Option, node| { if node.work_item.coordinate_system_id != scroll_node.coordinate_system_id { @@ -2048,3 +2006,23 @@ fn get_local_clip_rect_for_nodes( None => None, } } + +impl<'a> GpuDataRequest<'a> { + // Write the GPU cache data for an individual segment. + // TODO(gw): The second block is currently unused. In + // the future, it will be used to store a + // UV rect, allowing segments to reference + // part of an image. + fn write_segment( + &mut self, + local_rect: LayerRect, + ) { + self.push(local_rect); + self.push([ + 0.0, + 0.0, + 0.0, + 0.0 + ]); + } +} diff --git a/gfx/webrender/src/profiler.rs b/gfx/webrender/src/profiler.rs index 8a045e7aea2d..deb31f02b7dd 100644 --- a/gfx/webrender/src/profiler.rs +++ b/gfx/webrender/src/profiler.rs @@ -944,6 +944,7 @@ impl Profiler { &renderer_profile.color_targets, &renderer_profile.alpha_targets, &renderer_profile.draw_calls, + &renderer_profile.vertices, &self.backend_time, &self.compositor_time, &self.gpu_time, diff --git a/gfx/webrender/src/render_backend.rs b/gfx/webrender/src/render_backend.rs index c10768c9a7d9..baa1dac1dc8f 100644 --- a/gfx/webrender/src/render_backend.rs +++ b/gfx/webrender/src/render_backend.rs @@ -801,6 +801,7 @@ impl ToDebugString for SpecificDisplayItem { SpecificDisplayItem::PushStackingContext(..) => String::from("push_stacking_context"), SpecificDisplayItem::Iframe(..) => String::from("iframe"), SpecificDisplayItem::Clip(..) => String::from("clip"), + SpecificDisplayItem::ClipChain(..) => String::from("clip_chain"), SpecificDisplayItem::ScrollFrame(..) => String::from("scroll_frame"), SpecificDisplayItem::StickyFrame(..) => String::from("sticky_frame"), SpecificDisplayItem::SetGradientStops => String::from("set_gradient_stops"), @@ -816,16 +817,17 @@ impl ToDebugString for SpecificDisplayItem { impl RenderBackend { // Note: the mutable `self` is only needed here for resolving blob images fn save_capture(&mut self, root: &PathBuf) -> Vec { - use ron::ser::pretty; + use ron::ser::{to_string_pretty, PrettyConfig}; use std::fs; use std::io::Write; info!("capture: saving {}", root.to_string_lossy()); + let ron_config = PrettyConfig::default(); let (resources, deferred) = self.resource_cache.save_capture(root); for (&id, doc) in &self.documents { info!("\tdocument {:?}", id); - let ron = pretty::to_string(&doc.scene).unwrap(); + let ron = to_string_pretty(&doc.scene, ron_config.clone()).unwrap(); let file_name = format!("scene-{}-{}.ron", (id.0).0, id.1); let ron_path = root.clone().join(file_name); let mut file = fs::File::create(ron_path).unwrap(); @@ -844,7 +846,7 @@ impl RenderBackend { resources, }; - let ron = pretty::to_string(&serial).unwrap(); + let ron = to_string_pretty(&serial, ron_config).unwrap(); let ron_path = root.clone().join("backend.ron"); let mut file = fs::File::create(ron_path).unwrap(); write!(file, "{}\n", ron).unwrap(); diff --git a/gfx/webrender/src/render_task.rs b/gfx/webrender/src/render_task.rs index 18fd5d438828..32a4322e6ab4 100644 --- a/gfx/webrender/src/render_task.rs +++ b/gfx/webrender/src/render_task.rs @@ -2,19 +2,23 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{ClipId, DeviceIntPoint, DeviceIntRect, DeviceIntSize}; -use api::{LayerRect, PremultipliedColorF}; +use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize}; +use api::{ImageDescriptor, ImageFormat, LayerRect, PremultipliedColorF}; use box_shadow::BoxShadowCacheKey; use clip::{ClipSourcesWeakHandle}; use clip_scroll_tree::CoordinateSystemId; +use device::TextureFilter; +use gpu_cache::GpuCache; use gpu_types::{ClipScrollNodeIndex, PictureType}; -use internal_types::RenderPassIndex; +use internal_types::{FastHashMap, RenderPassIndex, SourceTexture}; use picture::ContentOrigin; use prim_store::{PrimitiveIndex}; #[cfg(feature = "debugger")] use print_tree::{PrintTreePrinter}; +use resource_cache::CacheItem; use std::{cmp, ops, usize, f32, i32}; use std::rc::Rc; +use texture_cache::{TextureCache, TextureCacheHandle}; use tiling::{RenderPass, RenderTargetIndex}; use tiling::{RenderTargetKind}; @@ -35,72 +39,82 @@ pub struct RenderTaskTree { pub task_data: Vec, } -pub type ClipChain = Option>; +pub type ClipChainNodeRef = Option>; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ClipChainNode { pub work_item: ClipWorkItem, pub local_clip_rect: LayerRect, pub screen_outer_rect: DeviceIntRect, pub screen_inner_rect: DeviceIntRect, - pub combined_outer_screen_rect: DeviceIntRect, - pub combined_inner_screen_rect: DeviceIntRect, - pub prev: ClipChain, + pub prev: ClipChainNodeRef, } -impl ClipChainNode { - pub fn new( +#[derive(Debug, Clone)] +pub struct ClipChain { + pub combined_outer_screen_rect: DeviceIntRect, + pub combined_inner_screen_rect: DeviceIntRect, + pub nodes: ClipChainNodeRef, +} + +impl ClipChain { + pub fn empty(screen_rect: &DeviceIntRect) -> ClipChain { + ClipChain { + combined_inner_screen_rect: *screen_rect, + combined_outer_screen_rect: *screen_rect, + nodes: None, + } + } + + pub fn new_with_added_node( + &self, work_item: ClipWorkItem, local_clip_rect: LayerRect, screen_outer_rect: DeviceIntRect, screen_inner_rect: DeviceIntRect, - parent_chain: ClipChain, - ) -> ClipChainNode { - let mut node = ClipChainNode { + ) -> ClipChain { + let new_node = ClipChainNode { work_item, local_clip_rect, screen_outer_rect, screen_inner_rect, - combined_outer_screen_rect: screen_outer_rect, - combined_inner_screen_rect: screen_inner_rect, prev: None, }; - node.set_parent(parent_chain); - node + + let mut new_chain = self.clone(); + new_chain.add_node(new_node); + new_chain } - fn set_parent(&mut self, new_parent: ClipChain) { - self.prev = new_parent.clone(); - - let parent_node = match new_parent { - Some(ref parent_node) => parent_node, - None => return, - }; + pub fn add_node(&mut self, mut new_node: ClipChainNode) { + new_node.prev = self.nodes.clone(); // If this clip's outer rectangle is completely enclosed by the clip // chain's inner rectangle, then the only clip that matters from this point // on is this clip. We can disconnect this clip from the parent clip chain. - if parent_node.combined_inner_screen_rect.contains_rect(&self.screen_outer_rect) { - self.prev = None; + if self.combined_inner_screen_rect.contains_rect(&new_node.screen_outer_rect) { + new_node.prev = None; } self.combined_outer_screen_rect = - parent_node.combined_outer_screen_rect.intersection(&self.screen_outer_rect) + self.combined_outer_screen_rect.intersection(&new_node.screen_outer_rect) .unwrap_or_else(DeviceIntRect::zero); self.combined_inner_screen_rect = - parent_node.combined_inner_screen_rect.intersection(&self.screen_inner_rect) + self.combined_inner_screen_rect.intersection(&new_node.screen_inner_rect) .unwrap_or_else(DeviceIntRect::zero); + + self.nodes = Some(Rc::new(new_node)); } } pub struct ClipChainNodeIter { - pub current: ClipChain, + pub current: ClipChainNodeRef, } impl Iterator for ClipChainNodeIter { type Item = Rc; - fn next(&mut self) -> ClipChain { + fn next(&mut self) -> ClipChainNodeRef { let previous = self.current.clone(); self.current = match self.current { Some(ref item) => item.prev.clone(), @@ -150,7 +164,8 @@ impl RenderTaskTree { RenderTaskLocation::Fixed => { debug_assert!(pass_index == passes.len() - 1); } - RenderTaskLocation::Dynamic(..) => { + RenderTaskLocation::Dynamic(..) | + RenderTaskLocation::TextureCache(..) => { debug_assert!(pass_index < passes.len() - 1); } } @@ -170,10 +185,7 @@ impl RenderTaskTree { } pub fn get_task_address(&self, id: RenderTaskId) -> RenderTaskAddress { - match self[id].kind { - RenderTaskKind::Alias(alias_id) => RenderTaskAddress(alias_id.0), - _ => RenderTaskAddress(id.0), - } + RenderTaskAddress(id.0) } pub fn build(&mut self) { @@ -196,19 +208,11 @@ impl ops::IndexMut for RenderTaskTree { } } -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub enum RenderTaskKey { - /// Draw the alpha mask for a shared clip. - CacheMask(ClipId), - CacheScaling(BoxShadowCacheKey, DeviceIntSize), - CacheBlur(BoxShadowCacheKey, i32), - CachePicture(BoxShadowCacheKey), -} - #[derive(Debug)] pub enum RenderTaskLocation { Fixed, Dynamic(Option<(DeviceIntPoint, RenderTargetIndex)>, DeviceIntSize), + TextureCache(SourceTexture, i32, DeviceIntRect), } #[derive(Debug, Clone)] @@ -238,7 +242,6 @@ pub struct PictureTask { pub struct BlurTask { pub blur_std_deviation: f32, pub target_kind: RenderTargetKind, - pub regions: Vec, pub color: PremultipliedColorF, pub scale_factor: f32, } @@ -249,9 +252,6 @@ impl BlurTask { pt.add_item(format!("std deviation: {}", self.blur_std_deviation)); pt.add_item(format!("target: {:?}", self.target_kind)); pt.add_item(format!("scale: {}", self.scale_factor)); - for region in &self.regions { - pt.add_item(format!("region {:?}", region)); - } } } @@ -267,7 +267,6 @@ pub enum RenderTaskKind { VerticalBlur(BlurTask), HorizontalBlur(BlurTask), Readback(DeviceIntRect), - Alias(RenderTaskId), Scaling(RenderTargetKind), } @@ -283,7 +282,6 @@ pub enum ClearMode { #[derive(Debug)] pub struct RenderTask { - pub cache_key: Option, pub location: RenderTaskLocation, pub children: Vec, pub kind: RenderTaskKind, @@ -300,7 +298,6 @@ impl RenderTask { color: PremultipliedColorF, clear_mode: ClearMode, children: Vec, - box_shadow_cache_key: Option, pic_type: PictureType, ) -> Self { let location = match size { @@ -309,10 +306,6 @@ impl RenderTask { }; RenderTask { - cache_key: match box_shadow_cache_key { - Some(key) => Some(RenderTaskKey::CachePicture(key)), - None => None, - }, children, location, kind: RenderTaskKind::Picture(PictureTask { @@ -329,7 +322,6 @@ impl RenderTask { pub fn new_readback(screen_rect: DeviceIntRect) -> Self { RenderTask { - cache_key: None, children: Vec::new(), location: RenderTaskLocation::Dynamic(None, screen_rect.size), kind: RenderTaskKind::Readback(screen_rect), @@ -339,13 +331,11 @@ impl RenderTask { } pub fn new_mask( - key: Option, outer_rect: DeviceIntRect, clips: Vec, prim_coordinate_system_id: CoordinateSystemId, ) -> RenderTask { RenderTask { - cache_key: key.map(RenderTaskKey::CacheMask), children: Vec::new(), location: RenderTaskLocation::Dynamic(None, outer_rect.size), kind: RenderTaskKind::CacheMask(CacheMaskTask { @@ -381,11 +371,9 @@ impl RenderTask { src_task_id: RenderTaskId, render_tasks: &mut RenderTaskTree, target_kind: RenderTargetKind, - regions: &[LayerRect], clear_mode: ClearMode, color: PremultipliedColorF, - box_shadow_cache_key: Option, - ) -> Self { + ) -> (Self, f32) { // Adjust large std deviation value. let mut adjusted_blur_std_deviation = blur_std_deviation; let blur_target_size = render_tasks[src_task_id].get_dynamic_size(); @@ -404,23 +392,17 @@ impl RenderTask { target_kind, downscaling_src_task_id, adjusted_blur_target_size, - box_shadow_cache_key, ); downscaling_src_task_id = render_tasks.add(downscaling_task); } scale_factor = blur_target_size.width as f32 / adjusted_blur_target_size.width as f32; let blur_task_v = RenderTask { - cache_key: match box_shadow_cache_key { - Some(key) => Some(RenderTaskKey::CacheBlur(key, 0)), - None => None, - }, children: vec![downscaling_src_task_id], location: RenderTaskLocation::Dynamic(None, adjusted_blur_target_size), kind: RenderTaskKind::VerticalBlur(BlurTask { blur_std_deviation: adjusted_blur_std_deviation, target_kind, - regions: regions.to_vec(), color, scale_factor, }), @@ -431,16 +413,11 @@ impl RenderTask { let blur_task_v_id = render_tasks.add(blur_task_v); let blur_task_h = RenderTask { - cache_key: match box_shadow_cache_key { - Some(key) => Some(RenderTaskKey::CacheBlur(key, 1)), - None => None, - }, children: vec![blur_task_v_id], location: RenderTaskLocation::Dynamic(None, adjusted_blur_target_size), kind: RenderTaskKind::HorizontalBlur(BlurTask { blur_std_deviation: adjusted_blur_std_deviation, target_kind, - regions: regions.to_vec(), color, scale_factor, }), @@ -448,20 +425,15 @@ impl RenderTask { pass_index: None, }; - blur_task_h + (blur_task_h, scale_factor) } pub fn new_scaling( target_kind: RenderTargetKind, src_task_id: RenderTaskId, target_size: DeviceIntSize, - box_shadow_cache_key: Option, ) -> Self { RenderTask { - cache_key: match box_shadow_cache_key { - Some(key) => Some(RenderTaskKey::CacheScaling(key, target_size)), - None => None, - }, children: vec![src_task_id], location: RenderTaskLocation::Dynamic(None, target_size), kind: RenderTaskKind::Scaling(target_kind), @@ -527,8 +499,7 @@ impl RenderTask { ) } RenderTaskKind::Readback(..) | - RenderTaskKind::Scaling(..) | - RenderTaskKind::Alias(..) => { + RenderTaskKind::Scaling(..) => { ( [0.0; 3], [0.0; 4], @@ -560,6 +531,7 @@ impl RenderTask { match self.location { RenderTaskLocation::Fixed => DeviceIntSize::zero(), RenderTaskLocation::Dynamic(_, size) => size, + RenderTaskLocation::TextureCache(_, _, rect) => rect.size, } } @@ -588,6 +560,9 @@ impl RenderTask { RenderTaskLocation::Dynamic(None, _) => { (DeviceIntRect::zero(), RenderTargetIndex(0)) } + RenderTaskLocation::TextureCache(_, layer, rect) => { + (rect, RenderTargetIndex(layer as usize)) + } } } @@ -611,10 +586,6 @@ impl RenderTask { RenderTaskKind::Picture(ref task_info) => { task_info.target_kind } - - RenderTaskKind::Alias(..) => { - panic!("BUG: target_kind() called on invalidated task"); - } } } @@ -633,10 +604,6 @@ impl RenderTask { RenderTaskKind::Scaling(..) => false, RenderTaskKind::CacheMask(..) => true, - - RenderTaskKind::Alias(..) => { - panic!("BUG: is_shared() called on aliased task"); - } } } @@ -667,10 +634,6 @@ impl RenderTask { pt.new_level("Scaling".to_owned()); pt.add_item(format!("kind: {:?}", kind)); } - RenderTaskKind::Alias(ref alias_id) => { - pt.add_item(format!("Alias of {:?}", alias_id)); - return false - } } pt.add_item(format!("clear to: {:?}", self.clear_mode)); @@ -685,3 +648,131 @@ impl RenderTask { true } } + +#[derive(Debug, Hash, PartialEq, Eq)] +pub enum RenderTaskCacheKeyKind { + BoxShadow(BoxShadowCacheKey), +} + +#[derive(Debug, Hash, PartialEq, Eq)] +pub struct RenderTaskCacheKey { + pub size: DeviceIntSize, + pub kind: RenderTaskCacheKeyKind, +} + +struct RenderTaskCacheEntry { + handle: TextureCacheHandle, +} + +// A cache of render tasks that are stored in the texture +// cache for usage across frames. +pub struct RenderTaskCache { + entries: FastHashMap, +} + +impl RenderTaskCache { + pub fn new() -> RenderTaskCache { + RenderTaskCache { + entries: FastHashMap::default(), + } + } + + pub fn clear(&mut self) { + self.entries.clear(); + } + + pub fn begin_frame( + &mut self, + texture_cache: &mut TextureCache, + ) { + // Drop any items from the cache that have been + // evicted from the texture cache. + // + // This isn't actually necessary for the texture + // cache to be able to evict old render tasks. + // It will evict render tasks as required, since + // the access time in the texture cache entry will + // be stale if this task hasn't been requested + // for a while. + // + // Nonetheless, we should remove stale entries + // from here so that this hash map doesn't + // grow indefinitely! + self.entries.retain(|_, value| { + texture_cache.is_allocated(&value.handle) + }); + } + + pub fn request_render_task( + &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, [f32; 3]) { + // Get the texture cache handle for this cache key, + // or create one. + let cache_entry = self.entries + .entry(key) + .or_insert(RenderTaskCacheEntry { + handle: TextureCacheHandle::new(), + }); + + // Check if this texture cache handle is valie. + if texture_cache.request(&mut cache_entry.handle, gpu_cache) { + // Invoke user closure to get render task chain + // to draw this into the texture cache. + let (render_task_id, user_data) = f(render_tasks); + let render_task = &mut render_tasks[render_task_id]; + + // Find out what size to alloc in the texture cache. + let size = match render_task.location { + RenderTaskLocation::Fixed | + RenderTaskLocation::TextureCache(..) => { + panic!("BUG: dynamic task was expected"); + } + RenderTaskLocation::Dynamic(_, size) => size, + }; + + // TODO(gw): Support color tasks in the texture cache, + // and perhaps consider if we can determine + // if some tasks are opaque as an optimization. + let descriptor = ImageDescriptor::new( + size.width as u32, + size.height as u32, + ImageFormat::R8, + false, + ); + + // Allocate space in the texture cache, but don't supply + // and CPU-side data to be uploaded. + texture_cache.update( + &mut cache_entry.handle, + descriptor, + TextureFilter::Linear, + None, + user_data, + None, + gpu_cache, + ); + + // Get the allocation details in the texture cache, and store + // this in the render task. The renderer will draw this + // task into the appropriate layer and rect of the texture + // cache on this frame. + let (texture_id, texture_layer, uv_rect) = + texture_cache.get_cache_location(&cache_entry.handle); + + render_task.location = RenderTaskLocation::TextureCache( + texture_id, + texture_layer, + uv_rect.to_i32() + ); + } + + // Finally, return the texture cache handle that we know + // is now up to date. + texture_cache.get(&cache_entry.handle) + } +} diff --git a/gfx/webrender/src/renderer.rs b/gfx/webrender/src/renderer.rs index 0cc07c7b6de1..834c7e770bdb 100644 --- a/gfx/webrender/src/renderer.rs +++ b/gfx/webrender/src/renderer.rs @@ -66,7 +66,7 @@ use texture_cache::TextureCache; use thread_profiler::{register_thread_with_profiler, write_profile}; use tiling::{AlphaRenderTarget, ColorRenderTarget}; use tiling::{RenderPass, RenderPassKind, RenderTargetList}; -use tiling::{Frame, RenderTarget, ScalingInfo}; +use tiling::{Frame, RenderTarget, ScalingInfo, TextureCacheRenderTarget}; use time::precise_time_ns; use util::TransformedRectKind; @@ -103,10 +103,6 @@ const GPU_TAG_CACHE_TEXT_RUN: GpuProfileTag = GpuProfileTag { label: "C_TextRun", color: debug_colors::MISTYROSE, }; -const GPU_TAG_CACHE_LINE: GpuProfileTag = GpuProfileTag { - label: "C_Line", - color: debug_colors::BROWN, -}; const GPU_TAG_SETUP_TARGET: GpuProfileTag = GpuProfileTag { label: "target init", color: debug_colors::SLATEGREY, @@ -413,11 +409,6 @@ const DESC_BLUR: VertexDescriptor = VertexDescriptor { count: 1, kind: VertexAttributeKind::I32, }, - VertexAttribute { - name: "aBlurRegion", - count: 4, - kind: VertexAttributeKind::F32 - }, ], }; @@ -769,6 +760,7 @@ impl SourceTextureResolver { #[allow(dead_code)] // SubpixelVariableTextColor is not used at the moment. pub enum BlendMode { None, + Alpha, PremultipliedAlpha, PremultipliedDestOut, SubpixelDualSource, @@ -1299,6 +1291,7 @@ impl BrushShader { BlendMode::None => { self.opaque.bind(device, projection, mode, renderer_errors) } + BlendMode::Alpha | BlendMode::PremultipliedAlpha | BlendMode::PremultipliedDestOut | BlendMode::SubpixelDualSource | @@ -2099,7 +2092,7 @@ impl Renderer { &mut texture, 8, 8, - ImageFormat::A8, + ImageFormat::R8, TextureFilter::Nearest, None, 1, @@ -2553,11 +2546,6 @@ impl Renderer { batch.len(), ); } - debug_target.add( - debug_server::BatchKind::Cache, - "Lines", - target.alpha_batcher.line_cache_prims.len(), - ); for batch in target .alpha_batcher @@ -2585,6 +2573,19 @@ impl Renderer { debug_target } + #[cfg(feature = "debugger")] + fn debug_texture_cache_target(target: &TextureCacheRenderTarget) -> debug_server::Target { + let mut debug_target = debug_server::Target::new("Texture Cache"); + + debug_target.add( + debug_server::BatchKind::Cache, + "Horizontal Blur", + target.horizontal_blurs.len(), + ); + + debug_target + } + #[cfg(feature = "debugger")] fn get_passes_for_debugger(&self) -> String { let mut debug_passes = debug_server::PassList::new(); @@ -2596,9 +2597,10 @@ impl Renderer { RenderPassKind::MainFramebuffer(ref target) => { debug_targets.push(Self::debug_color_target(target)); } - RenderPassKind::OffScreen { ref alpha, ref color } => { + RenderPassKind::OffScreen { ref alpha, ref color, ref texture_cache } => { debug_targets.extend(alpha.targets.iter().map(Self::debug_alpha_target)); debug_targets.extend(color.targets.iter().map(Self::debug_color_target)); + debug_targets.extend(texture_cache.iter().map(|(_, target)| Self::debug_texture_cache_target(target))) } } @@ -3444,27 +3446,6 @@ impl Renderer { ); } } - if !target.alpha_batcher.line_cache_prims.is_empty() { - // TODO(gw): Technically, we don't need blend for solid - // lines. We could check that here? - self.device.set_blend(true); - self.device.set_blend_mode_premultiplied_alpha(); - - let _timer = self.gpu_profile.start_timer(GPU_TAG_CACHE_LINE); - self.brush_line.bind( - &mut self.device, - BlendMode::PremultipliedAlpha, - projection, - 0, - &mut self.renderer_errors, - ); - self.draw_instanced_batch( - &target.alpha_batcher.line_cache_prims, - VertexArrayKind::Primitive, - &BatchTextures::no_texture(), - stats, - ); - } //TODO: record the pixel count for cached primitives @@ -3512,6 +3493,7 @@ impl Renderer { 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, @@ -3538,6 +3520,7 @@ impl Renderer { self.device.set_blend(true); match batch.key.blend_mode { + BlendMode::Alpha => panic!("Attempt to composite non-premultiplied text primitives."), BlendMode::PremultipliedAlpha => { self.device.set_blend_mode_premultiplied_alpha(); @@ -3706,6 +3689,10 @@ impl Renderer { BlendMode::None => { self.device.set_blend(false); } + BlendMode::Alpha => { + self.device.set_blend(true); + self.device.set_blend_mode_alpha(); + } BlendMode::PremultipliedAlpha => { self.device.set_blend(true); self.device.set_blend_mode_premultiplied_alpha(); @@ -3963,6 +3950,51 @@ impl Renderer { self.gpu_profile.finish_sampler(alpha_sampler); } + fn draw_texture_cache_target( + &mut self, + texture: &SourceTexture, + layer: i32, + target: &TextureCacheRenderTarget, + stats: &mut RendererStats, + ) { + let projection = { + let texture = self.texture_resolver + .resolve(texture) + .expect("BUG: invalid target texture"); + let target_size = texture.get_dimensions(); + + self.device + .bind_draw_target(Some((texture, layer)), Some(target_size)); + self.device.disable_depth(); + self.device.disable_depth_write(); + self.device.set_blend(false); + + Transform3D::ortho( + 0.0, + target_size.width as f32, + 0.0, + target_size.height as f32, + ORTHO_NEAR_PLANE, + ORTHO_FAR_PLANE, + ) + }; + + // Draw any blurs for this target. + if !target.horizontal_blurs.is_empty() { + let _timer = self.gpu_profile.start_timer(GPU_TAG_BLUR); + + self.cs_blur_a8 + .bind(&mut self.device, &projection, 0, &mut self.renderer_errors); + + self.draw_instanced_batch( + &target.horizontal_blurs, + VertexArrayKind::Blur, + &BatchTextures::no_texture(), + stats, + ); + } + } + fn update_deferred_resolves(&mut self, frame: &Frame) -> Option { // The first thing we do is run through any pending deferred // resolves, and use a callback to get the UV rect for this @@ -4082,6 +4114,7 @@ impl Renderer { } } else { if list.texture.is_some() { + list.check_ready(); return } match self.texture_resolver.render_target_pool.pop() { @@ -4103,13 +4136,14 @@ impl Renderer { None, ); list.texture = Some(texture); + list.check_ready(); } fn prepare_tile_frame(&mut self, frame: &mut Frame) { // Init textures and render targets to match this scene. // First pass grabs all the perfectly matching targets from the pool. for pass in &mut frame.passes { - if let RenderPassKind::OffScreen { ref mut alpha, ref mut color } = pass.kind { + if let RenderPassKind::OffScreen { ref mut alpha, ref mut color, .. } = pass.kind { self.prepare_target_list(alpha, true); self.prepare_target_list(color, true); } @@ -4123,7 +4157,7 @@ impl Renderer { // Some of the textures are already assigned by `prepare_frame`. // Now re-allocate the space for the rest of the target textures. for pass in &mut frame.passes { - if let RenderPassKind::OffScreen { ref mut alpha, ref mut color } = pass.kind { + if let RenderPassKind::OffScreen { ref mut alpha, ref mut color, .. } = pass.kind { self.prepare_target_list(alpha, false); self.prepare_target_list(color, false); } @@ -4216,9 +4250,17 @@ impl Renderer { (None, None) } - RenderPassKind::OffScreen { ref mut alpha, ref mut color } => { - assert!(alpha.targets.is_empty() || alpha.texture.is_some()); - assert!(color.targets.is_empty() || color.texture.is_some()); + RenderPassKind::OffScreen { ref mut alpha, ref mut color, ref mut texture_cache } => { + alpha.check_ready(); + color.check_ready(); + for (&(texture_id, target_index), target) in texture_cache { + self.draw_texture_cache_target( + &texture_id, + target_index, + target, + stats, + ); + } for (target_index, target) in alpha.targets.iter().enumerate() { stats.alpha_target_count += 1; diff --git a/gfx/webrender/src/resource_cache.rs b/gfx/webrender/src/resource_cache.rs index 2066656e26c8..a9cd0ab451a4 100644 --- a/gfx/webrender/src/resource_cache.rs +++ b/gfx/webrender/src/resource_cache.rs @@ -4,8 +4,7 @@ use api::{AddFont, BlobImageData, BlobImageResources, ResourceUpdate, ResourceUpdates}; use api::{BlobImageDescriptor, BlobImageError, BlobImageRenderer, BlobImageRequest}; -use api::ColorF; -use api::{DevicePoint, DeviceUintRect, DeviceUintSize}; +use api::{ColorF, DevicePoint, DeviceUintRect, DeviceUintSize}; use api::{Epoch, FontInstanceKey, FontKey, FontTemplate}; use api::{ExternalImageData, ExternalImageType}; use api::{FontInstanceOptions, FontInstancePlatformOptions, FontVariation}; @@ -25,6 +24,7 @@ use internal_types::{FastHashMap, FastHashSet, SourceTexture, TextureUpdateList} use internal_types::ExternalCaptureImage; use profiler::{ResourceProfileCounters, TextureCacheProfileCounters}; use rayon::ThreadPool; +use render_task::{RenderTaskCache, RenderTaskCacheKey, RenderTaskId, RenderTaskTree}; use std::collections::hash_map::Entry::{self, Occupied, Vacant}; use std::cmp; use std::fmt::Debug; @@ -52,6 +52,7 @@ pub struct GlyphFetchResult { // storing the coordinates as texel values // we don't need to go through and update // various CPU-side structures. +#[derive(Debug)] pub struct CacheItem { pub texture_id: SourceTexture, pub uv_rect_handle: GpuCacheHandle, @@ -219,6 +220,7 @@ impl BlobImageResources for Resources { pub struct ResourceCache { cached_glyphs: GlyphCache, cached_images: ImageCache, + cached_render_tasks: RenderTaskCache, resources: Resources, state: State, @@ -247,6 +249,7 @@ impl ResourceCache { ResourceCache { cached_glyphs: GlyphCache::new(), cached_images: ResourceClassCache::new(), + cached_render_tasks: RenderTaskCache::new(), resources: Resources { font_templates: FastHashMap::default(), font_instances: Arc::new(RwLock::new(FastHashMap::default())), @@ -278,6 +281,27 @@ impl ResourceCache { } } + // Request the texture cache item for a cacheable render + // task. If the item is already cached, the texture cache + // handle will be returned. Otherwise, the user supplied + // closure will be invoked to generate the render task + // chain that is required to draw this task. + pub fn request_render_task( + &mut self, + key: RenderTaskCacheKey, + gpu_cache: &mut GpuCache, + render_tasks: &mut RenderTaskTree, + f: F, + ) -> CacheItem where F: FnMut(&mut RenderTaskTree) -> (RenderTaskId, [f32; 3]) { + self.cached_render_tasks.request_render_task( + key, + &mut self.texture_cache, + gpu_cache, + render_tasks, + f + ) + } + pub fn update_resources( &mut self, updates: ResourceUpdates, @@ -762,6 +786,7 @@ impl ResourceCache { debug_assert_eq!(self.state, State::Idle); self.state = State::AddResources; self.texture_cache.begin_frame(frame_id); + self.cached_render_tasks.begin_frame(&mut self.texture_cache); self.current_frame_id = frame_id; } @@ -871,7 +896,7 @@ impl ResourceCache { &mut entry.texture_cache_handle, descriptor, filter, - image_data, + Some(image_data), [0.0; 3], image_template.dirty_rect, gpu_cache, @@ -896,6 +921,7 @@ impl ResourceCache { // recently used resources. self.cached_images.clear(); self.cached_glyphs.clear(); + self.cached_render_tasks.clear(); } pub fn clear_namespace(&mut self, namespace: IdNamespace) { @@ -1127,6 +1153,7 @@ impl ResourceCache { info!("loading resource cache"); self.cached_glyphs.clear(); self.cached_images.clear(); + self.cached_render_tasks.clear(); self.state = State::Idle; self.current_frame_id = FrameId(0); diff --git a/gfx/webrender/src/texture_cache.rs b/gfx/webrender/src/texture_cache.rs index 638819c9fd37..cc40e7e9d046 100644 --- a/gfx/webrender/src/texture_cache.rs +++ b/gfx/webrender/src/texture_cache.rs @@ -206,7 +206,7 @@ impl TextureCache { TextureCache { max_texture_size, array_a8_linear: TextureArray::new( - ImageFormat::A8, + ImageFormat::R8, TextureFilter::Linear, TEXTURE_ARRAY_LAYERS_LINEAR, ), @@ -284,7 +284,7 @@ impl TextureCache { handle: &mut TextureCacheHandle, descriptor: ImageDescriptor, filter: TextureFilter, - data: ImageData, + data: Option, user_data: [f32; 3], mut dirty_rect: Option, gpu_cache: &mut GpuCache, @@ -337,25 +337,27 @@ impl TextureCache { // Create an update command, which the render thread processes // to upload the new image data into the correct location // in GPU memory. - let (layer_index, origin) = match entry.kind { - EntryKind::Standalone { .. } => (0, DeviceUintPoint::zero()), - EntryKind::Cache { - layer_index, - origin, - .. - } => (layer_index, origin), - }; + if let Some(data) = data { + let (layer_index, origin) = match entry.kind { + EntryKind::Standalone { .. } => (0, DeviceUintPoint::zero()), + EntryKind::Cache { + layer_index, + origin, + .. + } => (layer_index, origin), + }; - let op = TextureUpdate::new_update( - data, - &descriptor, - origin, - entry.size, - entry.texture_id, - layer_index as i32, - dirty_rect, - ); - self.pending_updates.push(op); + let op = TextureUpdate::new_update( + data, + &descriptor, + origin, + entry.size, + entry.texture_id, + layer_index as i32, + dirty_rect, + ); + self.pending_updates.push(op); + } } // Get a specific region by index from a shared texture array. @@ -365,18 +367,26 @@ impl TextureCache { region_index: u16 ) -> &mut TextureRegion { let texture_array = match (format, filter) { - (ImageFormat::A8, TextureFilter::Linear) => &mut self.array_a8_linear, + (ImageFormat::R8, TextureFilter::Linear) => &mut self.array_a8_linear, (ImageFormat::BGRA8, TextureFilter::Linear) => &mut self.array_rgba8_linear, (ImageFormat::BGRA8, TextureFilter::Nearest) => &mut self.array_rgba8_nearest, (ImageFormat::Invalid, _) | (ImageFormat::RGBAF32, _) | (ImageFormat::RG8, _) | - (ImageFormat::A8, TextureFilter::Nearest) => unreachable!(), + (ImageFormat::R8, TextureFilter::Nearest) => unreachable!(), }; &mut texture_array.regions[region_index as usize] } + // Check if a given texture handle has a valid allocation + // in the texture cache. + pub fn is_allocated(&self, handle: &TextureCacheHandle) -> bool { + handle.entry.as_ref().map_or(false, |handle| { + self.entries.get_opt(handle).is_some() + }) + } + // Retrieve the details of an item in the cache. This is used // during batch creation to provide the resource rect address // to the shaders and texture ID to the batching logic. @@ -398,6 +408,36 @@ impl TextureCache { } } + // A more detailed version of get(). This allows access to the actual + // device rect of the cache allocation. + pub fn get_cache_location( + &self, + handle: &TextureCacheHandle, + ) -> (SourceTexture, i32, DeviceUintRect) { + let handle = handle + .entry + .as_ref() + .expect("BUG: handle not requested earlier in frame"); + + let entry = self.entries + .get_opt(handle) + .expect("BUG: was dropped from cache or not updated!"); + debug_assert_eq!(entry.last_access, self.frame_id); + let (layer_index, origin) = match entry.kind { + EntryKind::Standalone { .. } => { + (0, DeviceUintPoint::zero()) + } + EntryKind::Cache { + layer_index, + origin, + .. + } => (layer_index, origin), + }; + (SourceTexture::TextureCache(entry.texture_id), + layer_index as i32, + DeviceUintRect::new(origin, entry.size)) + } + // Expire old standalone textures. fn expire_old_standalone_entries(&mut self) { let mut eviction_candidates = Vec::new(); @@ -470,7 +510,7 @@ impl TextureCache { // want to evict everything we can, since that will result in // more items being uploaded than necessary. // Instead, we say we will keep evicting until both of these - // consitions are met: + // conditions are met: // - We have evicted some arbitrary number of items (512 currently). // AND // - We have freed an item that will definitely allow us to @@ -537,12 +577,12 @@ impl TextureCache { ) -> Option { // Work out which cache it goes in, based on format. let texture_array = match (descriptor.format, filter) { - (ImageFormat::A8, TextureFilter::Linear) => &mut self.array_a8_linear, + (ImageFormat::R8, TextureFilter::Linear) => &mut self.array_a8_linear, (ImageFormat::BGRA8, TextureFilter::Linear) => &mut self.array_rgba8_linear, (ImageFormat::BGRA8, TextureFilter::Nearest) => &mut self.array_rgba8_nearest, (ImageFormat::Invalid, _) | (ImageFormat::RGBAF32, _) | - (ImageFormat::A8, TextureFilter::Nearest) | + (ImageFormat::R8, TextureFilter::Nearest) | (ImageFormat::RG8, _) => unreachable!(), }; diff --git a/gfx/webrender/src/tiling.rs b/gfx/webrender/src/tiling.rs index 9e0786d3a5ee..b07760d538b3 100644 --- a/gfx/webrender/src/tiling.rs +++ b/gfx/webrender/src/tiling.rs @@ -14,16 +14,15 @@ use gpu_cache::{GpuCache, GpuCacheUpdateList}; use gpu_types::{BlurDirection, BlurInstance, BrushInstance, ClipChainRectIndex}; use gpu_types::{ClipScrollNodeData, ClipScrollNodeIndex}; use gpu_types::{PrimitiveInstance}; -use internal_types::{FastHashMap, RenderPassIndex}; +use internal_types::{FastHashMap, RenderPassIndex, SourceTexture}; use picture::{PictureKind}; use prim_store::{PrimitiveIndex, PrimitiveKind, PrimitiveStore}; -use prim_store::{BrushMaskKind, BrushKind, DeferredResolve}; +use prim_store::{BrushMaskKind, BrushKind, DeferredResolve, EdgeAaSegmentMask}; use profiler::FrameProfileCounters; -use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskKey, RenderTaskKind}; +use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskKind}; use render_task::{BlurTask, ClearMode, RenderTaskLocation, RenderTaskTree}; use resource_cache::{ResourceCache}; use std::{cmp, usize, f32, i32}; -use std::collections::hash_map::Entry; use texture_allocator::GuillotineAllocator; const MIN_TARGET_SIZE: u32 = 2048; @@ -38,12 +37,6 @@ pub struct ScrollbarPrimitive { #[derive(Debug, Copy, Clone)] pub struct RenderTargetIndex(pub usize); -#[derive(Debug)] -struct DynamicTaskInfo { - task_id: RenderTaskId, - rect: DeviceIntRect, -} - pub struct RenderTargetContext<'a> { pub device_pixel_scale: DevicePixelScale, pub prim_store: &'a PrimitiveStore, @@ -201,6 +194,22 @@ impl RenderTargetList { pub fn needs_depth(&self) -> bool { self.targets.iter().any(|target| target.needs_depth()) } + + pub fn check_ready(&self) { + match self.texture { + Some(ref t) => { + assert_eq!(t.get_dimensions(), self.max_size); + assert_eq!(t.get_format(), self.format); + assert_eq!(t.get_render_target_layer_count(), self.targets.len()); + assert_eq!(t.get_layer_count() as usize, self.targets.len()); + assert_eq!(t.has_depth(), t.get_rt_info().unwrap().has_depth); + assert_eq!(t.has_depth(), self.needs_depth()); + } + None => { + assert!(self.targets.is_empty()) + } + } + } } /// Frame output information for a given pipeline ID. @@ -282,9 +291,6 @@ impl RenderTarget for ColorRenderTarget { let task = &render_tasks[task_id]; match task.kind { - RenderTaskKind::Alias(..) => { - panic!("BUG: add_task() called on invalidated task"); - } RenderTaskKind::VerticalBlur(ref info) => { info.add_instances( &mut self.vertical_blurs, @@ -409,9 +415,6 @@ impl RenderTarget for AlphaRenderTarget { } match task.kind { - RenderTaskKind::Alias(..) => { - panic!("BUG: add_task() called on invalidated task"); - } RenderTaskKind::Readback(..) => { panic!("Should not be added to alpha target!"); } @@ -465,13 +468,15 @@ impl RenderTarget for AlphaRenderTarget { clip_task_address: RenderTaskAddress(0), z: 0, segment_index: 0, + edge_flags: EdgeAaSegmentMask::empty(), user_data0: 0, user_data1: 0, }; let brush = &ctx.prim_store.cpu_brushes[sub_metadata.cpu_prim_index.0]; let batch = match brush.kind { BrushKind::Solid { .. } | - BrushKind::Clear => { + BrushKind::Clear | + BrushKind::Line { .. } => { unreachable!("bug: unexpected brush here"); } BrushKind::Mask { ref kind, .. } => { @@ -525,12 +530,54 @@ impl RenderTarget for AlphaRenderTarget { } } +pub struct TextureCacheRenderTarget { + pub horizontal_blurs: Vec, +} + +impl TextureCacheRenderTarget { + fn new( + _size: Option, + _screen_size: DeviceIntSize, + ) -> Self { + TextureCacheRenderTarget { + horizontal_blurs: Vec::new(), + } + } + + fn add_task( + &mut self, + task_id: RenderTaskId, + render_tasks: &RenderTaskTree, + ) { + let task = &render_tasks[task_id]; + + match task.kind { + RenderTaskKind::HorizontalBlur(ref info) => { + info.add_instances( + &mut self.horizontal_blurs, + task_id, + task.children[0], + BlurDirection::Horizontal, + render_tasks, + ); + } + RenderTaskKind::VerticalBlur(..) | + RenderTaskKind::Picture(..) | + RenderTaskKind::CacheMask(..) | + RenderTaskKind::Readback(..) | + RenderTaskKind::Scaling(..) => { + panic!("BUG: unexpected task kind for texture cache target"); + } + } + } +} pub enum RenderPassKind { MainFramebuffer(ColorRenderTarget), OffScreen { alpha: RenderTargetList, color: RenderTargetList, + texture_cache: FastHashMap<(SourceTexture, i32), TextureCacheRenderTarget>, }, } @@ -542,7 +589,6 @@ pub enum RenderPassKind { pub struct RenderPass { pub kind: RenderPassKind, tasks: Vec, - dynamic_tasks: FastHashMap, } impl RenderPass { @@ -551,7 +597,6 @@ impl RenderPass { RenderPass { kind: RenderPassKind::MainFramebuffer(target), tasks: vec![], - dynamic_tasks: FastHashMap::default(), } } @@ -559,10 +604,10 @@ impl RenderPass { RenderPass { kind: RenderPassKind::OffScreen { color: RenderTargetList::new(screen_size, ImageFormat::BGRA8), - alpha: RenderTargetList::new(screen_size, ImageFormat::A8), + alpha: RenderTargetList::new(screen_size, ImageFormat::R8), + texture_cache: FastHashMap::default(), }, tasks: vec![], - dynamic_tasks: FastHashMap::default(), } } @@ -572,7 +617,7 @@ impl RenderPass { size: DeviceIntSize, target_kind: RenderTargetKind, ) { - if let RenderPassKind::OffScreen { ref mut color, ref mut alpha } = self.kind { + if let RenderPassKind::OffScreen { ref mut color, ref mut alpha, .. } = self.kind { let max_size = match target_kind { RenderTargetKind::Color => &mut color.max_size, RenderTargetKind::Alpha => &mut alpha.max_size, @@ -604,10 +649,10 @@ impl RenderPass { } target.build(ctx, gpu_cache, render_tasks, deferred_resolves); } - RenderPassKind::OffScreen { ref mut color, ref mut alpha } => { + RenderPassKind::OffScreen { ref mut color, ref mut alpha, ref mut texture_cache } => { // Step through each task, adding to batches as appropriate. for &task_id in &self.tasks { - let target_kind = { + let (target_kind, texture_target) = { let task = &mut render_tasks[task_id]; task.pass_index = Some(pass_index); let target_kind = task.target_kind(); @@ -615,22 +660,16 @@ impl RenderPass { // Find a target to assign this task to, or create a new // one if required. match task.location { - RenderTaskLocation::Fixed => {} + RenderTaskLocation::TextureCache(texture_id, layer, _) => { + // TODO(gw): When we support caching color items, we will + // need to calculate that here to get the + // correct target kind. + (RenderTargetKind::Alpha, Some((texture_id, layer))) + } + RenderTaskLocation::Fixed => { + (RenderTargetKind::Color, None) + } RenderTaskLocation::Dynamic(ref mut origin, size) => { - let dynamic_entry = match task.cache_key { - // See if this task is a duplicate. - // If so, just skip adding it! - Some(cache_key) => match self.dynamic_tasks.entry(cache_key) { - Entry::Occupied(entry) => { - debug_assert_eq!(entry.get().rect.size, size); - task.kind = RenderTaskKind::Alias(entry.get().task_id); - continue; - }, - Entry::Vacant(entry) => Some(entry), - }, - None => None, - }; - let alloc_size = DeviceUintSize::new(size.width as u32, size.height as u32); let (alloc_origin, target_index) = match target_kind { RenderTargetKind::Color => color.allocate(alloc_size), @@ -638,23 +677,26 @@ impl RenderPass { }; *origin = Some((alloc_origin.to_i32(), target_index)); - // If this task is cacheable / sharable, store it in the task hash - // for this pass. - if let Some(entry) = dynamic_entry { - entry.insert(DynamicTaskInfo { - task_id, - rect: DeviceIntRect::new(alloc_origin.to_i32(), size), - }); - } + (target_kind, None) } } - - target_kind }; - match target_kind { - RenderTargetKind::Color => color.add_task(task_id, ctx, gpu_cache, render_tasks, clip_store), - RenderTargetKind::Alpha => alpha.add_task(task_id, ctx, gpu_cache, render_tasks, clip_store), + match texture_target { + Some(texture_target) => { + let texture = texture_cache + .entry(texture_target) + .or_insert( + TextureCacheRenderTarget::new(None, DeviceIntSize::zero()) + ); + texture.add_task(task_id, render_tasks); + } + None => { + match target_kind { + RenderTargetKind::Color => color.add_task(task_id, ctx, gpu_cache, render_tasks, clip_store), + RenderTargetKind::Alpha => alpha.add_task(task_id, ctx, gpu_cache, render_tasks, clip_store), + } + } } } @@ -726,18 +768,8 @@ impl BlurTask { task_address: render_tasks.get_task_address(task_id), src_task_address: render_tasks.get_task_address(source_task_id), blur_direction, - region: LayerRect::zero(), }; - if self.regions.is_empty() { - instances.push(instance); - } else { - for region in &self.regions { - instances.push(BlurInstance { - region: *region, - ..instance - }); - } - } + instances.push(instance); } } diff --git a/gfx/webrender_api/src/display_item.rs b/gfx/webrender_api/src/display_item.rs index 390c27feb780..6e97443f13bd 100644 --- a/gfx/webrender_api/src/display_item.rs +++ b/gfx/webrender_api/src/display_item.rs @@ -111,6 +111,7 @@ pub enum SpecificDisplayItem { BoxShadow(BoxShadowDisplayItem), Gradient(GradientDisplayItem), RadialGradient(RadialGradientDisplayItem), + ClipChain(ClipChainItem), Iframe(IframeDisplayItem), PushStackingContext(PushStackingContextDisplayItem), PopStackingContext, @@ -126,6 +127,7 @@ pub enum SpecificDisplayItem { #[derive(Deserialize, Serialize)] pub enum CompletelySpecificDisplayItem { Clip(ClipDisplayItem, Vec), + ClipChain(ClipChainItem, Vec), ScrollFrame(ScrollFrameDisplayItem, Vec), StickyFrame(StickyFrameDisplayItem), Rectangle(RectangleDisplayItem), @@ -426,6 +428,12 @@ pub struct RadialGradient { pub extend_mode: ExtendMode, } // IMPLICIT stops: Vec +#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] +pub struct ClipChainItem { + pub id: ClipChainId, + pub parent: Option, +} // IMPLICIT stops: Vec + #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] pub struct RadialGradientDisplayItem { pub gradient: RadialGradient, @@ -507,6 +515,7 @@ pub struct ImageDisplayItem { pub stretch_size: LayoutSize, pub tile_spacing: LayoutSize, pub image_rendering: ImageRendering, + pub alpha_type: AlphaType, } #[repr(u32)] @@ -517,6 +526,12 @@ pub enum ImageRendering { Pixelated = 2, } +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub enum AlphaType { + Alpha = 0, + PremultipliedAlpha = 1, +} + #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] pub struct YuvImageDisplayItem { pub yuv_data: YuvData, @@ -727,9 +742,14 @@ impl ComplexClipRegion { } } + +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct ClipChainId(pub u64, pub PipelineId); + #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub enum ClipId { Clip(u64, PipelineId), + ClipChain(ClipChainId), ClipExternalId(u64, PipelineId), DynamicallyAddedNode(u64, PipelineId), } @@ -756,6 +776,7 @@ impl ClipId { pub fn pipeline_id(&self) -> PipelineId { match *self { ClipId::Clip(_, pipeline_id) | + ClipId::ClipChain(ClipChainId(_, pipeline_id)) | ClipId::ClipExternalId(_, pipeline_id) | ClipId::DynamicallyAddedNode(_, pipeline_id) => pipeline_id, } diff --git a/gfx/webrender_api/src/display_list.rs b/gfx/webrender_api/src/display_list.rs index 02d28990fb67..06ec03c1be94 100644 --- a/gfx/webrender_api/src/display_list.rs +++ b/gfx/webrender_api/src/display_list.rs @@ -2,17 +2,17 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use {BorderDetails, BorderDisplayItem, BorderRadius, BorderWidths, BoxShadowClipMode}; -use {BoxShadowDisplayItem, ClipAndScrollInfo, ClipDisplayItem, ClipId, ColorF, ComplexClipRegion}; -use {DisplayItem, ExtendMode, FilterOp, FontInstanceKey, GlyphInstance, GlyphOptions, Gradient}; -use {GradientDisplayItem, GradientStop, IframeDisplayItem, ImageDisplayItem, ImageKey, ImageMask}; -use {ImageRendering, LayerPrimitiveInfo, LayoutPoint, LayoutPrimitiveInfo, LayoutRect, LayoutSize}; -use {LayoutTransform, LayoutVector2D, LineDisplayItem, LineOrientation, LineStyle, LocalClip}; -use {MixBlendMode, PipelineId, PropertyBinding, PushStackingContextDisplayItem, RadialGradient}; -use {RadialGradientDisplayItem, RectangleDisplayItem, ScrollFrameDisplayItem, ScrollPolicy}; -use {ScrollSensitivity, Shadow, SpecificDisplayItem, StackingContext, StickyFrameDisplayItem}; -use {StickyOffsetBounds, TextDisplayItem, TransformStyle, YuvColorSpace, YuvData}; -use YuvImageDisplayItem; +use {AlphaType, BorderDetails, BorderDisplayItem, BorderRadius, BorderWidths, BoxShadowClipMode}; +use {BoxShadowDisplayItem, ClipAndScrollInfo, ClipChainId, ClipChainItem, ClipDisplayItem, ClipId}; +use {ColorF, ComplexClipRegion, DisplayItem, ExtendMode, FilterOp, FontInstanceKey, GlyphInstance}; +use {GlyphOptions, Gradient, GradientDisplayItem, GradientStop, IframeDisplayItem}; +use {ImageDisplayItem, ImageKey, ImageMask, ImageRendering, LayerPrimitiveInfo, LayoutPoint}; +use {LayoutPrimitiveInfo, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D}; +use {LineDisplayItem, LineOrientation, LineStyle, LocalClip, MixBlendMode, PipelineId}; +use {PropertyBinding, PushStackingContextDisplayItem, RadialGradient, RadialGradientDisplayItem}; +use {RectangleDisplayItem, ScrollFrameDisplayItem, ScrollPolicy, ScrollSensitivity, Shadow}; +use {SpecificDisplayItem, StackingContext, StickyFrameDisplayItem, StickyOffsetBounds}; +use {TextDisplayItem, TransformStyle, YuvColorSpace, YuvData, YuvImageDisplayItem}; use bincode; use euclid::SideOffsets2D; use serde::{Deserialize, Serialize}; @@ -86,6 +86,7 @@ pub struct BuiltDisplayListIter<'a> { cur_stops: ItemRange, cur_glyphs: ItemRange, cur_filters: ItemRange, + cur_clip_chain_items: ItemRange, cur_complex_clip: (ItemRange, usize), peeking: Peek, } @@ -197,6 +198,7 @@ impl<'a> BuiltDisplayListIter<'a> { cur_stops: ItemRange::default(), cur_glyphs: ItemRange::default(), cur_filters: ItemRange::default(), + cur_clip_chain_items: ItemRange::default(), cur_complex_clip: (ItemRange::default(), 0), peeking: Peek::NotPeeking, } @@ -223,6 +225,7 @@ impl<'a> BuiltDisplayListIter<'a> { // Don't let these bleed into another item self.cur_stops = ItemRange::default(); self.cur_complex_clip = (ItemRange::default(), 0); + self.cur_clip_chain_items = ItemRange::default(); loop { if self.data.len() == 0 { @@ -243,6 +246,9 @@ impl<'a> BuiltDisplayListIter<'a> { // This is a dummy item, skip over it continue; } + ClipChain(_) => { + self.cur_clip_chain_items = skip_slice::(self.list, &mut self.data).0; + } Clip(_) | ScrollFrame(_) => { self.cur_complex_clip = self.skip_slice::() } @@ -356,6 +362,10 @@ impl<'a, 'b> DisplayItemRef<'a, 'b> { self.iter.cur_filters } + pub fn clip_chain_items(&self) -> ItemRange { + self.iter.cur_clip_chain_items + } + pub fn display_list(&self) -> &BuiltDisplayList { self.iter.display_list() } @@ -418,20 +428,27 @@ impl Serialize for BuiltDisplayList { let mut seq = serializer.serialize_seq(None)?; let mut traversal = self.iter(); while let Some(item) = traversal.next() { - let di = item.display_item(); + let display_item = item.display_item(); let serial_di = GenericDisplayItem { - item: match di.item { - SpecificDisplayItem::Clip(v) => Clip(v, + item: match display_item.item { + SpecificDisplayItem::Clip(v) => Clip( + v, item.iter.list.get(item.iter.cur_complex_clip.0).collect() ), - SpecificDisplayItem::ScrollFrame(v) => ScrollFrame(v, + SpecificDisplayItem::ClipChain(v) => ClipChain( + v, + item.iter.list.get(item.iter.cur_clip_chain_items).collect(), + ), + SpecificDisplayItem::ScrollFrame(v) => ScrollFrame( + v, item.iter.list.get(item.iter.cur_complex_clip.0).collect() ), SpecificDisplayItem::StickyFrame(v) => StickyFrame(v), SpecificDisplayItem::Rectangle(v) => Rectangle(v), SpecificDisplayItem::ClearRectangle => ClearRectangle, SpecificDisplayItem::Line(v) => Line(v), - SpecificDisplayItem::Text(v) => Text(v, + SpecificDisplayItem::Text(v) => Text( + v, item.iter.list.get(item.iter.cur_glyphs).collect() ), SpecificDisplayItem::Image(v) => Image(v), @@ -441,7 +458,8 @@ impl Serialize for BuiltDisplayList { SpecificDisplayItem::Gradient(v) => Gradient(v), SpecificDisplayItem::RadialGradient(v) => RadialGradient(v), SpecificDisplayItem::Iframe(v) => Iframe(v), - SpecificDisplayItem::PushStackingContext(v) => PushStackingContext(v, + SpecificDisplayItem::PushStackingContext(v) => PushStackingContext( + v, item.iter.list.get(item.iter.cur_filters).collect() ), SpecificDisplayItem::PopStackingContext => PopStackingContext, @@ -451,8 +469,8 @@ impl Serialize for BuiltDisplayList { SpecificDisplayItem::PushShadow(v) => PushShadow(v), SpecificDisplayItem::PopAllShadows => PopAllShadows, }, - clip_and_scroll: di.clip_and_scroll, - info: di.info, + clip_and_scroll: display_item.clip_and_scroll, + info: display_item.info, }; seq.serialize_element(&serial_di)? } @@ -492,39 +510,44 @@ impl<'de> Deserialize<'de> for BuiltDisplayList { for complete in list { let item = DisplayItem { item: match complete.item { - Clip(v, complex_clips) => { + Clip(specific_item, complex_clips) => { push_vec(&mut temp, complex_clips); - SpecificDisplayItem::Clip(v) + SpecificDisplayItem::Clip(specific_item) }, - ScrollFrame(v, complex_clips) => { + ClipChain(specific_item, clip_chain_ids) => { + push_vec(&mut temp, clip_chain_ids); + SpecificDisplayItem::ClipChain(specific_item) + } + ScrollFrame(specific_item, complex_clips) => { push_vec(&mut temp, complex_clips); - SpecificDisplayItem::ScrollFrame(v) + SpecificDisplayItem::ScrollFrame(specific_item) }, - StickyFrame(v) => SpecificDisplayItem::StickyFrame(v), - Rectangle(v) => SpecificDisplayItem::Rectangle(v), + StickyFrame(specific_item) => SpecificDisplayItem::StickyFrame(specific_item), + Rectangle(specific_item) => SpecificDisplayItem::Rectangle(specific_item), ClearRectangle => SpecificDisplayItem::ClearRectangle, - Line(v) => SpecificDisplayItem::Line(v), - Text(v, glyphs) => { + Line(specific_item) => SpecificDisplayItem::Line(specific_item), + Text(specific_item, glyphs) => { push_vec(&mut temp, glyphs); - SpecificDisplayItem::Text(v) + SpecificDisplayItem::Text(specific_item) }, - Image(v) => SpecificDisplayItem::Image(v), - YuvImage(v) => SpecificDisplayItem::YuvImage(v), - Border(v) => SpecificDisplayItem::Border(v), - BoxShadow(v) => SpecificDisplayItem::BoxShadow(v), - Gradient(v) => SpecificDisplayItem::Gradient(v), - RadialGradient(v) => SpecificDisplayItem::RadialGradient(v), - Iframe(v) => SpecificDisplayItem::Iframe(v), - PushStackingContext(v, filters) => { + Image(specific_item) => SpecificDisplayItem::Image(specific_item), + YuvImage(specific_item) => SpecificDisplayItem::YuvImage(specific_item), + Border(specific_item) => SpecificDisplayItem::Border(specific_item), + BoxShadow(specific_item) => SpecificDisplayItem::BoxShadow(specific_item), + Gradient(specific_item) => SpecificDisplayItem::Gradient(specific_item), + RadialGradient(specific_item) => + SpecificDisplayItem::RadialGradient(specific_item), + Iframe(specific_item) => SpecificDisplayItem::Iframe(specific_item), + PushStackingContext(specific_item, filters) => { push_vec(&mut temp, filters); - SpecificDisplayItem::PushStackingContext(v) + SpecificDisplayItem::PushStackingContext(specific_item) }, PopStackingContext => SpecificDisplayItem::PopStackingContext, SetGradientStops(stops) => { push_vec(&mut temp, stops); SpecificDisplayItem::SetGradientStops }, - PushShadow(v) => SpecificDisplayItem::PushShadow(v), + PushShadow(specific_item) => SpecificDisplayItem::PushShadow(specific_item), PopAllShadows => SpecificDisplayItem::PopAllShadows, }, clip_and_scroll: complete.clip_and_scroll, @@ -766,6 +789,7 @@ pub struct SaveState { dl_len: usize, clip_stack_len: usize, next_clip_id: u64, + next_clip_chain_id: u64, } #[derive(Clone)] @@ -774,6 +798,7 @@ pub struct DisplayListBuilder { pub pipeline_id: PipelineId, clip_stack: Vec, next_clip_id: u64, + next_clip_chain_id: u64, builder_start_time: u64, /// The size of the content of this display list. This is used to allow scrolling @@ -804,6 +829,7 @@ impl DisplayListBuilder { ClipAndScrollInfo::simple(ClipId::root_scroll_node(pipeline_id)), ], next_clip_id: FIRST_CLIP_ID, + next_clip_chain_id: 0, builder_start_time: start_time, content_size, save_state: None, @@ -829,6 +855,7 @@ impl DisplayListBuilder { clip_stack_len: self.clip_stack.len(), dl_len: self.data.len(), next_clip_id: self.next_clip_id, + next_clip_chain_id: self.next_clip_chain_id, }); } @@ -839,6 +866,7 @@ impl DisplayListBuilder { self.clip_stack.truncate(state.clip_stack_len); self.data.truncate(state.dl_len); self.next_clip_id = state.next_clip_id; + self.next_clip_chain_id = state.next_clip_chain_id; } /// Discards the builder's save (indicating the attempted operation was sucessful). @@ -964,6 +992,7 @@ impl DisplayListBuilder { stretch_size: LayoutSize, tile_spacing: LayoutSize, image_rendering: ImageRendering, + alpha_type: AlphaType, key: ImageKey, ) { let item = SpecificDisplayItem::Image(ImageDisplayItem { @@ -971,6 +1000,7 @@ impl DisplayListBuilder { stretch_size, tile_spacing, image_rendering, + alpha_type, }); self.push_item(item, info); @@ -1315,6 +1345,11 @@ impl DisplayListBuilder { }) } + fn generate_clip_chain_id(&mut self) -> ClipChainId { + self.next_clip_chain_id += 1; + ClipChainId(self.next_clip_chain_id - 1, self.pipeline_id) + } + pub fn define_scroll_frame( &mut self, id: Option, @@ -1355,8 +1390,8 @@ impl DisplayListBuilder { { let id = self.generate_clip_id(id); let item = SpecificDisplayItem::ScrollFrame(ScrollFrameDisplayItem { - id: id, - image_mask: image_mask, + id, + image_mask, scroll_sensitivity, }); let info = LayoutPrimitiveInfo::with_clip_rect(content_rect, clip_rect); @@ -1367,6 +1402,21 @@ impl DisplayListBuilder { id } + pub fn define_clip_chain( + &mut self, + parent: Option, + clips: I, + ) -> ClipChainId + where + I: IntoIterator, + I::IntoIter: ExactSizeIterator + Clone, + { + let id = self.generate_clip_chain_id(); + self.push_new_empty_item(SpecificDisplayItem::ClipChain(ClipChainItem { id, parent})); + self.push_iter(clips); + id + } + pub fn define_clip( &mut self, id: Option, diff --git a/gfx/webrender_api/src/font.rs b/gfx/webrender_api/src/font.rs index 53e814dfa733..838e5a2061d5 100644 --- a/gfx/webrender_api/src/font.rs +++ b/gfx/webrender_api/src/font.rs @@ -199,6 +199,16 @@ impl Hash for FontVariation { #[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)] pub struct GlyphOptions { pub render_mode: FontRenderMode, + pub flags: FontInstanceFlags, +} + +impl Default for GlyphOptions { + fn default() -> GlyphOptions { + GlyphOptions { + render_mode: FontRenderMode::Subpixel, + flags: FontInstanceFlags::empty(), + } + } } bitflags! { @@ -210,6 +220,9 @@ bitflags! { const SYNTHETIC_BOLD = 1 << 1; const EMBEDDED_BITMAPS = 1 << 2; const SUBPIXEL_BGR = 1 << 3; + const TRANSPOSE = 1 << 4; + const FLIP_X = 1 << 5; + const FLIP_Y = 1 << 6; // Windows flags const FORCE_GDI = 1 << 16; diff --git a/gfx/webrender_api/src/image.rs b/gfx/webrender_api/src/image.rs index b3d0b964fc73..c3e184e5f092 100644 --- a/gfx/webrender_api/src/image.rs +++ b/gfx/webrender_api/src/image.rs @@ -51,7 +51,7 @@ pub struct ExternalImageData { #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub enum ImageFormat { Invalid = 0, - A8 = 1, + R8 = 1, BGRA8 = 3, RGBAF32 = 4, RG8 = 5, @@ -60,7 +60,7 @@ pub enum ImageFormat { impl ImageFormat { pub fn bytes_per_pixel(self) -> u32 { match self { - ImageFormat::A8 => 1, + ImageFormat::R8 => 1, ImageFormat::BGRA8 => 4, ImageFormat::RGBAF32 => 16, ImageFormat::RG8 => 2,