diff --git a/webrender/res/brush_image.glsl b/webrender/res/brush_image.glsl index ca10f3c7cb..379fd997f3 100644 --- a/webrender/res/brush_image.glsl +++ b/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 - vec2 texture_size = vec2(textureSize(sColor0, 0).xy); -#elif defined WR_FEATURE_COLOR_TARGET_ALPHA_MASK + // 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); - 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/webrender/res/cs_blur.glsl b/webrender/res/cs_blur.glsl index fabc548536..830c529f1d 100644 --- a/webrender/res/cs_blur.glsl +++ b/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/webrender/res/prim_shared.glsl b/webrender/res/prim_shared.glsl index 14ca38a933..ca1565a3b0 100644 --- a/webrender/res/prim_shared.glsl +++ b/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/webrender/src/batch.rs b/webrender/src/batch.rs index 0f158dc793..0190b293e9 100644 --- a/webrender/src/batch.rs +++ b/webrender/src/batch.rs @@ -16,7 +16,7 @@ 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}; @@ -109,7 +109,7 @@ impl BatchTextures { pub fn color(texture: SourceTexture) -> Self { BatchTextures { - colors: [texture, SourceTexture::Invalid, SourceTexture::Invalid], + colors: [texture, texture, SourceTexture::Invalid], } } } @@ -468,7 +468,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( @@ -833,13 +838,15 @@ impl AlphaBatcher { let picture = &ctx.prim_store.cpu_pictures[prim_metadata.cpu_prim_index.0]; - match picture.render_task_id { - Some(cache_task_id) => { - let cache_task_address = render_tasks.get_task_address(cache_task_id); - let textures = BatchTextures::render_target_cache(); - + match picture.surface { + Some(PictureSurface::TextureCache(ref cache_item)) => { match picture.kind { - PictureKind::TextShadow { .. } => { + 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())), @@ -855,12 +862,19 @@ impl AlphaBatcher { clip_task_address, z, segment_index: 0, - user_data0: cache_task_address.0 as i32, - user_data1: BrushImageKind::Simple as i32, + user_data0: cache_item.uv_rect_handle.as_int(gpu_cache), + user_data1: image_kind as i32, }; batch.push(PrimitiveInstance::from(instance)); } - PictureKind::BoxShadow { image_kind, .. } => { + } + } + Some(PictureSurface::RenderTask(cache_task_id)) => { + let cache_task_address = render_tasks.get_task_address(cache_task_id); + let textures = BatchTextures::render_target_cache(); + + match picture.kind { + PictureKind::TextShadow { .. } => { let kind = BatchKind::Brush( BrushBatchKind::Image( BrushImageSourceKind::from_render_target_kind(picture.target_kind())), @@ -877,10 +891,13 @@ impl AlphaBatcher { z, segment_index: 0, user_data0: cache_task_address.0 as i32, - user_data1: image_kind as i32, + user_data1: BrushImageKind::Simple as i32, }; batch.push(PrimitiveInstance::from(instance)); } + PictureKind::BoxShadow { .. } => { + panic!("BUG: should be handled as a texture cache surface"); + } PictureKind::Image { composite_mode, secondary_render_task_id, @@ -914,7 +931,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) => { diff --git a/webrender/src/box_shadow.rs b/webrender/src/box_shadow.rs index 2a3a06601d..b783ad4397 100644 --- a/webrender/src/box_shadow.rs +++ b/webrender/src/box_shadow.rs @@ -242,7 +242,6 @@ impl FrameBuilder { let mut pic_prim = PicturePrimitive::new_box_shadow( blur_radius, *color, - Vec::new(), clip_mode, image_kind, cache_key, @@ -321,7 +320,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, diff --git a/webrender/src/frame_builder.rs b/webrender/src/frame_builder.rs index a4af4f957b..562d4e2597 100644 --- a/webrender/src/frame_builder.rs +++ b/webrender/src/frame_builder.rs @@ -22,7 +22,7 @@ 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::{PrimitiveContainer, PrimitiveIndex, SpecificPrimitiveIndex}; @@ -33,8 +33,7 @@ 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}; @@ -1620,12 +1619,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/webrender/src/glyph_rasterizer.rs b/webrender/src/glyph_rasterizer.rs index 9bf21bf7a7..ebe907308d 100644 --- a/webrender/src/glyph_rasterizer.rs +++ b/webrender/src/glyph_rasterizer.rs @@ -400,7 +400,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 +523,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/webrender/src/gpu_types.rs b/webrender/src/gpu_types.rs index 5116f58f95..bc83c66d7a 100644 --- a/webrender/src/gpu_types.rs +++ b/webrender/src/gpu_types.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::{LayerRect, LayerToWorldTransform}; +use api::{LayerToWorldTransform}; use gpu_cache::GpuCacheAddress; use render_task::RenderTaskAddress; @@ -21,7 +21,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. diff --git a/webrender/src/picture.rs b/webrender/src/picture.rs index 9d47a50d76..099fc6af41 100644 --- a/webrender/src/picture.rs +++ b/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; + // 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, + |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 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 = 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 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 render_task = RenderTask::new_blur( - blur_std_deviation, - picture_task_id, - render_tasks, - RenderTargetKind::Alpha, - blur_regions, - blur_clear_mode, - color.premultiplied(), - Some(cache_key), + 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/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index 2d323abacc..18f5c98332 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -1178,6 +1178,8 @@ impl PrimitiveStore { &metadata.local_rect, child_tasks, parent_tasks, + resource_cache, + gpu_cache, ); } PrimitiveKind::TextRun => { @@ -1513,7 +1515,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, @@ -1651,7 +1652,6 @@ impl PrimitiveStore { } let clip_task = RenderTask::new_mask( - None, combined_outer_rect, clips, prim_coordinate_system_id, diff --git a/webrender/src/render_task.rs b/webrender/src/render_task.rs index 8a7087288e..32a4322e6a 100644 --- a/webrender/src/render_task.rs +++ b/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}; @@ -160,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); } } @@ -180,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) { @@ -206,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)] @@ -248,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, } @@ -259,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)); - } } } @@ -277,7 +267,6 @@ pub enum RenderTaskKind { VerticalBlur(BlurTask), HorizontalBlur(BlurTask), Readback(DeviceIntRect), - Alias(RenderTaskId), Scaling(RenderTargetKind), } @@ -293,7 +282,6 @@ pub enum ClearMode { #[derive(Debug)] pub struct RenderTask { - pub cache_key: Option, pub location: RenderTaskLocation, pub children: Vec, pub kind: RenderTaskKind, @@ -310,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 { @@ -319,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 { @@ -339,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), @@ -349,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 { @@ -391,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(); @@ -414,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, }), @@ -441,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, }), @@ -458,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), @@ -537,8 +499,7 @@ impl RenderTask { ) } RenderTaskKind::Readback(..) | - RenderTaskKind::Scaling(..) | - RenderTaskKind::Alias(..) => { + RenderTaskKind::Scaling(..) => { ( [0.0; 3], [0.0; 4], @@ -570,6 +531,7 @@ impl RenderTask { match self.location { RenderTaskLocation::Fixed => DeviceIntSize::zero(), RenderTaskLocation::Dynamic(_, size) => size, + RenderTaskLocation::TextureCache(_, _, rect) => rect.size, } } @@ -598,6 +560,9 @@ impl RenderTask { RenderTaskLocation::Dynamic(None, _) => { (DeviceIntRect::zero(), RenderTargetIndex(0)) } + RenderTaskLocation::TextureCache(_, layer, rect) => { + (rect, RenderTargetIndex(layer as usize)) + } } } @@ -621,10 +586,6 @@ impl RenderTask { RenderTaskKind::Picture(ref task_info) => { task_info.target_kind } - - RenderTaskKind::Alias(..) => { - panic!("BUG: target_kind() called on invalidated task"); - } } } @@ -643,10 +604,6 @@ impl RenderTask { RenderTaskKind::Scaling(..) => false, RenderTaskKind::CacheMask(..) => true, - - RenderTaskKind::Alias(..) => { - panic!("BUG: is_shared() called on aliased task"); - } } } @@ -677,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)); @@ -695,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/webrender/src/renderer.rs b/webrender/src/renderer.rs index 55efc609dc..0a5621947f 100644 --- a/webrender/src/renderer.rs +++ b/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; @@ -413,11 +413,6 @@ const DESC_BLUR: VertexDescriptor = VertexDescriptor { count: 1, kind: VertexAttributeKind::I32, }, - VertexAttribute { - name: "aBlurRegion", - count: 4, - kind: VertexAttributeKind::F32 - }, ], }; @@ -2585,6 +2580,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 +2604,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))) } } @@ -3963,6 +3972,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 @@ -4111,7 +4165,7 @@ impl Renderer { // 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); } @@ -4125,7 +4179,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); } @@ -4218,9 +4272,17 @@ impl Renderer { (None, None) } - RenderPassKind::OffScreen { ref mut alpha, ref mut color } => { + 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/webrender/src/resource_cache.rs b/webrender/src/resource_cache.rs index 2066656e26..a9cd0ab451 100644 --- a/webrender/src/resource_cache.rs +++ b/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/webrender/src/texture_cache.rs b/webrender/src/texture_cache.rs index 3e38ec921e..cc40e7e9d0 100644 --- a/webrender/src/texture_cache.rs +++ b/webrender/src/texture_cache.rs @@ -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. @@ -377,6 +379,14 @@ impl TextureCache { &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 diff --git a/webrender/src/tiling.rs b/webrender/src/tiling.rs index 1805f90e21..616092453c 100644 --- a/webrender/src/tiling.rs +++ b/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 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, @@ -298,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, @@ -425,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!"); } @@ -541,12 +528,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>, }, } @@ -558,7 +587,6 @@ pub enum RenderPassKind { pub struct RenderPass { pub kind: RenderPassKind, tasks: Vec, - dynamic_tasks: FastHashMap, } impl RenderPass { @@ -567,7 +595,6 @@ impl RenderPass { RenderPass { kind: RenderPassKind::MainFramebuffer(target), tasks: vec![], - dynamic_tasks: FastHashMap::default(), } } @@ -576,9 +603,9 @@ impl RenderPass { kind: RenderPassKind::OffScreen { color: RenderTargetList::new(screen_size, ImageFormat::BGRA8), alpha: RenderTargetList::new(screen_size, ImageFormat::R8), + texture_cache: FastHashMap::default(), }, tasks: vec![], - dynamic_tasks: FastHashMap::default(), } } @@ -588,7 +615,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, @@ -620,10 +647,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(); @@ -631,22 +658,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), @@ -654,23 +675,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), + } + } } } @@ -742,18 +766,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/wrench/reftests/boxshadow/box-shadow-border-radii.png b/wrench/reftests/boxshadow/box-shadow-border-radii.png index 4d8f7218db..923112a6bd 100644 Binary files a/wrench/reftests/boxshadow/box-shadow-border-radii.png and b/wrench/reftests/boxshadow/box-shadow-border-radii.png differ diff --git a/wrench/reftests/boxshadow/box-shadow-cache.png b/wrench/reftests/boxshadow/box-shadow-cache.png index d1ce036215..af67e10bcc 100644 Binary files a/wrench/reftests/boxshadow/box-shadow-cache.png and b/wrench/reftests/boxshadow/box-shadow-cache.png differ diff --git a/wrench/reftests/boxshadow/inset-alpha.png b/wrench/reftests/boxshadow/inset-alpha.png index bfbc0bb568..d1c9440005 100644 Binary files a/wrench/reftests/boxshadow/inset-alpha.png and b/wrench/reftests/boxshadow/inset-alpha.png differ diff --git a/wrench/reftests/boxshadow/inset-downscale.png b/wrench/reftests/boxshadow/inset-downscale.png index 8f4f1d318c..8b7710831d 100644 Binary files a/wrench/reftests/boxshadow/inset-downscale.png and b/wrench/reftests/boxshadow/inset-downscale.png differ diff --git a/wrench/reftests/boxshadow/overlap1.png b/wrench/reftests/boxshadow/overlap1.png index e0518aceff..9b9089b7c5 100644 Binary files a/wrench/reftests/boxshadow/overlap1.png and b/wrench/reftests/boxshadow/overlap1.png differ diff --git a/wrench/reftests/transforms/prim-suite.png b/wrench/reftests/transforms/prim-suite.png index 8935673ac3..56a65eb9cd 100644 Binary files a/wrench/reftests/transforms/prim-suite.png and b/wrench/reftests/transforms/prim-suite.png differ