diff --git a/gfx/webrender/res/cs_clip_box_shadow.glsl b/gfx/webrender/res/cs_clip_box_shadow.glsl index b659e4e83ac9..beae9406bdc6 100644 --- a/gfx/webrender/res/cs_clip_box_shadow.glsl +++ b/gfx/webrender/res/cs_clip_box_shadow.glsl @@ -11,7 +11,6 @@ flat varying float vLayer; flat varying vec4 vEdge; flat varying vec4 vUvBounds_NoClamp; flat varying float vClipMode; -flat varying int vStretchMode; #define MODE_STRETCH 0 #define MODE_SIMPLE 1 @@ -21,14 +20,21 @@ flat varying int vStretchMode; struct BoxShadowData { vec2 src_rect_size; float clip_mode; - int stretch_mode; + int stretch_mode_x; + int stretch_mode_y; RectWithSize dest_rect; }; BoxShadowData fetch_data(ivec2 address) { - vec4 data[2] = fetch_from_resource_cache_2_direct(address); - RectWithSize dest_rect = RectWithSize(data[1].xy, data[1].zw); - BoxShadowData bs_data = BoxShadowData(data[0].xy, data[0].z, int(data[0].w), dest_rect); + vec4 data[3] = fetch_from_resource_cache_3_direct(address); + RectWithSize dest_rect = RectWithSize(data[2].xy, data[2].zw); + BoxShadowData bs_data = BoxShadowData( + data[0].xy, + data[0].z, + int(data[1].x), + int(data[1].y), + dest_rect + ); return bs_data; } @@ -46,7 +52,6 @@ void main(void) { vLayer = res.layer; vPos = vi.local_pos; vClipMode = bs_data.clip_mode; - vStretchMode = bs_data.stretch_mode; vec2 uv0 = res.uv_rect.p0; vec2 uv1 = res.uv_rect.p1; @@ -54,17 +59,32 @@ void main(void) { vec2 texture_size = vec2(textureSize(sColor0, 0)); vec2 local_pos = vPos.xy / vPos.z; - switch (bs_data.stretch_mode) { + switch (bs_data.stretch_mode_x) { case MODE_STRETCH: { - vEdge.xy = vec2(0.5); - vEdge.zw = (bs_data.dest_rect.size / bs_data.src_rect_size) - vec2(0.5); - vUv = (local_pos - bs_data.dest_rect.p0) / bs_data.src_rect_size; + vEdge.x = 0.5; + vEdge.z = (bs_data.dest_rect.size.x / bs_data.src_rect_size.x) - 0.5; + vUv.x = (local_pos.x - bs_data.dest_rect.p0.x) / bs_data.src_rect_size.x; break; } case MODE_SIMPLE: default: { - vec2 f = (local_pos - bs_data.dest_rect.p0) / bs_data.dest_rect.size; - vUv = mix(uv0, uv1, f) / texture_size; + vEdge.xz = vec2(1.0); + vUv.x = (local_pos.x - bs_data.dest_rect.p0.x) / bs_data.dest_rect.size.x; + break; + } + } + + switch (bs_data.stretch_mode_y) { + case MODE_STRETCH: { + vEdge.y = 0.5; + vEdge.w = (bs_data.dest_rect.size.y / bs_data.src_rect_size.y) - 0.5; + vUv.y = (local_pos.y - bs_data.dest_rect.p0.y) / bs_data.src_rect_size.y; + break; + } + case MODE_SIMPLE: + default: { + vEdge.yw = vec2(1.0); + vUv.y = (local_pos.y - bs_data.dest_rect.p0.y) / bs_data.dest_rect.size.y; break; } } @@ -77,22 +97,10 @@ void main(void) { #ifdef WR_FRAGMENT_SHADER void main(void) { vec2 local_pos = vPos.xy / vPos.z; - vec2 uv; - - switch (vStretchMode) { - case MODE_STRETCH: { - uv = clamp(vUv.xy, vec2(0.0), vEdge.xy); - uv += max(vec2(0.0), vUv.xy - vEdge.zw); - uv = mix(vUvBounds_NoClamp.xy, vUvBounds_NoClamp.zw, uv); - break; - } - case MODE_SIMPLE: - default: { - uv = vUv.xy; - break; - } - } + vec2 uv = clamp(vUv.xy, vec2(0.0), vEdge.xy); + uv += max(vec2(0.0), vUv.xy - vEdge.zw); + uv = mix(vUvBounds_NoClamp.xy, vUvBounds_NoClamp.zw, uv); uv = clamp(uv, vUvBounds.xy, vUvBounds.zw); float in_shadow_rect = point_inside_rect( diff --git a/gfx/webrender/res/brush_line.glsl b/gfx/webrender/res/cs_clip_line.glsl similarity index 81% rename from gfx/webrender/res/brush_line.glsl rename to gfx/webrender/res/cs_clip_line.glsl index fbd66f41dfd1..fdccfb271425 100644 --- a/gfx/webrender/res/brush_line.glsl +++ b/gfx/webrender/res/cs_clip_line.glsl @@ -2,13 +2,10 @@ * 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 2 +#include shared,prim_shared,clip_shared -#include shared,prim_shared,brush +varying vec3 vLocalPos; -varying vec2 vLocalPos; - -flat varying vec4 vColor; flat varying int vStyle; flat varying float vAxisSelect; flat varying vec4 vParams; @@ -19,51 +16,50 @@ flat varying vec2 vLocalOrigin; #define LINE_ORIENTATION_VERTICAL 0 #define LINE_ORIENTATION_HORIZONTAL 1 -struct Line { - vec4 color; +struct LineDecorationData { + RectWithSize local_rect; float wavyLineThickness; float style; float orientation; }; -Line fetch_line(int address) { - vec4 data[2] = fetch_from_resource_cache_2(address); - return Line(data[0], data[1].x, data[1].y, data[1].z); +LineDecorationData fetch_data(ivec2 address) { + vec4 data[2] = fetch_from_resource_cache_2_direct(address); + RectWithSize local_rect = RectWithSize(data[0].xy, data[0].zw); + LineDecorationData line_data = LineDecorationData( + local_rect, + data[1].x, + data[1].y, + data[1].z + ); + return line_data; } -void brush_vs( - VertexInfo vi, - int prim_address, - RectWithSize local_rect, - ivec3 user_data, - PictureTask pic_task -) { +void main(void) { + ClipMaskInstance cmi = fetch_clip_item(); + ClipArea area = fetch_clip_area(cmi.render_task_address); + ClipScrollNode scroll_node = fetch_clip_scroll_node(cmi.scroll_node_id); + LineDecorationData data = fetch_data(cmi.clip_data_address); + + ClipVertexInfo vi = write_clip_tile_vertex(data.local_rect, + scroll_node, + area); + + vLocalPos = vi.local_pos; - // Note: `line` name is reserved in HLSL - Line line_prim = fetch_line(prim_address); - - switch (int(abs(pic_task.pic_kind_and_raster_mode))) { - case PIC_TYPE_TEXT_SHADOW: - vColor = pic_task.color; - break; - default: - vColor = line_prim.color; - break; - } - vec2 pos, size; - switch (int(line_prim.orientation)) { + switch (int(data.orientation)) { case LINE_ORIENTATION_HORIZONTAL: vAxisSelect = 0.0; - pos = local_rect.p0; - size = local_rect.size; + pos = data.local_rect.p0; + size = data.local_rect.size; break; case LINE_ORIENTATION_VERTICAL: vAxisSelect = 1.0; - pos = local_rect.p0.yx; - size = local_rect.size.yx; + pos = data.local_rect.p0.yx; + size = data.local_rect.size.yx; break; default: vAxisSelect = 0.0; @@ -71,7 +67,7 @@ void brush_vs( } vLocalOrigin = pos; - vStyle = int(line_prim.style); + vStyle = int(data.style); switch (vStyle) { case LINE_STYLE_SOLID: { @@ -98,7 +94,7 @@ void brush_vs( } case LINE_STYLE_WAVY: { // This logic copied from gecko to get the same results - float line_thickness = max(line_prim.wavyLineThickness, 1.0); + float line_thickness = max(data.wavyLineThickness, 1.0); // Difference in height between peaks and troughs // (and since slopes are 45 degrees, the length of each slope) float slope_length = size.y - line_thickness; @@ -121,9 +117,9 @@ void brush_vs( #define MAGIC_WAVY_LINE_AA_SNAP 0.5 -vec4 brush_fs() { +void main(void) { // Find the appropriate distance to apply the step over. - vec2 local_pos = vLocalPos; + vec2 local_pos = vLocalPos.xy / vLocalPos.z; float aa_range = compute_aa_range(local_pos); float alpha = 1.0; @@ -200,6 +196,6 @@ vec4 brush_fs() { default: break; } - return vColor * alpha; + oFragColor = vec4(alpha); } #endif diff --git a/gfx/webrender/res/prim_shared.glsl b/gfx/webrender/res/prim_shared.glsl index adc36b018a17..b64cd05b662d 100644 --- a/gfx/webrender/res/prim_shared.glsl +++ b/gfx/webrender/res/prim_shared.glsl @@ -76,7 +76,7 @@ vec4[2] fetch_from_resource_cache_2(int address) { #define VECS_PER_CLIP_SCROLL_NODE 9 #define VECS_PER_LOCAL_CLIP_RECT 1 -#define VECS_PER_RENDER_TASK 3 +#define VECS_PER_RENDER_TASK 2 #define VECS_PER_PRIM_HEADER 2 #define VECS_PER_TEXT_RUN 3 #define VECS_PER_GRADIENT_STOP 2 @@ -203,7 +203,6 @@ struct RenderTaskCommonData { struct RenderTaskData { RenderTaskCommonData common_data; vec3 data1; - vec4 data2; }; RenderTaskData fetch_render_task_data(int index) { @@ -211,7 +210,6 @@ RenderTaskData fetch_render_task_data(int index) { vec4 texel0 = TEXEL_FETCH(sRenderTasks, uv, 0, ivec2(0, 0)); vec4 texel1 = TEXEL_FETCH(sRenderTasks, uv, 0, ivec2(1, 0)); - vec4 texel2 = TEXEL_FETCH(sRenderTasks, uv, 0, ivec2(2, 0)); RectWithSize task_rect = RectWithSize( texel0.xy, @@ -225,8 +223,7 @@ RenderTaskData fetch_render_task_data(int index) { RenderTaskData data = RenderTaskData( common_data, - texel1.yzw, - texel2 + texel1.yzw ); return data; @@ -262,8 +259,6 @@ RenderTaskCommonData fetch_render_task_common_data(int index) { struct PictureTask { RenderTaskCommonData common_data; vec2 content_origin; - float pic_kind_and_raster_mode; - vec4 color; }; PictureTask fetch_picture_task(int address) { @@ -271,9 +266,7 @@ PictureTask fetch_picture_task(int address) { PictureTask task = PictureTask( task_data.common_data, - task_data.data1.xy, - task_data.data1.z, - task_data.data2 + task_data.data1.xy ); return task; diff --git a/gfx/webrender/src/batch.rs b/gfx/webrender/src/batch.rs index 910de4899617..260bd9621dd9 100644 --- a/gfx/webrender/src/batch.rs +++ b/gfx/webrender/src/batch.rs @@ -12,11 +12,11 @@ use clip_scroll_tree::{CoordinateSystemId}; use euclid::{TypedTransform3D, vec3}; use glyph_rasterizer::GlyphFormat; use gpu_cache::{GpuCache, GpuCacheAddress}; -use gpu_types::{BrushFlags, BrushInstance, ClipChainRectIndex}; +use gpu_types::{BrushFlags, BrushInstance, ClipChainRectIndex, ZBufferId, ZBufferIdGenerator}; use gpu_types::{ClipMaskInstance, ClipScrollNodeIndex, RasterizationSpace}; use gpu_types::{CompositePrimitiveInstance, PrimitiveInstance, SimplePrimitiveInstance}; use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture}; -use picture::{PictureCompositeMode, PictureKind, PicturePrimitive}; +use picture::{PictureCompositeMode, PicturePrimitive}; use plane_split::{BspSplitter, Polygon, Splitter}; use prim_store::{CachedGradient, ImageSource, PrimitiveIndex, PrimitiveKind, PrimitiveMetadata, PrimitiveStore}; use prim_store::{BrushPrimitive, BrushKind, DeferredResolve, EdgeAaSegmentMask, PictureIndex, PrimitiveRun}; @@ -56,7 +56,6 @@ pub enum BrushImageSourceKind { #[cfg_attr(feature = "replay", derive(Deserialize))] pub enum BrushBatchKind { Solid, - Line, Image(ImageBufferKind), Blend, MixBlend { @@ -457,6 +456,7 @@ impl AlphaBatchBuilder { gpu_cache: &mut GpuCache, render_tasks: &RenderTaskTree, deferred_resolves: &mut Vec, + z_generator: &mut ZBufferIdGenerator, ) { let task_address = render_tasks.get_task_address(task_id); @@ -489,6 +489,7 @@ impl AlphaBatchBuilder { deferred_resolves, &mut splitter, content_origin, + z_generator, ); } @@ -524,7 +525,7 @@ impl AlphaBatchBuilder { RenderTaskAddress(0), gpu_address, 0, - prim_index.0 as i32, + z_generator.next(), 0, 0, ); @@ -548,6 +549,7 @@ impl AlphaBatchBuilder { deferred_resolves: &mut Vec, splitter: &mut BspSplitter, content_origin: DeviceIntPoint, + z_generator: &mut ZBufferIdGenerator, ) { for i in 0 .. run.count { let prim_index = PrimitiveIndex(run.base_prim_index.0 + i); @@ -566,6 +568,7 @@ impl AlphaBatchBuilder { deferred_resolves, splitter, content_origin, + z_generator, ); } } @@ -588,8 +591,9 @@ impl AlphaBatchBuilder { deferred_resolves: &mut Vec, splitter: &mut BspSplitter, content_origin: DeviceIntPoint, + z_generator: &mut ZBufferIdGenerator, ) { - let z = prim_index.0 as i32; + let z = z_generator.next(); let prim_metadata = ctx.prim_store.get_metadata(prim_index); let scroll_node = &ctx.node_data[scroll_id.0 as usize]; // TODO(gw): Calculating this for every primitive is a bit @@ -644,281 +648,39 @@ impl AlphaBatchBuilder { 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(ImageBufferKind::Texture2DArray) - ); - let key = BatchKey::new(kind, non_segmented_blend_mode, textures); - let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect); + // If this picture is participating in a 3D rendering context, + // then don't add it to any batches here. Instead, create a polygon + // for it and add it to the current plane splitter. + if picture.is_in_3d_context { + // Push into parent plane splitter. - let uv_rect_address = render_tasks[cache_task_id] - .get_texture_handle() - .as_int(gpu_cache); + let real_xf = &ctx.clip_scroll_tree + .nodes[picture.reference_frame_index.0] + .world_content_transform + .into(); + let polygon = make_polygon( + picture.real_local_rect, + &real_xf, + prim_index.0, + ); - 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(), - brush_flags: BrushFlags::empty(), - user_data: [ - uv_rect_address, - BrushImageSourceKind::Color as i32, - RasterizationSpace::Screen as i32, - ], - }; - batch.push(PrimitiveInstance::from(instance)); - } - PictureKind::Image { - composite_mode, - secondary_render_task_id, - is_in_3d_context, - reference_frame_index, - real_local_rect, - ref extra_gpu_data_handle, - .. - } => { - // If this picture is participating in a 3D rendering context, - // then don't add it to any batches here. Instead, create a polygon - // for it and add it to the current plane splitter. - if is_in_3d_context { - // Push into parent plane splitter. + splitter.add(polygon); - let real_xf = &ctx.clip_scroll_tree - .nodes[reference_frame_index.0] - .world_content_transform - .into(); - let polygon = make_polygon( - real_local_rect, - &real_xf, - prim_index.0, - ); + return; + } - splitter.add(polygon); + // Depending on the composite mode of the picture, we generate the + // old style Composite primitive instances. In the future, we'll + // remove these and pass them through the brush batching pipeline. + // 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 = cache_task_id; - return; - } - - // Depending on the composite mode of the picture, we generate the - // old style Composite primitive instances. In the future, we'll - // remove these and pass them through the brush batching pipeline. - // 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 = cache_task_id; - - match composite_mode.expect("bug: only composites here") { - PictureCompositeMode::Filter(filter) => { - match filter { - FilterOp::Blur(..) => { - let kind = BatchKind::Brush( - BrushBatchKind::Image(ImageBufferKind::Texture2DArray) - ); - let key = BatchKey::new(kind, non_segmented_blend_mode, textures); - let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect); - - let uv_rect_address = render_tasks[cache_task_id] - .get_texture_handle() - .as_int(gpu_cache); - - 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(), - brush_flags: BrushFlags::empty(), - user_data: [ - uv_rect_address, - BrushImageSourceKind::Color as i32, - RasterizationSpace::Screen as i32, - ], - }; - batch.push(PrimitiveInstance::from(instance)); - } - FilterOp::DropShadow(offset, _, _) => { - let kind = BatchKind::Brush( - BrushBatchKind::Image(ImageBufferKind::Texture2DArray), - ); - let key = BatchKey::new(kind, non_segmented_blend_mode, textures); - - let uv_rect_address = render_tasks[cache_task_id] - .get_texture_handle() - .as_int(gpu_cache); - - 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(), - brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION, - user_data: [ - uv_rect_address, - BrushImageSourceKind::ColorAlphaMask as i32, - // TODO(gw): This is totally wrong, but the drop-shadow code itself - // is completely wrong, and doesn't work correctly with - // transformed Picture sources. I'm leaving this as is for - // now, and will fix drop-shadows properly, as a follow up. - RasterizationSpace::Local as i32, - ], - }; - - { - let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect); - batch.push(PrimitiveInstance::from(instance)); - } - - let secondary_id = secondary_render_task_id.expect("no secondary!?"); - let saved_index = render_tasks[secondary_id].saved_index.expect("no saved index!?"); - debug_assert_ne!(saved_index, SavedTargetIndex::PENDING); - let secondary_task_address = render_tasks.get_task_address(secondary_id); - let secondary_textures = BatchTextures { - colors: [ - SourceTexture::RenderTaskCache(saved_index), - SourceTexture::Invalid, - SourceTexture::Invalid, - ], - }; - let key = BatchKey::new( - BatchKind::HardwareComposite, - BlendMode::PremultipliedAlpha, - secondary_textures, - ); - let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect); - let content_rect = prim_metadata.local_rect.translate(&-offset); - let rect = - (content_rect * LayerToWorldScale::new(1.0) * ctx.device_pixel_scale).round() - .to_i32(); - - let instance = CompositePrimitiveInstance::new( - task_address, - secondary_task_address, - RenderTaskAddress(0), - rect.origin.x, - rect.origin.y, - z, - rect.size.width, - rect.size.height, - ); - - batch.push(PrimitiveInstance::from(instance)); - } - _ => { - let key = BatchKey::new( - BatchKind::Brush(BrushBatchKind::Blend), - BlendMode::PremultipliedAlpha, - BatchTextures::render_target_cache(), - ); - - let filter_mode = match filter { - FilterOp::Blur(..) => 0, - FilterOp::Contrast(..) => 1, - FilterOp::Grayscale(..) => 2, - FilterOp::HueRotate(..) => 3, - FilterOp::Invert(..) => 4, - FilterOp::Saturate(..) => 5, - FilterOp::Sepia(..) => 6, - FilterOp::Brightness(..) => 7, - FilterOp::Opacity(..) => 8, - FilterOp::DropShadow(..) => 9, - FilterOp::ColorMatrix(..) => 10, - }; - - let user_data = match filter { - FilterOp::Contrast(amount) | - FilterOp::Grayscale(amount) | - FilterOp::Invert(amount) | - FilterOp::Saturate(amount) | - FilterOp::Sepia(amount) | - FilterOp::Brightness(amount) | - FilterOp::Opacity(_, amount) => { - (amount * 65536.0) as i32 - } - FilterOp::HueRotate(angle) => { - (0.01745329251 * angle * 65536.0) as i32 - } - // Go through different paths - FilterOp::Blur(..) | - FilterOp::DropShadow(..) => { - unreachable!(); - } - FilterOp::ColorMatrix(_) => { - extra_gpu_data_handle.as_int(gpu_cache) - } - }; - - 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(), - brush_flags: BrushFlags::empty(), - user_data: [ - cache_task_address.0 as i32, - filter_mode, - user_data, - ], - }; - - let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect); - batch.push(PrimitiveInstance::from(instance)); - } - } - } - PictureCompositeMode::MixBlend(mode) => { - let backdrop_id = secondary_render_task_id.expect("no backdrop!?"); - - let key = BatchKey::new( - BatchKind::Brush( - BrushBatchKind::MixBlend { - task_id, - source_id, - backdrop_id, - }, - ), - BlendMode::PremultipliedAlpha, - BatchTextures::no_texture(), - ); - let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect); - let backdrop_task_address = render_tasks.get_task_address(backdrop_id); - let source_task_address = render_tasks.get_task_address(source_id); - - 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(), - brush_flags: BrushFlags::empty(), - user_data: [ - mode as u32 as i32, - backdrop_task_address.0 as i32, - source_task_address.0 as i32, - ], - }; - - batch.push(PrimitiveInstance::from(instance)); - } - PictureCompositeMode::Blit => { + match picture.composite_mode.expect("bug: only composites here") { + PictureCompositeMode::Filter(filter) => { + match filter { + FilterOp::Blur(..) => { let kind = BatchKind::Brush( BrushBatchKind::Image(ImageBufferKind::Texture2DArray) ); @@ -947,8 +709,209 @@ impl AlphaBatchBuilder { }; batch.push(PrimitiveInstance::from(instance)); } + FilterOp::DropShadow(offset, _, _) => { + let kind = BatchKind::Brush( + BrushBatchKind::Image(ImageBufferKind::Texture2DArray), + ); + let key = BatchKey::new(kind, non_segmented_blend_mode, textures); + + let uv_rect_address = render_tasks[cache_task_id] + .get_texture_handle() + .as_int(gpu_cache); + + 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(), + brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION, + user_data: [ + uv_rect_address, + BrushImageSourceKind::ColorAlphaMask as i32, + // TODO(gw): This is totally wrong, but the drop-shadow code itself + // is completely wrong, and doesn't work correctly with + // transformed Picture sources. I'm leaving this as is for + // now, and will fix drop-shadows properly, as a follow up. + RasterizationSpace::Local as i32, + ], + }; + + { + let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect); + batch.push(PrimitiveInstance::from(instance)); + } + + let secondary_id = picture.secondary_render_task_id.expect("no secondary!?"); + let saved_index = render_tasks[secondary_id].saved_index.expect("no saved index!?"); + debug_assert_ne!(saved_index, SavedTargetIndex::PENDING); + let secondary_task_address = render_tasks.get_task_address(secondary_id); + let secondary_textures = BatchTextures { + colors: [ + SourceTexture::RenderTaskCache(saved_index), + SourceTexture::Invalid, + SourceTexture::Invalid, + ], + }; + let key = BatchKey::new( + BatchKind::HardwareComposite, + BlendMode::PremultipliedAlpha, + secondary_textures, + ); + let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect); + let content_rect = prim_metadata.local_rect.translate(&-offset); + let rect = + (content_rect * LayerToWorldScale::new(1.0) * ctx.device_pixel_scale).round() + .to_i32(); + + let instance = CompositePrimitiveInstance::new( + task_address, + secondary_task_address, + RenderTaskAddress(0), + rect.origin.x, + rect.origin.y, + z, + rect.size.width, + rect.size.height, + ); + + batch.push(PrimitiveInstance::from(instance)); + } + _ => { + let key = BatchKey::new( + BatchKind::Brush(BrushBatchKind::Blend), + BlendMode::PremultipliedAlpha, + BatchTextures::render_target_cache(), + ); + + let filter_mode = match filter { + FilterOp::Blur(..) => 0, + FilterOp::Contrast(..) => 1, + FilterOp::Grayscale(..) => 2, + FilterOp::HueRotate(..) => 3, + FilterOp::Invert(..) => 4, + FilterOp::Saturate(..) => 5, + FilterOp::Sepia(..) => 6, + FilterOp::Brightness(..) => 7, + FilterOp::Opacity(..) => 8, + FilterOp::DropShadow(..) => 9, + FilterOp::ColorMatrix(..) => 10, + }; + + let user_data = match filter { + FilterOp::Contrast(amount) | + FilterOp::Grayscale(amount) | + FilterOp::Invert(amount) | + FilterOp::Saturate(amount) | + FilterOp::Sepia(amount) | + FilterOp::Brightness(amount) | + FilterOp::Opacity(_, amount) => { + (amount * 65536.0) as i32 + } + FilterOp::HueRotate(angle) => { + (0.01745329251 * angle * 65536.0) as i32 + } + // Go through different paths + FilterOp::Blur(..) | + FilterOp::DropShadow(..) => { + unreachable!(); + } + FilterOp::ColorMatrix(_) => { + picture.extra_gpu_data_handle.as_int(gpu_cache) + } + }; + + 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(), + brush_flags: BrushFlags::empty(), + user_data: [ + cache_task_address.0 as i32, + filter_mode, + user_data, + ], + }; + + let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect); + batch.push(PrimitiveInstance::from(instance)); + } } } + PictureCompositeMode::MixBlend(mode) => { + let backdrop_id = picture.secondary_render_task_id.expect("no backdrop!?"); + + let key = BatchKey::new( + BatchKind::Brush( + BrushBatchKind::MixBlend { + task_id, + source_id, + backdrop_id, + }, + ), + BlendMode::PremultipliedAlpha, + BatchTextures::no_texture(), + ); + let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect); + let backdrop_task_address = render_tasks.get_task_address(backdrop_id); + let source_task_address = render_tasks.get_task_address(source_id); + + 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(), + brush_flags: BrushFlags::empty(), + user_data: [ + mode as u32 as i32, + backdrop_task_address.0 as i32, + source_task_address.0 as i32, + ], + }; + + batch.push(PrimitiveInstance::from(instance)); + } + PictureCompositeMode::Blit => { + let kind = BatchKind::Brush( + BrushBatchKind::Image(ImageBufferKind::Texture2DArray) + ); + let key = BatchKey::new(kind, non_segmented_blend_mode, textures); + let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect); + + let uv_rect_address = render_tasks[cache_task_id] + .get_texture_handle() + .as_int(gpu_cache); + + 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(), + brush_flags: BrushFlags::empty(), + user_data: [ + uv_rect_address, + BrushImageSourceKind::Color as i32, + RasterizationSpace::Screen as i32, + ], + }; + batch.push(PrimitiveInstance::from(instance)); + } } } None => { @@ -961,6 +924,7 @@ impl AlphaBatchBuilder { gpu_cache, render_tasks, deferred_resolves, + z_generator, ); } } @@ -1181,7 +1145,7 @@ impl AlphaBatchBuilder { scroll_id: ClipScrollNodeIndex, task_address: RenderTaskAddress, transform_kind: TransformedRectKind, - z: i32, + z: ZBufferId, render_tasks: &RenderTaskTree, user_data: [i32; 3], ) { @@ -1281,13 +1245,6 @@ impl BrushPrimitive { cached_gradients: &[CachedGradient], ) -> Option<(BrushBatchKind, BatchTextures, [i32; 3])> { match self.kind { - BrushKind::Line { .. } => { - Some(( - BrushBatchKind::Line, - BatchTextures::no_texture(), - [0; 3], - )) - } BrushKind::Image { request, .. } => { let cache_item = resolve_image( request, @@ -1440,7 +1397,6 @@ impl AlphaBatchHelpers for PrimitiveStore { } } BrushKind::Solid { .. } | - BrushKind::Line { .. } | BrushKind::YuvImage { .. } | BrushKind::RadialGradient { .. } | BrushKind::LinearGradient { .. } | @@ -1552,6 +1508,7 @@ pub struct ClipBatcher { pub border_clears: Vec, pub borders: Vec, pub box_shadows: FastHashMap>, + pub line_decorations: Vec, } impl ClipBatcher { @@ -1562,6 +1519,7 @@ impl ClipBatcher { border_clears: Vec::new(), borders: Vec::new(), box_shadows: FastHashMap::default(), + line_decorations: Vec::new(), } } @@ -1629,6 +1587,12 @@ impl ClipBatcher { continue; } } + ClipSource::LineDecoration(..) => { + self.line_decorations.push(ClipMaskInstance { + clip_data_address: gpu_address, + ..instance + }); + } ClipSource::BoxShadow(ref info) => { debug_assert_ne!(info.cache_item.texture_id, SourceTexture::Invalid); diff --git a/gfx/webrender/src/box_shadow.rs b/gfx/webrender/src/box_shadow.rs index 3d8a4b15537e..4ccfd5e39b02 100644 --- a/gfx/webrender/src/box_shadow.rs +++ b/gfx/webrender/src/box_shadow.rs @@ -19,7 +19,8 @@ pub struct BoxShadowClipSource { pub shadow_radius: BorderRadius, pub blur_radius: f32, pub clip_mode: BoxShadowClipMode, - pub stretch_mode: BoxShadowStretchMode, + pub stretch_mode_x: BoxShadowStretchMode, + pub stretch_mode_y: BoxShadowStretchMode, // The current cache key (in device-pixels), and handles // to the cached clip region and blurred texture. diff --git a/gfx/webrender/src/clip.rs b/gfx/webrender/src/clip.rs index e8e0209c891f..215fdbb20c09 100644 --- a/gfx/webrender/src/clip.rs +++ b/gfx/webrender/src/clip.rs @@ -4,7 +4,7 @@ use api::{BorderRadius, ClipMode, ComplexClipRegion, DeviceIntRect, DevicePixelScale, ImageMask}; use api::{ImageRendering, LayerRect, LayerSize, LayoutPoint, LayoutVector2D, LocalClip}; -use api::{BoxShadowClipMode, LayerPoint, LayerToWorldScale}; +use api::{BoxShadowClipMode, LayerPoint, LayerToWorldScale, LineOrientation, LineStyle}; use border::{BorderCornerClipSource, ensure_no_corner_overlap}; use box_shadow::{BLUR_SAMPLE_SCALE, BoxShadowClipSource, BoxShadowCacheKey}; use clip_scroll_tree::{ClipChainIndex, CoordinateSystemId}; @@ -16,13 +16,21 @@ use prim_store::{ClipData, ImageMaskData}; use render_task::to_cache_size; use resource_cache::{CacheItem, ImageRequest, ResourceCache}; use util::{LayerToWorldFastTransform, MaxRect, calculate_screen_bounding_rect}; -use util::extract_inner_rect_safe; +use util::{extract_inner_rect_safe, pack_as_float}; use std::sync::Arc; pub type ClipStore = FreeList; pub type ClipSourcesHandle = FreeListHandle; pub type ClipSourcesWeakHandle = WeakFreeListHandle; +#[derive(Debug)] +pub struct LineDecorationClipSource { + rect: LayerRect, + style: LineStyle, + orientation: LineOrientation, + wavy_line_thickness: f32, +} + #[derive(Clone, Debug)] pub struct ClipRegion { pub main: LayerRect, @@ -82,6 +90,7 @@ pub enum ClipSource { /// and different styles per edge. BorderCorner(BorderCornerClipSource), BoxShadow(BoxShadowClipSource), + LineDecoration(LineDecorationClipSource), } impl From for ClipSources { @@ -120,6 +129,22 @@ impl ClipSource { ) } + pub fn new_line_decoration( + rect: LayerRect, + style: LineStyle, + orientation: LineOrientation, + wavy_line_thickness: f32, + ) -> ClipSource { + ClipSource::LineDecoration( + LineDecorationClipSource { + rect, + style, + orientation, + wavy_line_thickness, + } + ) + } + pub fn new_box_shadow( shadow_rect: LayerRect, shadow_radius: BorderRadius, @@ -178,14 +203,20 @@ impl ClipSource { ); // If the width or height ends up being bigger than the original - // primitive shadow rect, just blur the entire rect and draw that - // as a simple blit. This is necessary for correctness, since the - // blur of one corner may affect the blur in another corner. - let mut stretch_mode = BoxShadowStretchMode::Stretch; - if shadow_rect.size.width < minimal_shadow_rect.size.width || - shadow_rect.size.height < minimal_shadow_rect.size.height { - minimal_shadow_rect.size = shadow_rect.size; - stretch_mode = BoxShadowStretchMode::Simple; + // primitive shadow rect, just blur the entire rect along that + // axis and draw that as a simple blit. This is necessary for + // correctness, since the blur of one corner may affect the blur + // in another corner. + let mut stretch_mode_x = BoxShadowStretchMode::Stretch; + if shadow_rect.size.width < minimal_shadow_rect.size.width { + minimal_shadow_rect.size.width = shadow_rect.size.width; + stretch_mode_x = BoxShadowStretchMode::Simple; + } + + let mut stretch_mode_y = BoxShadowStretchMode::Stretch; + if shadow_rect.size.height < minimal_shadow_rect.size.height { + minimal_shadow_rect.size.height = shadow_rect.size.height; + stretch_mode_y = BoxShadowStretchMode::Simple; } // Expand the shadow rect by enough room for the blur to take effect. @@ -200,13 +231,30 @@ impl ClipSource { prim_shadow_rect, blur_radius, clip_mode, - stretch_mode, + stretch_mode_x, + stretch_mode_y, cache_item: CacheItem::invalid(), cache_key: None, clip_data_handle: GpuCacheHandle::new(), minimal_shadow_rect, }) } + + // Return a modified clip source that is the same as self + // but offset in local-space by a specified amount. + pub fn offset(&self, offset: &LayoutVector2D) -> ClipSource { + match *self { + ClipSource::LineDecoration(ref info) => { + ClipSource::LineDecoration(LineDecorationClipSource { + rect: info.rect.translate(offset), + ..*info + }) + } + _ => { + panic!("bug: other clip sources not expected here yet"); + } + } + } } #[derive(Debug)] @@ -279,7 +327,8 @@ impl ClipSources { .and_then(|r| inner_rect.and_then(|ref inner| r.intersection(inner))); } ClipSource::BoxShadow(..) | - ClipSource::BorderCorner { .. } => { + ClipSource::BorderCorner { .. } | + ClipSource::LineDecoration(..) => { can_calculate_inner_rect = false; break; } @@ -317,7 +366,13 @@ impl ClipSources { info.shadow_rect_alloc_size.width, info.shadow_rect_alloc_size.height, info.clip_mode as i32 as f32, - info.stretch_mode as i32 as f32, + 0.0, + ]); + request.push([ + info.stretch_mode_x as i32 as f32, + info.stretch_mode_y as i32 as f32, + 0.0, + 0.0, ]); request.push(info.prim_shadow_rect); } @@ -332,6 +387,15 @@ impl ClipSources { ClipSource::BorderCorner(ref mut source) => { source.write(request); } + ClipSource::LineDecoration(ref info) => { + request.push(info.rect); + request.push([ + info.wavy_line_thickness, + pack_as_float(info.style as u32), + pack_as_float(info.orientation as u32), + 0.0, + ]); + } } } diff --git a/gfx/webrender/src/display_list_flattener.rs b/gfx/webrender/src/display_list_flattener.rs index c3fbdd2c52c2..33b0746c7b53 100644 --- a/gfx/webrender/src/display_list_flattener.rs +++ b/gfx/webrender/src/display_list_flattener.rs @@ -15,7 +15,6 @@ use api::{SpecificDisplayItem, StackingContext, StickyFrameDisplayItem, TexelRec use api::{TransformStyle, YuvColorSpace, YuvData}; use app_units::Au; use border::ImageBorderSegment; -use box_shadow::{BLUR_SAMPLE_SCALE}; use clip::{ClipRegion, ClipSource, ClipSources, ClipStore}; use clip_scroll_node::{ClipScrollNode, NodeType, StickyFrameInfo}; use clip_scroll_tree::{ClipChainIndex, ClipScrollNodeIndex, ClipScrollTree}; @@ -25,7 +24,7 @@ use glyph_rasterizer::FontInstance; use hit_test::{HitTestingItem, HitTestingRun}; use image::{decompose_image, TiledImageInfo}; use internal_types::{FastHashMap, FastHashSet}; -use picture::{PictureCompositeMode, PictureKind}; +use picture::PictureCompositeMode; use prim_store::{BrushKind, BrushPrimitive, BrushSegmentDescriptor, CachedGradient}; use prim_store::{CachedGradientIndex, ImageCacheKey, ImagePrimitiveCpu, ImageSource}; use prim_store::{PictureIndex, PrimitiveContainer, PrimitiveIndex, PrimitiveStore}; @@ -169,14 +168,6 @@ pub struct DisplayListFlattener<'a> { /// types that the ClipScrollTree uses. id_to_index_mapper: ClipIdToIndexMapper, - /// A stack of the current shadow primitives. The sub-Vec stores - /// a buffer of fast-path primitives to be appended on pop. - shadow_prim_stack: Vec<(PrimitiveIndex, Vec<(PrimitiveIndex, ScrollNodeAndClipChain)>)>, - - /// A buffer of "real" content when doing fast-path shadows. This is appended - /// when the shadow stack is empty. - pending_shadow_contents: Vec<(PrimitiveIndex, ScrollNodeAndClipChain, LayerPrimitiveInfo)>, - /// A stack of scroll nodes used during display list processing to properly /// parent new scroll nodes. reference_frame_stack: Vec<(ClipId, ClipScrollNodeIndex)>, @@ -187,6 +178,9 @@ pub struct DisplayListFlattener<'a> { /// A stack of the current pictures. picture_stack: Vec, + /// A stack of the currently active shadows + shadow_stack: Vec<(Shadow, PictureIndex)>, + /// A list of scrollbar primitives. pub scrollbar_prims: Vec, @@ -240,12 +234,11 @@ impl<'a> DisplayListFlattener<'a> { output_pipelines, id_to_index_mapper: ClipIdToIndexMapper::default(), hit_testing_runs: recycle_vec(old_builder.hit_testing_runs), - shadow_prim_stack: Vec::new(), cached_gradients: recycle_vec(old_builder.cached_gradients), - pending_shadow_contents: Vec::new(), scrollbar_prims: recycle_vec(old_builder.scrollbar_prims), reference_frame_stack: Vec::new(), picture_stack: Vec::new(), + shadow_stack: Vec::new(), sc_stack: Vec::new(), prim_store: old_builder.prim_store.recycle(), clip_store: old_builder.clip_store.recycle(), @@ -930,12 +923,42 @@ impl<'a> DisplayListFlattener<'a> { info: &LayerPrimitiveInfo, clip_sources: Vec, container: PrimitiveContainer, - ) -> PrimitiveIndex { - self.add_primitive_to_hit_testing_list(info, clip_and_scroll); - let prim_index = self.create_primitive(info, clip_sources, container); + ) { + if !self.shadow_stack.is_empty() { + // TODO(gw): Restructure this so we don't need to move the shadow + // stack out (borrowck due to create_primitive below). + let shadow_stack = mem::replace(&mut self.shadow_stack, Vec::new()); + for &(ref shadow, shadow_pic_index) in &shadow_stack { + // Offset the local rect and clip rect by the shadow offset. + let mut info = info.clone(); + info.rect = info.rect.translate(&shadow.offset); + info.clip_rect = info.clip_rect.translate(&shadow.offset); - self.add_primitive_to_draw_list(prim_index, clip_and_scroll); - prim_index + // Offset any local clip sources by the shadow offset. + let clip_sources: Vec = clip_sources + .iter() + .map(|cs| cs.offset(&shadow.offset)) + .collect(); + + // Construct and add a primitive for the given shadow. + let shadow_prim_index = self.create_primitive( + &info, + clip_sources, + container.create_shadow(shadow), + ); + + // Add the new primitive to the shadow picture. + let shadow_pic = &mut self.prim_store.pictures[shadow_pic_index.0]; + shadow_pic.add_primitive(shadow_prim_index, clip_and_scroll); + } + self.shadow_stack = shadow_stack; + } + + if container.is_visible() { + let prim_index = self.create_primitive(info, clip_sources, container); + self.add_primitive_to_hit_testing_list(info, clip_and_scroll); + self.add_primitive_to_draw_list(prim_index, clip_and_scroll); + } } pub fn push_stacking_context( @@ -973,6 +996,7 @@ impl<'a> DisplayListFlattener<'a> { pipeline_id, current_reference_frame_index, None, + true, ); self.picture_stack.push(pic_index); @@ -986,17 +1010,10 @@ impl<'a> DisplayListFlattener<'a> { let parent_pic_index = self.picture_stack.last().unwrap(); let parent_pic = &mut self.prim_store.pictures[parent_pic_index.0]; - match parent_pic.kind { - PictureKind::Image { ref mut composite_mode, .. } => { - // If not already isolated for some other reason, - // make this picture as isolated. - if composite_mode.is_none() { - *composite_mode = Some(PictureCompositeMode::Blit); - } - } - PictureKind::TextShadow { .. } => { - panic!("bug: text pictures invalid here"); - } + // If not already isolated for some other reason, + // make this picture as isolated. + if parent_pic.composite_mode.is_none() { + parent_pic.composite_mode = Some(PictureCompositeMode::Blit); } } @@ -1035,6 +1052,7 @@ impl<'a> DisplayListFlattener<'a> { pipeline_id, current_reference_frame_index, None, + true, ); let prim = BrushPrimitive::new_picture(container_index); @@ -1087,6 +1105,7 @@ impl<'a> DisplayListFlattener<'a> { pipeline_id, current_reference_frame_index, None, + true, ); let src_prim = BrushPrimitive::new_picture(src_pic_index); @@ -1118,6 +1137,7 @@ impl<'a> DisplayListFlattener<'a> { pipeline_id, current_reference_frame_index, None, + true, ); let src_prim = BrushPrimitive::new_picture(src_pic_index); @@ -1171,6 +1191,7 @@ impl<'a> DisplayListFlattener<'a> { pipeline_id, current_reference_frame_index, frame_output_pipeline_id, + true, ); // Create a brush primitive that draws this picture. @@ -1237,7 +1258,7 @@ impl<'a> DisplayListFlattener<'a> { } assert!( - self.shadow_prim_stack.is_empty(), + self.shadow_stack.is_empty(), "Found unpopped text shadows when popping stacking context!" ); } @@ -1381,44 +1402,47 @@ impl<'a> DisplayListFlattener<'a> { info: &LayerPrimitiveInfo, ) { let pipeline_id = self.sc_stack.last().unwrap().pipeline_id; - let pic_index = self.prim_store.add_shadow_picture(shadow, pipeline_id); + let current_reference_frame_index = self.current_reference_frame_index(); + let max_clip = LayerRect::max_rect(); - let prim = BrushPrimitive::new_picture(pic_index); + // 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 std_deviation = shadow.blur_radius * 0.5; - // Create an empty shadow primitive. Insert it into - // the draw lists immediately so that it will be drawn - // before any visual text elements that are added as - // part of this shadow context. - let prim_index = self.create_primitive( - info, - Vec::new(), - PrimitiveContainer::Brush(prim), + // Create a picture that the shadow primitives will be added to. If the + // blur radius is 0, the code in Picture::prepare_for_render will + // detect this and mark the picture to be drawn directly into the + // parent picture, which avoids an intermediate surface and blur. + let shadow_pic_index = self.prim_store.add_image_picture( + Some(PictureCompositeMode::Filter(FilterOp::Blur(std_deviation))), + false, + pipeline_id, + current_reference_frame_index, + None, + false, ); - let pending = vec![(prim_index, clip_and_scroll)]; - self.shadow_prim_stack.push((prim_index, pending)); + // Create the primitive to draw the shadow picture into the scene. + let shadow_prim = BrushPrimitive::new_picture(shadow_pic_index); + let shadow_prim_index = self.prim_store.add_primitive( + &LayerRect::zero(), + &max_clip, + info.is_backface_visible, + None, + None, + PrimitiveContainer::Brush(shadow_prim), + ); + + // Add the shadow primitive. This must be done before pushing this + // picture on to the shadow stack, to avoid infinite recursion! + self.add_primitive_to_draw_list(shadow_prim_index, clip_and_scroll); + self.shadow_stack.push((shadow, shadow_pic_index)); } pub fn pop_all_shadows(&mut self) { - assert!(self.shadow_prim_stack.len() > 0, "popped shadows, but none were present"); - - // Borrowcheck dance - let mut shadows = mem::replace(&mut self.shadow_prim_stack, Vec::new()); - for (_, pending_primitives) in shadows.drain(..) { - // Push any fast-path shadows now - for (prim_index, clip_and_scroll) in pending_primitives { - self.add_primitive_to_draw_list(prim_index, clip_and_scroll); - } - } - - let mut pending_primitives = mem::replace(&mut self.pending_shadow_contents, Vec::new()); - for (prim_index, clip_and_scroll, info) in pending_primitives.drain(..) { - self.add_primitive_to_hit_testing_list(&info, clip_and_scroll); - self.add_primitive_to_draw_list(prim_index, clip_and_scroll); - } - - mem::replace(&mut self.pending_shadow_contents, pending_primitives); - mem::replace(&mut self.shadow_prim_stack, shadows); + assert!(self.shadow_stack.len() > 0, "popped shadows, but none were present"); + self.shadow_stack.clear(); } pub fn add_solid_rectangle( @@ -1486,13 +1510,17 @@ impl<'a> DisplayListFlattener<'a> { None, ); - let prim_index = self.add_primitive( - clip_and_scroll, + let prim_index = self.create_primitive( info, Vec::new(), PrimitiveContainer::Brush(prim), ); + self.add_primitive_to_draw_list( + prim_index, + clip_and_scroll, + ); + self.scrollbar_prims.push(ScrollbarPrimitive { prim_index, scroll_frame_index: scrollbar_info.0, @@ -1509,97 +1537,37 @@ impl<'a> DisplayListFlattener<'a> { line_color: &ColorF, style: LineStyle, ) { - let line = BrushPrimitive::new( - BrushKind::Line { - wavy_line_thickness, - color: line_color.premultiplied(), - style, - orientation, + let prim = BrushPrimitive::new( + BrushKind::Solid { + color: *line_color, }, None, ); - let mut fast_shadow_prims = Vec::new(); - let mut slow_shadow_prims = Vec::new(); - for (idx, &(shadow_prim_index, _)) in self.shadow_prim_stack.iter().enumerate() { - let shadow_metadata = &self.prim_store.cpu_metadata[shadow_prim_index.0]; - let brush = &self.prim_store.cpu_brushes[shadow_metadata.cpu_prim_index.0]; - let pic_index = brush.get_picture_index(); - let picture = &self.prim_store.pictures[pic_index.0]; - match picture.kind { - PictureKind::TextShadow { offset, color, blur_radius, .. } => { - if blur_radius == 0.0 { - fast_shadow_prims.push((idx, offset, color)); - } else { - slow_shadow_prims.push((pic_index, offset, color)); - } - } - _ => {} + let extra_clips = match style { + LineStyle::Solid => { + Vec::new() } - } - - for (idx, shadow_offset, shadow_color) in fast_shadow_prims { - 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); - info.clip_rect = info.clip_rect.translate(&shadow_offset); - let prim_index = self.create_primitive( - &info, - Vec::new(), - PrimitiveContainer::Brush(line), - ); - self.shadow_prim_stack[idx].1.push((prim_index, clip_and_scroll)); - } - - if line_color.a > 0.0 { - let prim_index = self.create_primitive( - &info, - Vec::new(), - PrimitiveContainer::Brush(line), - ); - - if self.shadow_prim_stack.is_empty() { - self.add_primitive_to_hit_testing_list(&info, clip_and_scroll); - self.add_primitive_to_draw_list(prim_index, clip_and_scroll); - } else { - self.pending_shadow_contents.push((prim_index, clip_and_scroll, *info)); + LineStyle::Wavy | + LineStyle::Dotted | + LineStyle::Dashed => { + vec![ + ClipSource::new_line_decoration( + info.rect, + style, + orientation, + wavy_line_thickness, + ), + ] } - } + }; - for (pic_index, shadow_offset, shadow_color) in slow_shadow_prims { - 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); - info.clip_rect = info.clip_rect.translate(&shadow_offset); - let prim_index = self.create_primitive( - &info, - Vec::new(), - PrimitiveContainer::Brush(line), - ); - - let picture = &mut self.prim_store.pictures[pic_index.0]; - - picture.add_primitive( - prim_index, - clip_and_scroll, - ); - } + self.add_primitive( + clip_and_scroll, + info, + extra_clips, + PrimitiveContainer::Brush(prim), + ); } pub fn add_border( @@ -2118,102 +2086,12 @@ impl<'a> DisplayListFlattener<'a> { } }; - // Text shadows that have a blur radius of 0 need to be rendered as normal - // text elements to get pixel perfect results for reftests. It's also a big - // performance win to avoid blurs and render target allocations where - // possible. For any text shadows that have zero blur, create a normal text - // primitive with the shadow's color and offset. These need to be added - // *before* the visual text primitive in order to get the correct paint - // order. Store them in a Vec first to work around borrowck issues. - // TODO(gw): Refactor to avoid having to store them in a Vec first. - let mut fast_shadow_prims = Vec::new(); - let mut slow_shadow_prims = Vec::new(); - for (idx, &(shadow_prim_index, _)) in self.shadow_prim_stack.iter().enumerate() { - let shadow_metadata = &self.prim_store.cpu_metadata[shadow_prim_index.0]; - let brush = &self.prim_store.cpu_brushes[shadow_metadata.cpu_prim_index.0]; - let pic_index = brush.get_picture_index(); - let picture_prim = &self.prim_store.pictures[pic_index.0]; - match picture_prim.kind { - PictureKind::TextShadow { offset, color, blur_radius, .. } => { - let mut text_prim = prim.clone(); - text_prim.font.color = color.into(); - text_prim.shadow = true; - text_prim.offset += offset; - - if blur_radius == 0.0 { - fast_shadow_prims.push((idx, text_prim, offset)); - } else { - text_prim.font.render_mode = text_prim - .font - .render_mode - .limit_by(FontRenderMode::Alpha); - - slow_shadow_prims.push((pic_index, text_prim, offset, blur_radius)); - } - } - _ => {} - } - } - - for (idx, text_prim, offset) in fast_shadow_prims { - let rect = prim_info.rect; - let mut info = prim_info.clone(); - info.rect = rect.translate(&offset); - info.clip_rect = info.clip_rect.translate(&offset); - let prim_index = self.create_primitive( - &info, - Vec::new(), - PrimitiveContainer::TextRun(text_prim), - ); - self.shadow_prim_stack[idx].1.push((prim_index, clip_and_scroll)); - } - - // Only add a visual element if it can contribute to the scene. - if text_color.a > 0.0 { - // Create (and add to primitive store) the primitive that will be - // used for both the visual element and also the shadow(s). - let prim_index = self.create_primitive( - prim_info, - Vec::new(), - PrimitiveContainer::TextRun(prim), - ); - - if self.shadow_prim_stack.is_empty() { - self.add_primitive_to_hit_testing_list(prim_info, clip_and_scroll); - self.add_primitive_to_draw_list(prim_index, clip_and_scroll); - } else { - self.pending_shadow_contents.push((prim_index, clip_and_scroll, *prim_info)); - } - } - - // Now add this primitive index to all the currently active text shadow - // primitives. Although we're adding the indices *after* the visual - // primitive here, they will still draw before the visual text, since - // the shadow primitive itself has been added to the draw cmd - // list *before* the visual element, during push_shadow. We need - // the primitive index of the visual element here before we can add - // the indices as sub-primitives to the shadow primitives. - for (pic_index, shadow_prim, offset, blur_radius) in slow_shadow_prims { - let blur_region = blur_radius * BLUR_SAMPLE_SCALE; - - let rect = prim_info.rect; - let mut info = prim_info.clone(); - info.rect = rect.translate(&offset).inflate(blur_region, blur_region); - info.clip_rect = info.clip_rect.translate(&offset); - - let prim_index = self.create_primitive( - &info, - Vec::new(), - PrimitiveContainer::TextRun(shadow_prim), - ); - - let picture = &mut self.prim_store.pictures[pic_index.0]; - - picture.add_primitive( - prim_index, - clip_and_scroll, - ); - } + self.add_primitive( + clip_and_scroll, + prim_info, + Vec::new(), + PrimitiveContainer::TextRun(prim), + ); } pub fn add_image( diff --git a/gfx/webrender/src/frame_builder.rs b/gfx/webrender/src/frame_builder.rs index ae010af7c84f..d3e063d593a1 100644 --- a/gfx/webrender/src/frame_builder.rs +++ b/gfx/webrender/src/frame_builder.rs @@ -10,7 +10,7 @@ use clip_scroll_node::{ClipScrollNode}; use clip_scroll_tree::{ClipScrollNodeIndex, ClipScrollTree}; use display_list_flattener::{DisplayListFlattener}; use gpu_cache::GpuCache; -use gpu_types::{ClipChainRectIndex, ClipScrollNodeData, PictureType}; +use gpu_types::{ClipChainRectIndex, ClipScrollNodeData}; use hit_test::{HitTester, HitTestingRun}; use internal_types::{FastHashMap}; use prim_store::{CachedGradient, PrimitiveIndex, PrimitiveRun, PrimitiveStore}; @@ -74,6 +74,7 @@ pub struct PictureContext<'a> { pub display_list: &'a BuiltDisplayList, pub inv_world_transform: Option, pub apply_local_clip_rect: bool, + pub inflation_factor: f32, } pub struct PictureState { @@ -203,6 +204,7 @@ impl FrameBuilder { display_list, inv_world_transform: None, apply_local_clip_rect: true, + inflation_factor: 0.0, }; let mut pic_state = PictureState::new(); @@ -226,7 +228,6 @@ impl FrameBuilder { PremultipliedColorF::TRANSPARENT, ClearMode::Transparent, pic_state.tasks, - PictureType::Image, ); let render_task_id = frame_state.render_tasks.add(root_render_task); @@ -310,7 +311,7 @@ impl FrameBuilder { self.update_scroll_bars(clip_scroll_tree, gpu_cache); - let mut render_tasks = RenderTaskTree::new(); + let mut render_tasks = RenderTaskTree::new(frame_id); let main_render_task_id = self.build_layer_screen_rects_and_cull_layers( clip_scroll_tree, diff --git a/gfx/webrender/src/gpu_types.rs b/gfx/webrender/src/gpu_types.rs index 6588c2e21e3a..223fa097bd70 100644 --- a/gfx/webrender/src/gpu_types.rs +++ b/gfx/webrender/src/gpu_types.rs @@ -9,6 +9,28 @@ use render_task::RenderTaskAddress; // Contains type that must exactly match the same structures declared in GLSL. +#[derive(Copy, Clone, Debug)] +#[repr(C)] +pub struct ZBufferId(i32); + +pub struct ZBufferIdGenerator { + next: i32, +} + +impl ZBufferIdGenerator { + pub fn new() -> ZBufferIdGenerator { + ZBufferIdGenerator { + next: 0 + } + } + + pub fn next(&mut self) -> ZBufferId { + let id = ZBufferId(self.next); + self.next += 1; + id + } +} + #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] @@ -75,7 +97,7 @@ pub struct SimplePrimitiveInstance { pub clip_task_address: RenderTaskAddress, pub clip_chain_rect_index: ClipChainRectIndex, pub scroll_id: ClipScrollNodeIndex, - pub z_sort_index: i32, + pub z: ZBufferId, } impl SimplePrimitiveInstance { @@ -85,7 +107,7 @@ impl SimplePrimitiveInstance { clip_task_address: RenderTaskAddress, clip_chain_rect_index: ClipChainRectIndex, scroll_id: ClipScrollNodeIndex, - z_sort_index: i32, + z: ZBufferId, ) -> Self { SimplePrimitiveInstance { specific_prim_address, @@ -93,7 +115,7 @@ impl SimplePrimitiveInstance { clip_task_address, clip_chain_rect_index, scroll_id, - z_sort_index, + z, } } @@ -104,7 +126,7 @@ impl SimplePrimitiveInstance { self.task_address.0 as i32, self.clip_task_address.0 as i32, ((self.clip_chain_rect_index.0 as i32) << 16) | self.scroll_id.0 as i32, - self.z_sort_index, + self.z.0, data0, data1, data2, @@ -119,7 +141,7 @@ pub struct CompositePrimitiveInstance { pub backdrop_task_address: RenderTaskAddress, pub data0: i32, pub data1: i32, - pub z: i32, + pub z: ZBufferId, pub data2: i32, pub data3: i32, } @@ -131,7 +153,7 @@ impl CompositePrimitiveInstance { backdrop_task_address: RenderTaskAddress, data0: i32, data1: i32, - z: i32, + z: ZBufferId, data2: i32, data3: i32, ) -> Self { @@ -155,7 +177,7 @@ impl From for PrimitiveInstance { instance.task_address.0 as i32, instance.src_task_address.0 as i32, instance.backdrop_task_address.0 as i32, - instance.z, + instance.z.0, instance.data0, instance.data1, instance.data2, @@ -187,7 +209,7 @@ pub struct BrushInstance { pub clip_chain_rect_index: ClipChainRectIndex, pub scroll_id: ClipScrollNodeIndex, pub clip_task_address: RenderTaskAddress, - pub z: i32, + pub z: ZBufferId, pub segment_index: i32, pub edge_flags: EdgeAaSegmentMask, pub brush_flags: BrushFlags, @@ -201,7 +223,7 @@ impl From for PrimitiveInstance { instance.picture_address.0 as i32 | (instance.clip_task_address.0 as i32) << 16, instance.prim_address.as_int(), ((instance.clip_chain_rect_index.0 as i32) << 16) | instance.scroll_id.0 as i32, - instance.z, + instance.z.0, instance.segment_index | ((instance.edge_flags.bits() as i32) << 16) | ((instance.brush_flags.bits() as i32) << 24), @@ -245,15 +267,6 @@ impl ClipScrollNodeData { #[repr(C)] pub struct ClipChainRectIndex(pub usize); -#[derive(Copy, Debug, Clone, PartialEq)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -#[repr(C)] -pub enum PictureType { - Image = 1, - TextShadow = 2, -} - #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] diff --git a/gfx/webrender/src/hit_test.rs b/gfx/webrender/src/hit_test.rs index 8e5fb1862971..042c486ec5af 100644 --- a/gfx/webrender/src/hit_test.rs +++ b/gfx/webrender/src/hit_test.rs @@ -302,8 +302,9 @@ fn get_regions_for_clip_scroll_node( HitTestRegion::RoundedRectangle(*rect, *radii, *mode), ClipSource::Image(ref mask) => HitTestRegion::Rectangle(mask.rect), ClipSource::BorderCorner(_) | + ClipSource::LineDecoration(_) | ClipSource::BoxShadow(_) => { - unreachable!("Didn't expect to hit test against BorderCorner / BoxShadow"); + unreachable!("Didn't expect to hit test against BorderCorner / BoxShadow / LineDecoration"); } } }).collect() diff --git a/gfx/webrender/src/picture.rs b/gfx/webrender/src/picture.rs index 51216a4d80d8..73e944555d15 100644 --- a/gfx/webrender/src/picture.rs +++ b/gfx/webrender/src/picture.rs @@ -2,14 +2,12 @@ * 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::{ColorF, FilterOp, MixBlendMode, PipelineId}; -use api::{DeviceIntRect, LayerRect, LayerToWorldScale, LayerVector2D}; -use api::{PremultipliedColorF, Shadow}; +use api::{FilterOp, MixBlendMode, PipelineId, PremultipliedColorF}; +use api::{DeviceIntRect, LayerRect, LayerToWorldScale}; use box_shadow::{BLUR_SAMPLE_SCALE}; use clip_scroll_tree::ClipScrollNodeIndex; use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureState}; use gpu_cache::{GpuCacheHandle, GpuDataRequest}; -use gpu_types::{PictureType}; use prim_store::{PrimitiveIndex, PrimitiveRun, PrimitiveRunLocalRect}; use prim_store::{PrimitiveMetadata, ScrollNodeAndClipChain}; use render_task::{ClearMode, RenderTask}; @@ -40,95 +38,67 @@ pub enum PictureCompositeMode { Blit, } -#[derive(Debug)] -pub enum PictureKind { - TextShadow { - offset: LayerVector2D, - color: ColorF, - blur_radius: f32, - }, - Image { - // If a mix-blend-mode, contains the render task for - // the readback of the framebuffer that we use to sample - // from in the mix-blend-mode shader. - // For drop-shadow filter, this will store the original - // picture task which would be rendered on screen after - // blur pass. - secondary_render_task_id: Option, - /// How this picture should be composited. - /// If None, don't composite - just draw directly on parent surface. - composite_mode: Option, - // If true, this picture is part of a 3D context. - is_in_3d_context: bool, - // If requested as a frame output (for rendering - // pages to a texture), this is the pipeline this - // picture is the root of. - frame_output_pipeline_id: Option, - // The original reference frame ID for this picture. - // It is only different if this is part of a 3D - // rendering context. - reference_frame_index: ClipScrollNodeIndex, - real_local_rect: LayerRect, - // An optional cache handle for storing extra data - // in the GPU cache, depending on the type of - // picture. - extra_gpu_data_handle: GpuCacheHandle, - }, -} - #[derive(Debug)] pub struct PicturePrimitive { // If this picture is drawn to an intermediate surface, // the associated target information. pub surface: Option, - // Details specific to this type of picture. - pub kind: PictureKind, - // List of primitive runs that make up this picture. pub runs: Vec, // The pipeline that the primitives on this picture belong to. pub pipeline_id: PipelineId, + // If true, apply the local clip rect to primitive drawn + // in this picture. + pub apply_local_clip_rect: bool, + // The current screen-space rect of the rendered // portion of this picture. task_rect: DeviceIntRect, + + // If a mix-blend-mode, contains the render task for + // the readback of the framebuffer that we use to sample + // from in the mix-blend-mode shader. + // For drop-shadow filter, this will store the original + // picture task which would be rendered on screen after + // blur pass. + pub secondary_render_task_id: Option, + /// How this picture should be composited. + /// If None, don't composite - just draw directly on parent surface. + pub composite_mode: Option, + // If true, this picture is part of a 3D context. + pub is_in_3d_context: bool, + // If requested as a frame output (for rendering + // pages to a texture), this is the pipeline this + // picture is the root of. + pub frame_output_pipeline_id: Option, + // The original reference frame ID for this picture. + // It is only different if this is part of a 3D + // rendering context. + pub reference_frame_index: ClipScrollNodeIndex, + pub real_local_rect: LayerRect, + // An optional cache handle for storing extra data + // in the GPU cache, depending on the type of + // picture. + pub extra_gpu_data_handle: GpuCacheHandle, } impl PicturePrimitive { - pub fn new_text_shadow(shadow: Shadow, pipeline_id: PipelineId) -> Self { - PicturePrimitive { - runs: Vec::new(), - surface: None, - kind: PictureKind::TextShadow { - offset: shadow.offset, - color: shadow.color, - blur_radius: shadow.blur_radius, - }, - pipeline_id, - task_rect: DeviceIntRect::zero(), - } - } - pub fn resolve_scene_properties(&mut self, properties: &SceneProperties) -> bool { - match self.kind { - PictureKind::Image { ref mut composite_mode, .. } => { - match composite_mode { - &mut Some(PictureCompositeMode::Filter(ref mut filter)) => { - match filter { - &mut FilterOp::Opacity(ref binding, ref mut value) => { - *value = properties.resolve_float(binding, *value); - } - _ => {} - } - - filter.is_visible() + match self.composite_mode { + Some(PictureCompositeMode::Filter(ref mut filter)) => { + match filter { + &mut FilterOp::Opacity(ref binding, ref mut value) => { + *value = properties.resolve_float(binding, *value); } - _ => true, + _ => {} } + + filter.is_visible() } - _ => true + _ => true, } } @@ -138,19 +108,19 @@ impl PicturePrimitive { pipeline_id: PipelineId, reference_frame_index: ClipScrollNodeIndex, frame_output_pipeline_id: Option, + apply_local_clip_rect: bool, ) -> Self { PicturePrimitive { runs: Vec::new(), surface: None, - kind: PictureKind::Image { - secondary_render_task_id: None, - composite_mode, - is_in_3d_context, - frame_output_pipeline_id, - reference_frame_index, - real_local_rect: LayerRect::zero(), - extra_gpu_data_handle: GpuCacheHandle::new(), - }, + secondary_render_task_id: None, + composite_mode, + is_in_3d_context, + frame_output_pipeline_id, + reference_frame_index, + real_local_rect: LayerRect::zero(), + extra_gpu_data_handle: GpuCacheHandle::new(), + apply_local_clip_rect, pipeline_id, task_rect: DeviceIntRect::zero(), } @@ -182,32 +152,20 @@ impl PicturePrimitive { ) -> LayerRect { let local_content_rect = prim_run_rect.local_rect_in_actual_parent_space; - match self.kind { - PictureKind::Image { composite_mode, ref mut real_local_rect, .. } => { - *real_local_rect = prim_run_rect.local_rect_in_original_parent_space; + self.real_local_rect = prim_run_rect.local_rect_in_original_parent_space; - match composite_mode { - Some(PictureCompositeMode::Filter(FilterOp::Blur(blur_radius))) => { - let inflate_size = blur_radius * BLUR_SAMPLE_SCALE; - local_content_rect.inflate(inflate_size, inflate_size) - } - Some(PictureCompositeMode::Filter(FilterOp::DropShadow(offset, blur_radius, _))) => { - let inflate_size = blur_radius * BLUR_SAMPLE_SCALE; - local_content_rect.inflate(inflate_size, inflate_size) - .translate(&offset) - } - _ => { - local_content_rect - } - } + match self.composite_mode { + Some(PictureCompositeMode::Filter(FilterOp::Blur(blur_radius))) => { + let inflate_size = (blur_radius * BLUR_SAMPLE_SCALE).ceil(); + local_content_rect.inflate(inflate_size, inflate_size) } - PictureKind::TextShadow { blur_radius, .. } => { - let blur_offset = blur_radius * BLUR_SAMPLE_SCALE; - - local_content_rect.inflate( - blur_offset, - blur_offset, - ) + Some(PictureCompositeMode::Filter(FilterOp::DropShadow(offset, blur_radius, _))) => { + let inflate_size = blur_radius * BLUR_SAMPLE_SCALE; + local_content_rect.inflate(inflate_size, inflate_size) + .translate(&offset) + } + _ => { + local_content_rect } } } @@ -226,239 +184,178 @@ impl PicturePrimitive { .screen_rect .as_ref() .expect("bug: trying to draw an off-screen picture!?"); - let device_rect; + let device_rect = match self.composite_mode { + Some(PictureCompositeMode::Filter(FilterOp::Blur(blur_radius))) => { + // If blur radius is 0, we can skip drawing this an an + // intermediate surface. + if blur_radius == 0.0 { + pic_state.tasks.extend(pic_state_for_children.tasks); + self.surface = None; - match self.kind { - PictureKind::Image { - ref mut secondary_render_task_id, - ref mut extra_gpu_data_handle, - composite_mode, - .. - } => { - device_rect = match composite_mode { - Some(PictureCompositeMode::Filter(FilterOp::Blur(blur_radius))) => { - // If blur radius is 0, we can skip drawing this an an - // intermediate surface. - if blur_radius == 0.0 { - pic_state.tasks.extend(pic_state_for_children.tasks); - self.surface = None; + DeviceIntRect::zero() + } else { + let blur_std_deviation = blur_radius * frame_context.device_pixel_scale.0; + let blur_range = (blur_std_deviation * BLUR_SAMPLE_SCALE).ceil() as i32; - DeviceIntRect::zero() - } else { - let blur_std_deviation = blur_radius * frame_context.device_pixel_scale.0; - let blur_range = (blur_std_deviation * BLUR_SAMPLE_SCALE).ceil() as i32; + // The clipped field is the part of the picture that is visible + // on screen. The unclipped field is the screen-space rect of + // the complete picture, if no screen / clip-chain was applied + // (this includes the extra space for blur region). To ensure + // that we draw a large enough part of the picture to get correct + // blur results, inflate that clipped area by the blur range, and + // then intersect with the total screen rect, to minimize the + // allocation size. + let device_rect = prim_screen_rect + .clipped + .inflate(blur_range, blur_range) + .intersection(&prim_screen_rect.unclipped) + .unwrap(); - // The clipped field is the part of the picture that is visible - // on screen. The unclipped field is the screen-space rect of - // the complete picture, if no screen / clip-chain was applied - // (this includes the extra space for blur region). To ensure - // that we draw a large enough part of the picture to get correct - // blur results, inflate that clipped area by the blur range, and - // then intersect with the total screen rect, to minimize the - // allocation size. - let device_rect = prim_screen_rect - .clipped - .inflate(blur_range, blur_range) - .intersection(&prim_screen_rect.unclipped) - .unwrap(); + let picture_task = RenderTask::new_picture( + RenderTaskLocation::Dynamic(None, device_rect.size), + prim_index, + RenderTargetKind::Color, + device_rect.origin, + PremultipliedColorF::TRANSPARENT, + ClearMode::Transparent, + pic_state_for_children.tasks, + ); - let picture_task = RenderTask::new_picture( - RenderTaskLocation::Dynamic(None, device_rect.size), - prim_index, - RenderTargetKind::Color, - device_rect.origin, - PremultipliedColorF::TRANSPARENT, - ClearMode::Transparent, - pic_state_for_children.tasks, - PictureType::Image, - ); + let picture_task_id = frame_state.render_tasks.add(picture_task); - let picture_task_id = frame_state.render_tasks.add(picture_task); + let blur_render_task = RenderTask::new_blur( + blur_std_deviation, + picture_task_id, + frame_state.render_tasks, + RenderTargetKind::Color, + ClearMode::Transparent, + ); - let blur_render_task = RenderTask::new_blur( - blur_std_deviation, - picture_task_id, - frame_state.render_tasks, - RenderTargetKind::Color, - ClearMode::Transparent, - ); + let render_task_id = frame_state.render_tasks.add(blur_render_task); + pic_state.tasks.push(render_task_id); + self.surface = Some(render_task_id); - let render_task_id = frame_state.render_tasks.add(blur_render_task); - pic_state.tasks.push(render_task_id); - self.surface = Some(render_task_id); - - device_rect - } - } - Some(PictureCompositeMode::Filter(FilterOp::DropShadow(offset, blur_radius, _))) => { - // TODO(gw): This is totally wrong and can never work with - // transformed drop-shadow elements. Fix me! - let rect = (prim_metadata.local_rect.translate(&-offset) * content_scale).round().to_i32(); - let mut picture_task = RenderTask::new_picture( - RenderTaskLocation::Dynamic(None, rect.size), - prim_index, - RenderTargetKind::Color, - rect.origin, - PremultipliedColorF::TRANSPARENT, - ClearMode::Transparent, - pic_state_for_children.tasks, - PictureType::Image, - ); - picture_task.mark_for_saving(); - - let blur_std_deviation = blur_radius * frame_context.device_pixel_scale.0; - let picture_task_id = frame_state.render_tasks.add(picture_task); - - let blur_render_task = RenderTask::new_blur( - blur_std_deviation.round(), - picture_task_id, - frame_state.render_tasks, - RenderTargetKind::Color, - ClearMode::Transparent, - ); - - *secondary_render_task_id = Some(picture_task_id); - - let render_task_id = frame_state.render_tasks.add(blur_render_task); - pic_state.tasks.push(render_task_id); - self.surface = Some(render_task_id); - - rect - } - Some(PictureCompositeMode::MixBlend(..)) => { - let picture_task = RenderTask::new_picture( - RenderTaskLocation::Dynamic(None, prim_screen_rect.clipped.size), - prim_index, - RenderTargetKind::Color, - prim_screen_rect.clipped.origin, - PremultipliedColorF::TRANSPARENT, - ClearMode::Transparent, - pic_state_for_children.tasks, - PictureType::Image, - ); - - let readback_task_id = frame_state.render_tasks.add( - RenderTask::new_readback(prim_screen_rect.clipped) - ); - - *secondary_render_task_id = Some(readback_task_id); - pic_state.tasks.push(readback_task_id); - - let render_task_id = frame_state.render_tasks.add(picture_task); - pic_state.tasks.push(render_task_id); - self.surface = Some(render_task_id); - - prim_screen_rect.clipped - } - Some(PictureCompositeMode::Filter(filter)) => { - // If this filter is not currently going to affect - // the picture, just collapse this picture into the - // current render task. This most commonly occurs - // when opacity == 1.0, but can also occur on other - // filters and be a significant performance win. - if filter.is_noop() { - pic_state.tasks.extend(pic_state_for_children.tasks); - self.surface = None; - } else { - - if let FilterOp::ColorMatrix(m) = filter { - if let Some(mut request) = frame_state.gpu_cache.request(extra_gpu_data_handle) { - for i in 0..5 { - request.push([m[i*4], m[i*4+1], m[i*4+2], m[i*4+3]]); - } - } - } - - let picture_task = RenderTask::new_picture( - RenderTaskLocation::Dynamic(None, prim_screen_rect.clipped.size), - prim_index, - RenderTargetKind::Color, - prim_screen_rect.clipped.origin, - PremultipliedColorF::TRANSPARENT, - ClearMode::Transparent, - pic_state_for_children.tasks, - PictureType::Image, - ); - - let render_task_id = frame_state.render_tasks.add(picture_task); - pic_state.tasks.push(render_task_id); - self.surface = Some(render_task_id); - } - - prim_screen_rect.clipped - } - Some(PictureCompositeMode::Blit) => { - let picture_task = RenderTask::new_picture( - RenderTaskLocation::Dynamic(None, prim_screen_rect.clipped.size), - prim_index, - RenderTargetKind::Color, - prim_screen_rect.clipped.origin, - PremultipliedColorF::TRANSPARENT, - ClearMode::Transparent, - pic_state_for_children.tasks, - PictureType::Image, - ); - - let render_task_id = frame_state.render_tasks.add(picture_task); - pic_state.tasks.push(render_task_id); - self.surface = Some(render_task_id); - - prim_screen_rect.clipped - } - None => { - pic_state.tasks.extend(pic_state_for_children.tasks); - self.surface = None; - - DeviceIntRect::zero() - } - }; + device_rect + } } - PictureKind::TextShadow { blur_radius, color, .. } => { - // This is a shadow element. Create a render task that will - // render the text run to a target, and then apply a gaussian - // blur to that text run in order to build the actual primitive - // which will be blitted to the framebuffer. - - // 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 * frame_context.device_pixel_scale.0).round(); - let blur_std_deviation = device_radius * 0.5; - - let blur_range = (blur_std_deviation * BLUR_SAMPLE_SCALE).ceil() as i32; - - device_rect = prim_screen_rect - .clipped - .inflate(blur_range, blur_range) - .intersection(&prim_screen_rect.unclipped) - .unwrap(); - - let picture_task = RenderTask::new_picture( - RenderTaskLocation::Dynamic(None, device_rect.size), + Some(PictureCompositeMode::Filter(FilterOp::DropShadow(offset, blur_radius, _))) => { + // TODO(gw): This is totally wrong and can never work with + // transformed drop-shadow elements. Fix me! + let rect = (prim_metadata.local_rect.translate(&-offset) * content_scale).round().to_i32(); + let mut picture_task = RenderTask::new_picture( + RenderTaskLocation::Dynamic(None, rect.size), prim_index, RenderTargetKind::Color, - device_rect.origin, - color.premultiplied(), + rect.origin, + PremultipliedColorF::TRANSPARENT, ClearMode::Transparent, - Vec::new(), - PictureType::TextShadow, + pic_state_for_children.tasks, ); + picture_task.mark_for_saving(); + let blur_std_deviation = blur_radius * frame_context.device_pixel_scale.0; let picture_task_id = frame_state.render_tasks.add(picture_task); let blur_render_task = RenderTask::new_blur( - blur_std_deviation, + blur_std_deviation.round(), picture_task_id, frame_state.render_tasks, RenderTargetKind::Color, ClearMode::Transparent, ); + self.secondary_render_task_id = Some(picture_task_id); + let render_task_id = frame_state.render_tasks.add(blur_render_task); pic_state.tasks.push(render_task_id); self.surface = Some(render_task_id); + + rect } - } + Some(PictureCompositeMode::MixBlend(..)) => { + let picture_task = RenderTask::new_picture( + RenderTaskLocation::Dynamic(None, prim_screen_rect.clipped.size), + prim_index, + RenderTargetKind::Color, + prim_screen_rect.clipped.origin, + PremultipliedColorF::TRANSPARENT, + ClearMode::Transparent, + pic_state_for_children.tasks, + ); + + let readback_task_id = frame_state.render_tasks.add( + RenderTask::new_readback(prim_screen_rect.clipped) + ); + + self.secondary_render_task_id = Some(readback_task_id); + pic_state.tasks.push(readback_task_id); + + let render_task_id = frame_state.render_tasks.add(picture_task); + pic_state.tasks.push(render_task_id); + self.surface = Some(render_task_id); + + prim_screen_rect.clipped + } + Some(PictureCompositeMode::Filter(filter)) => { + // If this filter is not currently going to affect + // the picture, just collapse this picture into the + // current render task. This most commonly occurs + // when opacity == 1.0, but can also occur on other + // filters and be a significant performance win. + if filter.is_noop() { + pic_state.tasks.extend(pic_state_for_children.tasks); + self.surface = None; + } else { + + if let FilterOp::ColorMatrix(m) = filter { + if let Some(mut request) = frame_state.gpu_cache.request(&mut self.extra_gpu_data_handle) { + for i in 0..5 { + request.push([m[i*4], m[i*4+1], m[i*4+2], m[i*4+3]]); + } + } + } + + let picture_task = RenderTask::new_picture( + RenderTaskLocation::Dynamic(None, prim_screen_rect.clipped.size), + prim_index, + RenderTargetKind::Color, + prim_screen_rect.clipped.origin, + PremultipliedColorF::TRANSPARENT, + ClearMode::Transparent, + pic_state_for_children.tasks, + ); + + let render_task_id = frame_state.render_tasks.add(picture_task); + pic_state.tasks.push(render_task_id); + self.surface = Some(render_task_id); + } + + prim_screen_rect.clipped + } + Some(PictureCompositeMode::Blit) => { + let picture_task = RenderTask::new_picture( + RenderTaskLocation::Dynamic(None, prim_screen_rect.clipped.size), + prim_index, + RenderTargetKind::Color, + prim_screen_rect.clipped.origin, + PremultipliedColorF::TRANSPARENT, + ClearMode::Transparent, + pic_state_for_children.tasks, + ); + + let render_task_id = frame_state.render_tasks.add(picture_task); + pic_state.tasks.push(render_task_id); + self.surface = Some(render_task_id); + + prim_screen_rect.clipped + } + None => { + pic_state.tasks.extend(pic_state_for_children.tasks); + self.surface = None; + + DeviceIntRect::zero() + } + }; // If scrolling or property animation has resulted in the task // rect being different than last time, invalidate the GPU @@ -473,22 +370,15 @@ impl PicturePrimitive { pub fn write_gpu_blocks(&self, request: &mut GpuDataRequest) { request.push(self.task_rect.to_f32()); - match self.kind { - PictureKind::TextShadow { .. } => { - request.push(PremultipliedColorF::WHITE); + let color = match self.composite_mode { + Some(PictureCompositeMode::Filter(FilterOp::DropShadow(_, _, color))) => { + color.premultiplied() } - PictureKind::Image { composite_mode, .. } => { - let color = match composite_mode { - Some(PictureCompositeMode::Filter(FilterOp::DropShadow(_, _, color))) => { - color.premultiplied() - } - _ => { - PremultipliedColorF::WHITE - } - }; + _ => { + PremultipliedColorF::WHITE + } + }; - request.push(color); - } - } + request.push(color); } } diff --git a/gfx/webrender/src/prim_store.rs b/gfx/webrender/src/prim_store.rs index 00d99d4f67ec..d32d6b69d2c9 100644 --- a/gfx/webrender/src/prim_store.rs +++ b/gfx/webrender/src/prim_store.rs @@ -4,10 +4,11 @@ use api::{AlphaType, BorderRadius, BoxShadowClipMode, BuiltDisplayList, ClipMode, ColorF, ComplexClipRegion}; use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, Epoch, ExtendMode, FontRenderMode}; -use api::{GlyphInstance, GlyphKey, GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag}; -use api::{LayerPoint, LayerRect, LayerSize, LayerToWorldTransform, LayerVector2D, LineOrientation}; -use api::{LineStyle, PipelineId, PremultipliedColorF, Shadow, YuvColorSpace, YuvFormat}; +use api::{FilterOp, GlyphInstance, GlyphKey, GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag}; +use api::{LayerPoint, LayerRect, LayerSize, LayerToWorldTransform, LayerVector2D}; +use api::{PipelineId, PremultipliedColorF, Shadow, YuvColorSpace, YuvFormat}; use border::{BorderCornerInstance, BorderEdgeKind}; +use box_shadow::BLUR_SAMPLE_SCALE; use clip_scroll_tree::{ClipChainIndex, ClipScrollNodeIndex, CoordinateSystemId}; use clip_scroll_node::ClipScrollNode; use clip::{ClipChain, ClipChainNode, ClipChainNodeIter, ClipChainNodeRef, ClipSource}; @@ -18,7 +19,7 @@ use glyph_rasterizer::{FontInstance, FontTransform}; use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest, ToGpuBlocks}; use gpu_types::{ClipChainRectIndex}; -use picture::{PictureCompositeMode, PictureKind, PicturePrimitive}; +use picture::{PictureCompositeMode, PicturePrimitive}; use render_task::{BlitSource, RenderTask, RenderTaskCacheKey, RenderTaskCacheKeyKind}; use render_task::RenderTaskId; use renderer::{MAX_VERTEX_TEXTURE_WIDTH}; @@ -198,12 +199,6 @@ pub enum BrushKind { color: ColorF, }, Clear, - Line { - color: PremultipliedColorF, - wavy_line_thickness: f32, - style: LineStyle, - orientation: LineOrientation, - }, Picture { pic_index: PictureIndex, }, @@ -248,8 +243,7 @@ impl BrushKind { BrushKind::RadialGradient { .. } | BrushKind::LinearGradient { .. } => true, - BrushKind::Clear | - BrushKind::Line { .. } => false, + BrushKind::Clear => false, } } } @@ -355,15 +349,6 @@ impl BrushPrimitive { // Opaque black with operator dest out request.push(PremultipliedColorF::BLACK); } - 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, - ]); - } BrushKind::LinearGradient { start_point, end_point, extend_mode, .. } => { request.push([ start_point.x, @@ -919,6 +904,95 @@ pub enum PrimitiveContainer { Brush(BrushPrimitive), } +impl PrimitiveContainer { + // Return true if the primary primitive is visible. + // Used to trivially reject non-visible primitives. + // TODO(gw): Currently, primitives other than those + // listed here are handled before the + // add_primitive() call. In the future + // we should move the logic for all other + // primitive types to use this. + pub fn is_visible(&self) -> bool { + match *self { + PrimitiveContainer::TextRun(ref info) => { + info.font.color.a > 0 + } + PrimitiveContainer::Brush(ref brush) => { + match brush.kind { + BrushKind::Solid { ref color } => { + color.a > 0.0 + } + BrushKind::Clear | + BrushKind::Picture { .. } | + BrushKind::Image { .. } | + BrushKind::YuvImage { .. } | + BrushKind::RadialGradient { .. } | + BrushKind::LinearGradient { .. } => { + true + } + } + } + PrimitiveContainer::Image(..) | + PrimitiveContainer::Border(..) => { + true + } + } + } + + // Create a clone of this PrimitiveContainer, applying whatever + // changes are necessary to the primitive to support rendering + // it as part of the supplied shadow. + pub fn create_shadow(&self, shadow: &Shadow) -> PrimitiveContainer { + match *self { + PrimitiveContainer::TextRun(ref info) => { + let mut render_mode = info.font.render_mode; + + if shadow.blur_radius > 0.0 { + render_mode = render_mode.limit_by(FontRenderMode::Alpha); + } + + PrimitiveContainer::TextRun(TextRunPrimitiveCpu { + font: FontInstance { + color: shadow.color.into(), + render_mode, + ..info.font.clone() + }, + offset: info.offset + shadow.offset, + glyph_range: info.glyph_range, + glyph_count: info.glyph_count, + glyph_keys: info.glyph_keys.clone(), + glyph_gpu_blocks: Vec::new(), + shadow: true, + }) + } + PrimitiveContainer::Brush(ref brush) => { + match brush.kind { + BrushKind::Solid { .. } => { + PrimitiveContainer::Brush(BrushPrimitive::new( + BrushKind::Solid { + color: shadow.color, + }, + None, + )) + } + BrushKind::Clear | + BrushKind::Picture { .. } | + BrushKind::Image { .. } | + BrushKind::YuvImage { .. } | + BrushKind::RadialGradient { .. } | + BrushKind::LinearGradient { .. } => { + panic!("bug: other brush kinds not expected here yet"); + } + } + } + PrimitiveContainer::Image(..) | + PrimitiveContainer::Border(..) => { + panic!("bug: other primitive containers not expected here"); + } + } + } +} + pub struct PrimitiveStore { /// CPU side information only. pub cpu_brushes: Vec, @@ -962,6 +1036,7 @@ impl PrimitiveStore { pipeline_id: PipelineId, reference_frame_index: ClipScrollNodeIndex, frame_output_pipeline_id: Option, + apply_local_clip_rect: bool, ) -> PictureIndex { let pic = PicturePrimitive::new_image( composite_mode, @@ -969,22 +1044,7 @@ impl PrimitiveStore { pipeline_id, reference_frame_index, frame_output_pipeline_id, - ); - - let pic_index = PictureIndex(self.pictures.len()); - self.pictures.push(pic); - - pic_index - } - - pub fn add_shadow_picture( - &mut self, - shadow: Shadow, - pipeline_id: PipelineId, - ) -> PictureIndex { - let pic = PicturePrimitive::new_text_shadow( - shadow, - pipeline_id, + apply_local_clip_rect, ); let pic_index = PictureIndex(self.pictures.len()); @@ -1024,7 +1084,6 @@ impl PrimitiveStore { let opacity = match brush.kind { BrushKind::Clear => PrimitiveOpacity::translucent(), BrushKind::Solid { ref color } => PrimitiveOpacity::from_alpha(color.a), - BrushKind::Line { .. } => PrimitiveOpacity::translucent(), BrushKind::Image { .. } => PrimitiveOpacity::translucent(), BrushKind::YuvImage { .. } => PrimitiveOpacity::opaque(), BrushKind::RadialGradient { .. } => PrimitiveOpacity::translucent(), @@ -1300,8 +1359,7 @@ impl PrimitiveStore { ); } BrushKind::Solid { .. } | - BrushKind::Clear | - BrushKind::Line { .. } => {} + BrushKind::Clear => {} } } } @@ -1427,6 +1485,7 @@ impl PrimitiveStore { continue; } ClipSource::BorderCorner(..) | + ClipSource::LineDecoration(..) | ClipSource::Image(..) => { // TODO(gw): We can easily extend the segment builder // to support these clip sources in the @@ -1560,6 +1619,18 @@ impl PrimitiveStore { true } + fn reset_clip_task(&mut self, prim_index: PrimitiveIndex) { + let metadata = &mut self.cpu_metadata[prim_index.0]; + metadata.clip_task_id = None; + if metadata.prim_kind == PrimitiveKind::Brush { + if let Some(ref mut desc) = self.cpu_brushes[metadata.cpu_prim_index.0].segment_desc { + for segment in &mut desc.segments { + segment.clip_task_id = None; + } + } + } + } + fn update_clip_task( &mut self, prim_index: PrimitiveIndex, @@ -1569,7 +1640,8 @@ impl PrimitiveStore { frame_context: &FrameBuildingContext, frame_state: &mut FrameBuildingState, ) -> bool { - self.cpu_metadata[prim_index.0].clip_task_id = None; + // Reset clips from previous frames since we may clip differently each frame. + self.reset_clip_task(prim_index); let prim_screen_rect = match prim_screen_rect.intersection(&frame_context.screen_rect) { Some(rect) => rect, @@ -1733,13 +1805,16 @@ impl PrimitiveStore { return None; } - let (apply_local_clip_rect, original_reference_frame_index) = match pic.kind { - PictureKind::Image { reference_frame_index, composite_mode, .. } => { - may_need_clip_mask = composite_mode.is_some(); - (true, Some(reference_frame_index)) + may_need_clip_mask = pic.composite_mode.is_some(); + + let inflation_factor = match pic.composite_mode { + Some(PictureCompositeMode::Filter(FilterOp::Blur(blur_radius))) => { + // The amount of extra space needed for primitives inside + // this picture to ensure the visibility check is correct. + BLUR_SAMPLE_SCALE * blur_radius } - PictureKind::TextShadow { .. } => { - (false, None) + _ => { + 0.0 } }; @@ -1757,10 +1832,11 @@ impl PrimitiveStore { PictureContext { pipeline_id: pic.pipeline_id, prim_runs: mem::replace(&mut pic.runs, Vec::new()), - original_reference_frame_index, + original_reference_frame_index: Some(pic.reference_frame_index), display_list, inv_world_transform, - apply_local_clip_rect, + apply_local_clip_rect: pic.apply_local_clip_rect, + inflation_factor, } }; @@ -1788,7 +1864,14 @@ impl PrimitiveStore { return None; } - let local_rect = metadata.local_clip_rect.intersection(&metadata.local_rect); + // Inflate the local rect for this primitive by the inflation factor of + // the picture context. This ensures that even if the primitive itself + // is not visible, any effects from the blur radius will be correctly + // taken into account. + let local_rect = metadata + .local_rect + .inflate(pic_context.inflation_factor, pic_context.inflation_factor) + .intersection(&metadata.local_clip_rect); let local_rect = match local_rect { Some(local_rect) => local_rect, None => return None, diff --git a/gfx/webrender/src/render_backend.rs b/gfx/webrender/src/render_backend.rs index e61c600f91c2..1e4575d5c427 100644 --- a/gfx/webrender/src/render_backend.rs +++ b/gfx/webrender/src/render_backend.rs @@ -73,7 +73,7 @@ struct SceneData { removed_pipelines: Vec, } -#[derive(Copy, Clone, PartialEq, PartialOrd, Debug, Eq, Ord)] +#[derive(Copy, Clone, Hash, PartialEq, PartialOrd, Debug, Eq, Ord)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct FrameId(pub u32); diff --git a/gfx/webrender/src/render_task.rs b/gfx/webrender/src/render_task.rs index 035978fcb6f0..b393a4941825 100644 --- a/gfx/webrender/src/render_task.rs +++ b/gfx/webrender/src/render_task.rs @@ -9,25 +9,26 @@ use clip::{ClipSource, ClipStore, ClipWorkItem}; use clip_scroll_tree::CoordinateSystemId; use device::TextureFilter; use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle}; -use gpu_types::{ImageSource, PictureType, RasterizationSpace}; +use gpu_types::{ImageSource, RasterizationSpace}; use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture}; use prim_store::{PrimitiveIndex, ImageCacheKey}; #[cfg(feature = "debugger")] use print_tree::{PrintTreePrinter}; +use render_backend::FrameId; use resource_cache::{CacheItem, ResourceCache}; use std::{cmp, ops, usize, f32, i32}; use texture_cache::{TextureCache, TextureCacheHandle}; use tiling::{RenderPass, RenderTargetIndex}; use tiling::{RenderTargetKind}; -const FLOATS_PER_RENDER_TASK_INFO: usize = 12; +const FLOATS_PER_RENDER_TASK_INFO: usize = 8; pub const MAX_BLUR_STD_DEVIATION: f32 = 4.0; pub const MIN_DOWNSCALING_RT_SIZE: i32 = 128; #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct RenderTaskId(pub u32); // TODO(gw): Make private when using GPU cache! +pub struct RenderTaskId(pub u32, FrameId); // TODO(gw): Make private when using GPU cache! #[derive(Debug, Copy, Clone)] #[repr(C)] @@ -42,24 +43,27 @@ pub struct RenderTaskTree { pub tasks: Vec, pub task_data: Vec, next_saved: SavedTargetIndex, + frame_id: FrameId, } impl RenderTaskTree { - pub fn new() -> Self { + pub fn new(frame_id: FrameId) -> Self { RenderTaskTree { tasks: Vec::new(), task_data: Vec::new(), next_saved: SavedTargetIndex(0), + frame_id, } } pub fn add(&mut self, task: RenderTask) -> RenderTaskId { - let id = RenderTaskId(self.tasks.len() as u32); + let id = self.tasks.len(); self.tasks.push(task); - id + RenderTaskId(id as _, self.frame_id) } pub fn max_depth(&self, id: RenderTaskId, depth: usize, max_depth: &mut usize) { + debug_assert_eq!(self.frame_id, id.1); let depth = depth + 1; *max_depth = cmp::max(*max_depth, depth); let task = &self.tasks[id.0 as usize]; @@ -74,6 +78,7 @@ impl RenderTaskTree { pass_index: usize, passes: &mut Vec, ) { + debug_assert_eq!(self.frame_id, id.1); let task = &self.tasks[id.0 as usize]; for child in &task.children { @@ -106,6 +111,7 @@ impl RenderTaskTree { } pub fn get_task_address(&self, id: RenderTaskId) -> RenderTaskAddress { + debug_assert_eq!(self.frame_id, id.1); RenderTaskAddress(id.0) } @@ -125,12 +131,14 @@ impl RenderTaskTree { impl ops::Index for RenderTaskTree { type Output = RenderTask; fn index(&self, id: RenderTaskId) -> &RenderTask { + debug_assert_eq!(self.frame_id, id.1); &self.tasks[id.0 as usize] } } impl ops::IndexMut for RenderTaskTree { fn index_mut(&mut self, id: RenderTaskId) -> &mut RenderTask { + debug_assert_eq!(self.frame_id, id.1); &mut self.tasks[id.0 as usize] } } @@ -168,7 +176,6 @@ pub struct PictureTask { pub target_kind: RenderTargetKind, pub content_origin: DeviceIntPoint, pub color: PremultipliedColorF, - pub pic_type: PictureType, pub uv_rect_handle: GpuCacheHandle, } @@ -262,7 +269,6 @@ impl RenderTask { color: PremultipliedColorF, clear_mode: ClearMode, children: Vec, - pic_type: PictureType, ) -> Self { RenderTask { children, @@ -272,7 +278,6 @@ impl RenderTask { target_kind, content_origin, color, - pic_type, uv_rect_handle: GpuCacheHandle::new(), }), clear_mode, @@ -385,6 +390,7 @@ impl RenderTask { ClipSource::Rectangle(..) | ClipSource::RoundedRectangle(..) | ClipSource::Image(..) | + ClipSource::LineDecoration(..) | ClipSource::BorderCorner(..) => {} } } @@ -523,56 +529,41 @@ impl RenderTask { // more type-safe. Although, it will always need // to be kept in sync with the GLSL code anyway. - let (data1, data2) = match self.kind { + let data = match self.kind { RenderTaskKind::Picture(ref task) => { - ( - // Note: has to match `PICTURE_TYPE_*` in shaders - [ - task.content_origin.x as f32, - task.content_origin.y as f32, - task.pic_type as u32 as f32, - ], - task.color.to_array() - ) + // Note: has to match `PICTURE_TYPE_*` in shaders + [ + task.content_origin.x as f32, + task.content_origin.y as f32, + 0.0, + ] } RenderTaskKind::CacheMask(ref task) => { - ( - [ - task.actual_rect.origin.x as f32, - task.actual_rect.origin.y as f32, - RasterizationSpace::Screen as i32 as f32, - ], - [0.0; 4], - ) + [ + task.actual_rect.origin.x as f32, + task.actual_rect.origin.y as f32, + RasterizationSpace::Screen as i32 as f32, + ] } RenderTaskKind::ClipRegion(..) => { - ( - [ - 0.0, - 0.0, - RasterizationSpace::Local as i32 as f32, - ], - [0.0; 4], - ) + [ + 0.0, + 0.0, + RasterizationSpace::Local as i32 as f32, + ] } RenderTaskKind::VerticalBlur(ref task) | RenderTaskKind::HorizontalBlur(ref task) => { - ( - [ - task.blur_std_deviation, - 0.0, - 0.0, - ], - [0.0; 4], - ) + [ + task.blur_std_deviation, + 0.0, + 0.0, + ] } RenderTaskKind::Readback(..) | RenderTaskKind::Scaling(..) | RenderTaskKind::Blit(..) => { - ( - [0.0; 3], - [0.0; 4], - ) + [0.0; 3] } }; @@ -585,13 +576,9 @@ impl RenderTask { target_rect.size.width as f32, target_rect.size.height as f32, target_index.0 as f32, - data1[0], - data1[1], - data1[2], - data2[0], - data2[1], - data2[2], - data2[3], + data[0], + data[1], + data[2], ] } } diff --git a/gfx/webrender/src/renderer.rs b/gfx/webrender/src/renderer.rs index c415aba78c76..05f32d8e97c1 100644 --- a/gfx/webrender/src/renderer.rs +++ b/gfx/webrender/src/renderer.rs @@ -112,10 +112,6 @@ const GPU_TAG_BRUSH_SOLID: GpuProfileTag = GpuProfileTag { label: "B_Solid", color: debug_colors::RED, }; -const GPU_TAG_BRUSH_LINE: GpuProfileTag = GpuProfileTag { - label: "Line", - color: debug_colors::DARKRED, -}; const GPU_TAG_CACHE_CLIP: GpuProfileTag = GpuProfileTag { label: "C_Clip", color: debug_colors::PURPLE, @@ -209,7 +205,6 @@ impl BatchKind { BatchKind::Brush(kind) => { match kind { BrushBatchKind::Solid => "Brush (Solid)", - BrushBatchKind::Line => "Brush (Line)", BrushBatchKind::Image(..) => "Brush (Image)", BrushBatchKind::Blend => "Brush (Blend)", BrushBatchKind::MixBlend { .. } => "Brush (Composite)", @@ -229,7 +224,6 @@ impl BatchKind { BatchKind::Brush(kind) => { match kind { BrushBatchKind::Solid => GPU_TAG_BRUSH_SOLID, - BrushBatchKind::Line => GPU_TAG_BRUSH_LINE, BrushBatchKind::Image(..) => GPU_TAG_BRUSH_IMAGE, BrushBatchKind::Blend => GPU_TAG_BRUSH_BLEND, BrushBatchKind::MixBlend { .. } => GPU_TAG_BRUSH_MIXBLEND, @@ -1762,6 +1756,11 @@ impl Renderer { "BoxShadows", target.clip_batcher.box_shadows.len(), ); + debug_target.add( + debug_server::BatchKind::Clip, + "LineDecorations", + target.clip_batcher.line_decorations.len(), + ); debug_target.add( debug_server::BatchKind::Cache, "Vertical Blur", @@ -3085,6 +3084,22 @@ impl Renderer { ); } + // draw line decoration clips + if !target.clip_batcher.line_decorations.is_empty() { + let _gm2 = self.gpu_profile.start_marker("clip lines"); + self.shaders.cs_clip_line.bind( + &mut self.device, + projection, + &mut self.renderer_errors, + ); + self.draw_instanced_batch( + &target.clip_batcher.line_decorations, + VertexArrayKind::Clip, + &BatchTextures::no_texture(), + stats, + ); + } + // draw image masks for (mask_texture_id, items) in target.clip_batcher.images.iter() { let _gm2 = self.gpu_profile.start_marker("clip images"); diff --git a/gfx/webrender/src/shade.rs b/gfx/webrender/src/shade.rs index b2c248f986bd..e2d27b2c8012 100644 --- a/gfx/webrender/src/shade.rs +++ b/gfx/webrender/src/shade.rs @@ -418,7 +418,6 @@ pub struct Shaders { // Brush shaders brush_solid: BrushShader, - brush_line: BrushShader, brush_image: Vec>, brush_blend: BrushShader, brush_mix_blend: BrushShader, @@ -433,6 +432,7 @@ pub struct Shaders { pub cs_clip_box_shadow: LazilyCompiledShader, pub cs_clip_image: LazilyCompiledShader, pub cs_clip_border: LazilyCompiledShader, + pub cs_clip_line: LazilyCompiledShader, // The are "primitive shaders". These shaders draw and blend // final results on screen. They are aware of tile boundaries. @@ -464,13 +464,6 @@ impl Shaders { options.precache_shaders, )?; - let brush_line = BrushShader::new( - "brush_line", - device, - &[], - options.precache_shaders, - )?; - let brush_blend = BrushShader::new( "brush_blend", device, @@ -539,6 +532,14 @@ impl Shaders { options.precache_shaders, )?; + let cs_clip_line = LazilyCompiledShader::new( + ShaderKind::ClipCache, + "cs_clip_line", + &[], + device, + options.precache_shaders, + )?; + let cs_clip_image = LazilyCompiledShader::new( ShaderKind::ClipCache, "cs_clip_image", @@ -676,7 +677,6 @@ impl Shaders { cs_blur_a8, cs_blur_rgba8, brush_solid, - brush_line, brush_image, brush_blend, brush_mix_blend, @@ -687,6 +687,7 @@ impl Shaders { cs_clip_box_shadow, cs_clip_border, cs_clip_image, + cs_clip_line, ps_text_run, ps_text_run_dual_source, ps_image, @@ -724,9 +725,6 @@ impl Shaders { .as_mut() .expect("Unsupported image shader kind") } - BrushBatchKind::Line => { - &mut self.brush_line - } BrushBatchKind::Blend => { &mut self.brush_blend } @@ -775,7 +773,6 @@ impl Shaders { self.cs_blur_a8.deinit(device); self.cs_blur_rgba8.deinit(device); self.brush_solid.deinit(device); - self.brush_line.deinit(device); self.brush_blend.deinit(device); self.brush_mix_blend.deinit(device); self.brush_radial_gradient.deinit(device); @@ -784,6 +781,7 @@ impl Shaders { self.cs_clip_box_shadow.deinit(device); self.cs_clip_image.deinit(device); self.cs_clip_border.deinit(device); + self.cs_clip_line.deinit(device); self.ps_text_run.deinit(device); self.ps_text_run_dual_source.deinit(device); for shader in self.brush_image { diff --git a/gfx/webrender/src/tiling.rs b/gfx/webrender/src/tiling.rs index f025e6b6268f..523f3a409a54 100644 --- a/gfx/webrender/src/tiling.rs +++ b/gfx/webrender/src/tiling.rs @@ -11,9 +11,8 @@ use clip_scroll_tree::{ClipScrollTree, ClipScrollNodeIndex}; use device::{FrameId, Texture}; use gpu_cache::{GpuCache}; use gpu_types::{BlurDirection, BlurInstance}; -use gpu_types::{ClipScrollNodeData}; +use gpu_types::{ClipScrollNodeData, ZBufferIdGenerator}; use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture}; -use picture::PictureKind; use prim_store::{CachedGradient, PrimitiveIndex, PrimitiveKind, PrimitiveStore}; use prim_store::{BrushKind, DeferredResolve}; use profiler::FrameProfileCounters; @@ -314,6 +313,7 @@ impl RenderTarget for ColorRenderTarget { deferred_resolves: &mut Vec, ) { let mut merged_batches = AlphaBatchContainer::new(None); + let mut z_generator = ZBufferIdGenerator::new(); for task_id in &self.alpha_tasks { let task = &render_tasks[*task_id]; @@ -336,6 +336,7 @@ impl RenderTarget for ColorRenderTarget { gpu_cache, render_tasks, deferred_resolves, + &mut z_generator, ); if let Some(batch_container) = batch_builder.build(&mut merged_batches) { @@ -395,15 +396,13 @@ impl RenderTarget for ColorRenderTarget { self.alpha_tasks.push(task_id); - if let PictureKind::Image { frame_output_pipeline_id, .. } = pic.kind { - // If this pipeline is registered as a frame output - // store the information necessary to do the copy. - if let Some(pipeline_id) = frame_output_pipeline_id { - self.outputs.push(FrameOutput { - pipeline_id, - task_id, - }); - } + // If this pipeline is registered as a frame output + // store the information necessary to do the copy. + if let Some(pipeline_id) = pic.frame_output_pipeline_id { + self.outputs.push(FrameOutput { + pipeline_id, + task_id, + }); } } _ => { diff --git a/gfx/webrender/tests/angle_shader_validation.rs b/gfx/webrender/tests/angle_shader_validation.rs index 5f065711905d..3f314fa881b6 100644 --- a/gfx/webrender/tests/angle_shader_validation.rs +++ b/gfx/webrender/tests/angle_shader_validation.rs @@ -40,6 +40,10 @@ const SHADERS: &[Shader] = &[ name: "cs_clip_border", features: CLIP_FEATURES, }, + Shader { + name: "cs_clip_line", + features: CLIP_FEATURES, + }, // Cache shaders Shader { name: "cs_blur", @@ -91,10 +95,6 @@ const SHADERS: &[Shader] = &[ name: "brush_composite", features: &[], }, - Shader { - name: "brush_line", - features: &[], - }, Shader { name: "brush_radial_gradient", features: &[ "DITHERING" ], diff --git a/gfx/webrender_api/Cargo.toml b/gfx/webrender_api/Cargo.toml index 0a2a4ee9563b..ee2715c9cb55 100644 --- a/gfx/webrender_api/Cargo.toml +++ b/gfx/webrender_api/Cargo.toml @@ -18,8 +18,8 @@ bitflags = "1.0" byteorder = "1.2.1" ipc-channel = {version = "0.10.0", optional = true} euclid = { version = "0.17", features = ["serde"] } -serde = { version = "=1.0.27", features = ["rc"] } -serde_derive = { version = "=1.0.27", features = ["deserialize_in_place"] } +serde = { version = "=1.0.35", features = ["rc"] } +serde_derive = { version = "=1.0.35", features = ["deserialize_in_place"] } time = "0.1" [target.'cfg(target_os = "macos")'.dependencies] diff --git a/gfx/webrender_bindings/revision.txt b/gfx/webrender_bindings/revision.txt index 8956782be6b1..b8046b48fa12 100644 --- a/gfx/webrender_bindings/revision.txt +++ b/gfx/webrender_bindings/revision.txt @@ -1 +1 @@ -30cfecc343e407ce277d07cf09f27ad9dd1917a1 +22493328352ba432a7cd89491d81bfaa19bc1bce diff --git a/gfx/wrench/src/main.rs b/gfx/wrench/src/main.rs index b2c5ed30deca..f55ae16da5f6 100644 --- a/gfx/wrench/src/main.rs +++ b/gfx/wrench/src/main.rs @@ -349,8 +349,8 @@ impl RenderNotifier for Notifier { self.tx.send(NotifierEvent::ShutDown).unwrap(); } - fn new_document_ready(&self, _: DocumentId, scrolled: bool, _composite_needed: bool) { - if !scrolled { + fn new_document_ready(&self, _: DocumentId, _scrolled: bool, composite_needed: bool) { + if composite_needed { self.wake_up(); } } @@ -504,6 +504,7 @@ fn main() { let mut show_help = false; let mut do_loop = false; let mut cpu_profile_index = 0; + let mut cursor_position = WorldPoint::zero(); let dim = window.get_inner_size(); wrench.update(dim); @@ -527,13 +528,14 @@ fn main() { glutin::WindowEvent::Closed => { return glutin::ControlFlow::Break; } - glutin::WindowEvent::Refresh | - glutin::WindowEvent::Focused(..) | - glutin::WindowEvent::CursorMoved { .. } => { + glutin::WindowEvent::Focused(..) => { + do_render = true; + } + glutin::WindowEvent::CursorMoved { position: (x, y), .. } => { + cursor_position = WorldPoint::new(x as f32, y as f32); do_render = true; } - glutin::WindowEvent::KeyboardInput { input: glutin::KeyboardInput { state: glutin::ElementState::Pressed, @@ -614,6 +616,20 @@ fn main() { wrench.set_page_zoom(new_zoom_factor); do_frame = true; } + VirtualKeyCode::X => { + let results = wrench.api.hit_test( + wrench.document_id, + None, + cursor_position, + HitTestFlags::FIND_ALL + ); + + println!("Hit test results:"); + for item in &results.items { + println!(" • {:?}", item); + } + println!(""); + } _ => {} } _ => {} diff --git a/gfx/wrench/src/wrench.rs b/gfx/wrench/src/wrench.rs index c067a46bb206..6b21aa37e001 100644 --- a/gfx/wrench/src/wrench.rs +++ b/gfx/wrench/src/wrench.rs @@ -515,27 +515,11 @@ impl Wrench { false, ); } - // TODO(nical) - Need to separate the set_display_list from the scrolling - // operations into separate transactions for mysterious -but probably related - // to the other comment below- reasons. - self.api.send_transaction(self.document_id, txn); - let mut txn = Transaction::new(); for (id, offset) in scroll_offsets { txn.scroll_node_with_id(*offset, *id, ScrollClamping::NoClamping); } - // TODO(nical) - Wrench does not notify frames when there was scrolling - // in the transaction (See RenderNotifier implementations). If we don't - // generate a frame after scrolling, wrench just stops and some tests - // will time out. - // I suppose this was to avoid taking the snapshot after scrolling if - // there was other updates coming in a subsequent messages but it's very - // error-prone with transactions. - // For now just send two transactions to avoid the deadlock, but we should - // figure this out. - self.api.send_transaction(self.document_id, txn); - let mut txn = Transaction::new(); txn.generate_frame(); self.api.send_transaction(self.document_id, txn); } @@ -575,6 +559,7 @@ impl Wrench { "M - Trigger memory pressure event", "T - Save CPU profile to a file", "C - Save a capture to captures/wrench/", + "X - Do a hit test at the current cursor position", ]; let color_and_offset = [(*BLACK_COLOR, 2.0), (*WHITE_COLOR, 0.0)]; diff --git a/gfx/wrench/src/yaml_frame_reader.rs b/gfx/wrench/src/yaml_frame_reader.rs index 7da8ed3cbdba..3af5b1e0774d 100644 --- a/gfx/wrench/src/yaml_frame_reader.rs +++ b/gfx/wrench/src/yaml_frame_reader.rs @@ -373,6 +373,19 @@ impl YamlFrameReader { } } + fn to_hit_testing_tag(&self, item: &Yaml) -> Option { + match *item { + Yaml::Array(ref array) if array.len() == 2 => { + match (array[0].as_i64(), array[1].as_i64()) { + (Some(first), Some(second)) => Some((first as u64, second as u16)), + _ => None, + } + } + _ => None, + } + + } + pub fn add_or_get_image( &mut self, file: &Path, @@ -387,7 +400,7 @@ impl YamlFrameReader { if self.list_resources { println!("{}", file.to_string_lossy()); } let (descriptor, image_data) = match image::open(file) { Ok(image) => { - let image_dims = image.dimensions(); + let (image_width, image_height) = image.dimensions(); let (format, bytes) = match image { image::ImageLuma8(_) => { (ImageFormat::R8, image.raw_pixels()) @@ -399,7 +412,7 @@ impl YamlFrameReader { } image::ImageRgb8(_) => { let bytes = image.raw_pixels(); - let mut pixels = Vec::new(); + let mut pixels = Vec::with_capacity(image_width as usize * image_height as usize * 4); for bgr in bytes.chunks(3) { pixels.extend_from_slice(&[ bgr[2], @@ -413,8 +426,8 @@ impl YamlFrameReader { _ => panic!("We don't support whatever your crazy image type is, come on"), }; let descriptor = ImageDescriptor::new( - image_dims.0, - image_dims.1, + image_width, + image_height, format, is_image_opaque(format, &bytes[..]), self.allow_mipmaps, @@ -1290,6 +1303,8 @@ impl YamlFrameReader { let mut info = LayoutPrimitiveInfo::with_clip_rect(LayoutRect::zero(), clip_rect); info.is_backface_visible = item["backface-visible"].as_bool().unwrap_or(true);; + info.tag = self.to_hit_testing_tag(&item["hit-testing-tag"]); + match item_type { "rect" => self.handle_rect(dl, item, &mut info), "clear-rect" => self.handle_clear_rect(dl, item, &mut info), diff --git a/gfx/wrench/src/yaml_frame_writer.rs b/gfx/wrench/src/yaml_frame_writer.rs index c9f875a58add..1c746ce8153d 100644 --- a/gfx/wrench/src/yaml_frame_writer.rs +++ b/gfx/wrench/src/yaml_frame_writer.rs @@ -687,9 +687,17 @@ impl YamlFrameWriter { }; let mut v = new_table(); - rect_node(&mut v, "bounds", &base.rect()); + let info = base.get_layer_primitive_info(&LayoutVector2D::zero()); + rect_node(&mut v, "bounds", &info.rect); + rect_node(&mut v, "clip-rect", &info.clip_rect); - rect_node(&mut v, "clip-rect", base.clip_rect()); + if let Some(tag) = info.tag { + yaml_node( + &mut v, + "hit-testing-tag", + Yaml::Array(vec![Yaml::Integer(tag.0 as i64), Yaml::Integer(tag.1 as i64)]) + ); + } let clip_and_scroll_yaml = match clip_id_mapper.map_info(&base.clip_and_scroll()) { (scroll_id, Some(clip_id)) => { @@ -1000,7 +1008,6 @@ impl YamlFrameWriter { Clip(item) => { str_node(&mut v, "type", "clip"); usize_node(&mut v, "id", clip_id_mapper.add_id(item.id)); - size_node(&mut v, "content-size", &base.rect().size); let (complex_clips, complex_clip_count) = base.complex_clip(); if let Some(complex) = self.make_complex_clips_node(