diff --git a/webrender/res/brush.glsl b/webrender/res/brush.glsl index e48f586498..ac1d57d077 100644 --- a/webrender/res/brush.glsl +++ b/webrender/res/brush.glsl @@ -20,7 +20,7 @@ void brush_vs( struct BrushInstance { int picture_address; int prim_address; - int clip_node_id; + int clip_chain_rect_index; int scroll_node_id; int clip_address; int z; @@ -29,11 +29,11 @@ struct BrushInstance { }; BrushInstance load_brush() { - BrushInstance bi; + BrushInstance bi; bi.picture_address = aData0.x; bi.prim_address = aData0.y; - bi.clip_node_id = aData0.z / 65536; + bi.clip_chain_rect_index = aData0.z / 65536; bi.scroll_node_id = aData0.z % 65536; bi.clip_address = aData0.w; bi.z = aData1.x; @@ -48,13 +48,14 @@ struct BrushPrimitive { RectWithSize local_clip_rect; }; -BrushPrimitive fetch_brush_primitive(int address) { +BrushPrimitive fetch_brush_primitive(int address, int clip_chain_rect_index) { vec4 data[2] = fetch_from_resource_cache_2(address); - BrushPrimitive prim = BrushPrimitive( - RectWithSize(data[0].xy, data[0].zw), - RectWithSize(data[1].xy, data[1].zw) - ); + RectWithSize clip_chain_rect = fetch_clip_chain_rect(clip_chain_rect_index); + RectWithSize brush_clip_rect = RectWithSize(data[1].xy, data[1].zw); + RectWithSize clip_rect = intersect_rects(clip_chain_rect, brush_clip_rect); + + BrushPrimitive prim = BrushPrimitive(RectWithSize(data[0].xy, data[0].zw), clip_rect); return prim; } @@ -66,7 +67,8 @@ void main(void) { // Load the geometry for this brush. For now, this is simply the // local rect of the primitive. In the future, this will support // loading segment rects, and other rect formats (glyphs). - BrushPrimitive brush_prim = fetch_brush_primitive(brush.prim_address); + BrushPrimitive brush_prim = + fetch_brush_primitive(brush.prim_address, brush.clip_chain_rect_index); // Fetch the segment of this brush primitive we are drawing. int segment_address = brush.prim_address + @@ -95,16 +97,16 @@ void main(void) { gl_Position = uTransform * vec4(device_pos, 0.0, 1.0); } else { VertexInfo vi; - Layer layer = fetch_layer(brush.clip_node_id, brush.scroll_node_id); + ClipScrollNode scroll_node = fetch_clip_scroll_node(brush.scroll_node_id); ClipArea clip_area = fetch_clip_area(brush.clip_address); // Write the normal vertex information out. - if (layer.is_axis_aligned) { + if (scroll_node.is_axis_aligned) { vi = write_vertex( local_segment_rect, brush_prim.local_clip_rect, float(brush.z), - layer, + scroll_node, pic_task, brush_prim.local_rect ); @@ -125,7 +127,7 @@ void main(void) { brush_prim.local_clip_rect, edge_aa_segment_mask, float(brush.z), - layer, + scroll_node, pic_task ); } diff --git a/webrender/res/clip_shared.glsl b/webrender/res/clip_shared.glsl index 90da9e7799..7da359ffdb 100644 --- a/webrender/res/clip_shared.glsl +++ b/webrender/res/clip_shared.glsl @@ -11,13 +11,13 @@ #define SEGMENT_CORNER_BR 4 in int aClipRenderTaskAddress; -in int aClipLayerAddress; +in int aScrollNodeId; in int aClipSegment; in ivec4 aClipDataResourceAddress; struct ClipMaskInstance { int render_task_address; - int layer_address; + int scroll_node_id; int segment; ivec2 clip_data_address; ivec2 resource_address; @@ -27,7 +27,7 @@ ClipMaskInstance fetch_clip_item() { ClipMaskInstance cmi; cmi.render_task_address = aClipRenderTaskAddress; - cmi.layer_address = aClipLayerAddress; + cmi.scroll_node_id = aScrollNodeId; cmi.segment = aClipSegment; cmi.clip_data_address = aClipDataResourceAddress.xy; cmi.resource_address = aClipDataResourceAddress.zw; @@ -49,13 +49,13 @@ RectWithSize intersect_rect(RectWithSize a, RectWithSize b) { // The transformed vertex function that always covers the whole clip area, // which is the intersection of all clip instances of a given primitive ClipVertexInfo write_clip_tile_vertex(RectWithSize local_clip_rect, - Layer layer, + ClipScrollNode scroll_node, ClipArea area) { vec2 actual_pos = area.screen_origin + aPosition.xy * area.common_data.task_rect.size; - vec4 layer_pos = get_layer_pos(actual_pos / uDevicePixelRatio, layer); + vec4 node_pos = get_node_pos(actual_pos / uDevicePixelRatio, scroll_node); - // compute the point position in side the layer, in CSS space + // compute the point position inside the scroll node, in CSS space vec2 vertex_pos = actual_pos + area.common_data.task_rect.p0 - area.screen_origin; @@ -64,7 +64,7 @@ ClipVertexInfo write_clip_tile_vertex(RectWithSize local_clip_rect, vLocalBounds = vec4(local_clip_rect.p0, local_clip_rect.p0 + local_clip_rect.size); - ClipVertexInfo vi = ClipVertexInfo(layer_pos.xyw, actual_pos, local_clip_rect); + ClipVertexInfo vi = ClipVertexInfo(node_pos.xyw, actual_pos, local_clip_rect); return vi; } diff --git a/webrender/res/cs_clip_border.glsl b/webrender/res/cs_clip_border.glsl index e2b5ae3b2a..a82708ffa4 100644 --- a/webrender/res/cs_clip_border.glsl +++ b/webrender/res/cs_clip_border.glsl @@ -64,7 +64,7 @@ BorderClipDot fetch_border_clip_dot(ivec2 address, int segment) { void main(void) { ClipMaskInstance cmi = fetch_clip_item(); ClipArea area = fetch_clip_area(cmi.render_task_address); - Layer layer = fetch_layer(cmi.layer_address, cmi.layer_address); + ClipScrollNode scroll_node = fetch_clip_scroll_node(cmi.scroll_node_id); // Fetch the header information for this corner clip. BorderCorner corner = fetch_border_corner(cmi.clip_data_address); @@ -120,7 +120,7 @@ void main(void) { vec2 pos = corner.rect.p0 + aPosition.xy * corner.rect.size; // Transform to world pos - vec4 world_pos = layer.transform * vec4(pos, 0.0, 1.0); + vec4 world_pos = scroll_node.transform * vec4(pos, 0.0, 1.0); world_pos.xyz /= world_pos.w; // Scale into device pixels. @@ -132,8 +132,8 @@ void main(void) { area.common_data.task_rect.p0; // Calculate the local space position for this vertex. - vec4 layer_pos = get_layer_pos(world_pos.xy, layer); - vPos = layer_pos.xyw; + vec4 node_pos = get_node_pos(world_pos.xy, scroll_node); + vPos = node_pos.xyw; gl_Position = uTransform * vec4(final_pos, 0.0, 1.0); } diff --git a/webrender/res/cs_clip_image.glsl b/webrender/res/cs_clip_image.glsl index 009896e17c..d8d703f16c 100644 --- a/webrender/res/cs_clip_image.glsl +++ b/webrender/res/cs_clip_image.glsl @@ -24,13 +24,13 @@ ImageMaskData fetch_mask_data(ivec2 address) { void main(void) { ClipMaskInstance cmi = fetch_clip_item(); ClipArea area = fetch_clip_area(cmi.render_task_address); - Layer layer = fetch_layer(cmi.layer_address, cmi.layer_address); + ClipScrollNode scroll_node = fetch_clip_scroll_node(cmi.scroll_node_id); ImageMaskData mask = fetch_mask_data(cmi.clip_data_address); RectWithSize local_rect = mask.local_rect; ImageResource res = fetch_image_resource_direct(cmi.resource_address); ClipVertexInfo vi = write_clip_tile_vertex(local_rect, - layer, + scroll_node, area); vPos = vi.local_pos; diff --git a/webrender/res/cs_clip_rectangle.glsl b/webrender/res/cs_clip_rectangle.glsl index c1a985ce1f..2dafddab0f 100644 --- a/webrender/res/cs_clip_rectangle.glsl +++ b/webrender/res/cs_clip_rectangle.glsl @@ -58,13 +58,11 @@ ClipData fetch_clip(ivec2 address) { void main(void) { ClipMaskInstance cmi = fetch_clip_item(); ClipArea area = fetch_clip_area(cmi.render_task_address); - Layer layer = fetch_layer(cmi.layer_address, cmi.layer_address); + ClipScrollNode scroll_node = fetch_clip_scroll_node(cmi.scroll_node_id); ClipData clip = fetch_clip(cmi.clip_data_address); RectWithSize local_rect = clip.rect.rect; - ClipVertexInfo vi = write_clip_tile_vertex(local_rect, - layer, - area); + ClipVertexInfo vi = write_clip_tile_vertex(local_rect, scroll_node, area); vPos = vi.local_pos; vClipMode = clip.rect.mode.x; diff --git a/webrender/res/prim_shared.glsl b/webrender/res/prim_shared.glsl index 153cab7767..193dcd79c7 100644 --- a/webrender/res/prim_shared.glsl +++ b/webrender/res/prim_shared.glsl @@ -69,7 +69,8 @@ vec4[2] fetch_from_resource_cache_2(int address) { #ifdef WR_VERTEX_SHADER -#define VECS_PER_LAYER 7 +#define VECS_PER_CLIP_SCROLL_NODE 5 +#define VECS_PER_LOCAL_CLIP_RECT 1 #define VECS_PER_RENDER_TASK 3 #define VECS_PER_PRIM_HEADER 2 #define VECS_PER_TEXT_RUN 3 @@ -77,6 +78,7 @@ vec4[2] fetch_from_resource_cache_2(int address) { #define VECS_PER_GRADIENT_STOP 2 uniform HIGHP_SAMPLER_FLOAT sampler2D sClipScrollNodes; +uniform HIGHP_SAMPLER_FLOAT sampler2D sLocalClipRects; uniform HIGHP_SAMPLER_FLOAT sampler2D sRenderTasks; // Instanced attributes @@ -143,9 +145,6 @@ vec4 fetch_from_resource_cache_1(int address) { struct ClipScrollNode { mat4 transform; - vec4 local_clip_rect; - vec2 reference_frame_relative_scroll_offset; - vec2 scroll_offset; bool is_axis_aligned; }; @@ -156,7 +155,7 @@ ClipScrollNode fetch_clip_scroll_node(int index) { // This is required because trying to use an offset // of more than 8 texels doesn't work on some versions // of OSX. - ivec2 uv = get_fetch_uv(index, VECS_PER_LAYER); + ivec2 uv = get_fetch_uv(index, VECS_PER_CLIP_SCROLL_NODE); ivec2 uv0 = ivec2(uv.x + 0, uv.y); node.transform[0] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(0, 0)); @@ -164,41 +163,16 @@ ClipScrollNode fetch_clip_scroll_node(int index) { node.transform[2] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(2, 0)); node.transform[3] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(3, 0)); - vec4 clip_rect = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(4, 0)); - node.local_clip_rect = clip_rect; - - vec4 offsets = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(5, 0)); - node.reference_frame_relative_scroll_offset = offsets.xy; - node.scroll_offset = offsets.zw; - - vec4 misc = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(6, 0)); + vec4 misc = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(4, 0)); node.is_axis_aligned = misc.x == 0.0; return node; } -struct Layer { - mat4 transform; - RectWithSize local_clip_rect; - bool is_axis_aligned; -}; - -Layer fetch_layer(int clip_node_id, int scroll_node_id) { - ClipScrollNode clip_node = fetch_clip_scroll_node(clip_node_id); - ClipScrollNode scroll_node = fetch_clip_scroll_node(scroll_node_id); - - Layer layer; - layer.transform = scroll_node.transform; - - vec4 local_clip_rect = clip_node.local_clip_rect; - local_clip_rect.xy += clip_node.reference_frame_relative_scroll_offset; - local_clip_rect.xy -= scroll_node.reference_frame_relative_scroll_offset; - local_clip_rect.xy -= scroll_node.scroll_offset; - - layer.local_clip_rect = RectWithSize(local_clip_rect.xy, local_clip_rect.zw); - layer.is_axis_aligned = scroll_node.is_axis_aligned; - - return layer; +RectWithSize fetch_clip_chain_rect(int index) { + ivec2 uv = get_fetch_uv(index, VECS_PER_LOCAL_CLIP_RECT); + vec4 rect = TEXEL_FETCH(sLocalClipRects, uv, 0, ivec2(0, 0)); + return RectWithSize(rect.xy, rect.zw); } struct RenderTaskCommonData { @@ -401,7 +375,7 @@ struct PrimitiveInstance { int render_task_index; int clip_task_index; int scroll_node_id; - int clip_node_id; + int clip_chain_rect_index; int z; int user_data0; int user_data1; @@ -415,7 +389,7 @@ PrimitiveInstance fetch_prim_instance() { pi.specific_prim_address = pi.prim_address + VECS_PER_PRIM_HEADER; pi.render_task_index = aData0.y; pi.clip_task_index = aData0.z; - pi.clip_node_id = aData0.w / 65536; + pi.clip_chain_rect_index = aData0.w / 65536; pi.scroll_node_id = aData0.w % 65536; pi.z = aData1.x; pi.user_data0 = aData1.y; @@ -453,7 +427,7 @@ CompositeInstance fetch_composite_instance() { } struct Primitive { - Layer layer; + ClipScrollNode scroll_node; ClipArea clip_area; PictureTask task; RectWithSize local_rect; @@ -481,13 +455,15 @@ Primitive load_primitive() { Primitive prim; - prim.layer = fetch_layer(pi.clip_node_id, pi.scroll_node_id); + prim.scroll_node = fetch_clip_scroll_node(pi.scroll_node_id); prim.clip_area = fetch_clip_area(pi.clip_task_index); prim.task = fetch_picture_task(pi.render_task_index); + RectWithSize clip_chain_rect = fetch_clip_chain_rect(pi.clip_chain_rect_index); + PrimitiveGeometry geom = fetch_primitive_geometry(pi.prim_address); prim.local_rect = geom.local_rect; - prim.local_clip_rect = geom.local_clip_rect; + prim.local_clip_rect = intersect_rects(clip_chain_rect, geom.local_clip_rect); prim.specific_prim_address = pi.specific_prim_address; prim.user_data0 = pi.user_data0; @@ -515,50 +491,50 @@ bool ray_plane(vec3 normal, vec3 point, vec3 ray_origin, vec3 ray_dir, out float // Apply the inverse transform "inv_transform" // to the reference point "ref" in CSS space, -// producing a local point on a layer plane, +// producing a local point on a ClipScrollNode plane, // set by a base point "a" and a normal "n". vec4 untransform(vec2 ref, vec3 n, vec3 a, mat4 inv_transform) { vec3 p = vec3(ref, -10000.0); vec3 d = vec3(0, 0, 1.0); float t = 0.0; - // get an intersection of the layer plane with Z axis vector, + // get an intersection of the ClipScrollNode plane with Z axis vector, // originated from the "ref" point ray_plane(n, a, p, d, t); - float z = p.z + d.z * t; // Z of the visible point on the layer + float z = p.z + d.z * t; // Z of the visible point on the ClipScrollNode vec4 r = inv_transform * vec4(ref, z, 1.0); return r; } -// Given a CSS space position, transform it back into the layer space. -vec4 get_layer_pos(vec2 pos, Layer layer) { - // get a point on the layer plane - vec4 ah = layer.transform * vec4(0.0, 0.0, 0.0, 1.0); +// Given a CSS space position, transform it back into the ClipScrollNode space. +vec4 get_node_pos(vec2 pos, ClipScrollNode node) { + // get a point on the scroll node plane + vec4 ah = node.transform * vec4(0.0, 0.0, 0.0, 1.0); vec3 a = ah.xyz / ah.w; - // get the normal to the layer plane - mat4 inv_transform = inverse(layer.transform); + // get the normal to the scroll node plane + mat4 inv_transform = inverse(node.transform); vec3 n = transpose(mat3(inv_transform)) * vec3(0.0, 0.0, 1.0); return untransform(pos, n, a, inv_transform); } // Compute a snapping offset in world space (adjusted to pixel ratio), -// given local position on the layer and a snap rectangle. +// given local position on the scroll_node and a snap rectangle. vec2 compute_snap_offset(vec2 local_pos, - Layer layer, + ClipScrollNode scroll_node, RectWithSize snap_rect) { // Ensure that the snap rect is at *least* one device pixel in size. // TODO(gw): It's not clear to me that this is "correct". Specifically, // how should it interact with sub-pixel snap rects when there - // is a layer transform with scale present? But it does fix + // is a scroll_node transform with scale present? But it does fix // the test cases we have in Servo that are failing without it // and seem better than not having this at all. snap_rect.size = max(snap_rect.size, vec2(1.0 / uDevicePixelRatio)); // Transform the snap corners to the world space. - vec4 world_snap_p0 = layer.transform * vec4(snap_rect.p0, 0.0, 1.0); - vec4 world_snap_p1 = layer.transform * vec4(snap_rect.p0 + snap_rect.size, 0.0, 1.0); + vec4 world_snap_p0 = scroll_node.transform * vec4(snap_rect.p0, 0.0, 1.0); + vec4 world_snap_p1 = scroll_node.transform * vec4(snap_rect.p0 + snap_rect.size, 0.0, 1.0); // Snap bounds in world coordinates, adjusted for pixel ratio. XY = top left, ZW = bottom right vec4 world_snap = uDevicePixelRatio * vec4(world_snap_p0.xy, world_snap_p1.xy) / vec4(world_snap_p0.ww, world_snap_p1.ww); @@ -579,7 +555,7 @@ struct VertexInfo { VertexInfo write_vertex(RectWithSize instance_rect, RectWithSize local_clip_rect, float z, - Layer layer, + ClipScrollNode scroll_node, PictureTask task, RectWithSize snap_rect) { @@ -587,13 +563,13 @@ VertexInfo write_vertex(RectWithSize instance_rect, vec2 local_pos = instance_rect.p0 + instance_rect.size * aPosition.xy; // Clamp to the two local clip rects. - vec2 clamped_local_pos = clamp_rect(clamp_rect(local_pos, local_clip_rect), layer.local_clip_rect); + vec2 clamped_local_pos = clamp_rect(local_pos, local_clip_rect); /// Compute the snapping offset. - vec2 snap_offset = compute_snap_offset(clamped_local_pos, layer, snap_rect); + vec2 snap_offset = compute_snap_offset(clamped_local_pos, scroll_node, snap_rect); // Transform the current vertex to world space. - vec4 world_pos = layer.transform * vec4(clamped_local_pos, 0.0, 1.0); + vec4 world_pos = scroll_node.transform * vec4(clamped_local_pos, 0.0, 1.0); // Convert the world positions to device pixel space. vec2 device_pos = world_pos.xy / world_pos.w * uDevicePixelRatio; @@ -633,19 +609,15 @@ VertexInfo write_transform_vertex(RectWithSize local_segment_rect, RectWithSize local_clip_rect, vec4 clip_edge_mask, float z, - Layer layer, + ClipScrollNode scroll_node, PictureTask task) { - // Calculate a clip rect from local clip + layer clip. + // Calculate a clip rect from local_rect + local clip RectWithEndpoint clip_rect = to_rect_with_endpoint(local_clip_rect); - clip_rect.p0 = clamp_rect(clip_rect.p0, layer.local_clip_rect); - clip_rect.p1 = clamp_rect(clip_rect.p1, layer.local_clip_rect); - - // Calculate a clip rect from local_rect + local clip + layer clip. RectWithEndpoint segment_rect = to_rect_with_endpoint(local_segment_rect); segment_rect.p0 = clamp(segment_rect.p0, clip_rect.p0, clip_rect.p1); segment_rect.p1 = clamp(segment_rect.p1, clip_rect.p0, clip_rect.p1); - // Calculate a clip rect from local_rect + local clip + layer clip. + // Calculate a clip rect from local_rect + local clip RectWithEndpoint prim_rect = to_rect_with_endpoint(local_prim_rect); prim_rect.p0 = clamp(prim_rect.p0, clip_rect.p0, clip_rect.p1); prim_rect.p1 = clamp(prim_rect.p1, clip_rect.p0, clip_rect.p1); @@ -666,7 +638,7 @@ VertexInfo write_transform_vertex(RectWithSize local_segment_rect, vec2 local_pos = local_segment_rect.p0 + local_segment_rect.size * aPosition.xy; // Transform the current vertex to the world cpace. - vec4 world_pos = layer.transform * vec4(local_pos, 0.0, 1.0); + vec4 world_pos = scroll_node.transform * vec4(local_pos, 0.0, 1.0); // Convert the world positions to device pixel space. vec2 device_pos = world_pos.xy / world_pos.w * uDevicePixelRatio; @@ -698,7 +670,7 @@ VertexInfo write_transform_vertex_primitive(Primitive prim) { prim.local_clip_rect, vec4(0.0), prim.z, - prim.layer, + prim.scroll_node, prim.task ); } diff --git a/webrender/res/ps_angle_gradient.glsl b/webrender/res/ps_angle_gradient.glsl index 7056fc5c2b..81abc6bc4d 100644 --- a/webrender/res/ps_angle_gradient.glsl +++ b/webrender/res/ps_angle_gradient.glsl @@ -23,7 +23,7 @@ void main(void) { VertexInfo vi = write_vertex(prim.local_rect, prim.local_clip_rect, prim.z, - prim.layer, + prim.scroll_node, prim.task, prim.local_rect); diff --git a/webrender/res/ps_border_corner.glsl b/webrender/res/ps_border_corner.glsl index 1064543fb9..bea4ffcfea 100644 --- a/webrender/res/ps_border_corner.glsl +++ b/webrender/res/ps_border_corner.glsl @@ -303,13 +303,13 @@ void main(void) { prim.local_clip_rect, vec4(1.0), prim.z, - prim.layer, + prim.scroll_node, prim.task); #else VertexInfo vi = write_vertex(segment_rect, prim.local_clip_rect, prim.z, - prim.layer, + prim.scroll_node, prim.task, prim.local_rect); #endif diff --git a/webrender/res/ps_border_edge.glsl b/webrender/res/ps_border_edge.glsl index b7a0087217..7dc0708285 100644 --- a/webrender/res/ps_border_edge.glsl +++ b/webrender/res/ps_border_edge.glsl @@ -221,13 +221,13 @@ void main(void) { prim.local_clip_rect, vec4(1.0), prim.z, - prim.layer, + prim.scroll_node, prim.task); #else VertexInfo vi = write_vertex(segment_rect, prim.local_clip_rect, prim.z, - prim.layer, + prim.scroll_node, prim.task, prim.local_rect); #endif diff --git a/webrender/res/ps_gradient.glsl b/webrender/res/ps_gradient.glsl index 1aec09fc9d..390a250581 100644 --- a/webrender/res/ps_gradient.glsl +++ b/webrender/res/ps_gradient.glsl @@ -72,7 +72,7 @@ void main(void) { prim.local_clip_rect, vec4(1.0), prim.z, - prim.layer, + prim.scroll_node, prim.task); vLocalPos = vi.local_pos; vec2 f = (vi.local_pos.xy - prim.local_rect.p0) / prim.local_rect.size; @@ -80,7 +80,7 @@ void main(void) { VertexInfo vi = write_vertex(segment_rect, prim.local_clip_rect, prim.z, - prim.layer, + prim.scroll_node, prim.task, prim.local_rect); diff --git a/webrender/res/ps_image.glsl b/webrender/res/ps_image.glsl index cfff1c3162..fe36b9aaf1 100644 --- a/webrender/res/ps_image.glsl +++ b/webrender/res/ps_image.glsl @@ -34,7 +34,7 @@ void main(void) { VertexInfo vi = write_vertex(prim.local_rect, prim.local_clip_rect, prim.z, - prim.layer, + prim.scroll_node, prim.task, prim.local_rect); vLocalPos = vi.local_pos - prim.local_rect.p0; diff --git a/webrender/res/ps_line.glsl b/webrender/res/ps_line.glsl index d583d52785..dc4f4286f0 100644 --- a/webrender/res/ps_line.glsl +++ b/webrender/res/ps_line.glsl @@ -114,7 +114,7 @@ void main(void) { VertexInfo vi = write_vertex(prim.local_rect, prim.local_clip_rect, prim.z, - prim.layer, + prim.scroll_node, prim.task, prim.local_rect); #endif diff --git a/webrender/res/ps_radial_gradient.glsl b/webrender/res/ps_radial_gradient.glsl index f7d65485f5..46483ee5b0 100644 --- a/webrender/res/ps_radial_gradient.glsl +++ b/webrender/res/ps_radial_gradient.glsl @@ -25,7 +25,7 @@ void main(void) { VertexInfo vi = write_vertex(prim.local_rect, prim.local_clip_rect, prim.z, - prim.layer, + prim.scroll_node, prim.task, prim.local_rect); diff --git a/webrender/res/ps_text_run.glsl b/webrender/res/ps_text_run.glsl index 6d49745c45..3c0dd90694 100644 --- a/webrender/res/ps_text_run.glsl +++ b/webrender/res/ps_text_run.glsl @@ -29,11 +29,11 @@ varying vec4 vUvClip; VertexInfo write_text_vertex(vec2 clamped_local_pos, RectWithSize local_clip_rect, float z, - Layer layer, + ClipScrollNode scroll_node, PictureTask task, RectWithSize snap_rect) { // Transform the current vertex to world space. - vec4 world_pos = layer.transform * vec4(clamped_local_pos, 0.0, 1.0); + vec4 world_pos = scroll_node.transform * vec4(clamped_local_pos, 0.0, 1.0); // Convert the world positions to device pixel space. vec2 device_pos = world_pos.xy / world_pos.w * uDevicePixelRatio; @@ -45,12 +45,12 @@ VertexInfo write_text_vertex(vec2 clamped_local_pos, #ifdef WR_FEATURE_GLYPH_TRANSFORM // For transformed subpixels, we just need to align the glyph origin to a device pixel. - // Only check the layer transform's translation since the scales and axes match. - vec2 world_snap_p0 = snap_rect.p0 + layer.transform[3].xy * uDevicePixelRatio; + // Only check the scroll node transform's translation since the scales and axes match. + vec2 world_snap_p0 = snap_rect.p0 + scroll_node.transform[3].xy * uDevicePixelRatio; final_pos += floor(world_snap_p0 + 0.5) - world_snap_p0; #elif !defined(WR_FEATURE_TRANSFORM) - // Compute the snapping offset only if the layer transform is axis-aligned. - final_pos += compute_snap_offset(clamped_local_pos, layer, snap_rect); + // Compute the snapping offset only if the scroll node transform is axis-aligned. + final_pos += compute_snap_offset(clamped_local_pos, scroll_node, snap_rect); #endif gl_Position = uTransform * vec4(final_pos, z, 1.0); @@ -74,7 +74,7 @@ void main(void) { #ifdef WR_FEATURE_GLYPH_TRANSFORM // Transform from local space to glyph space. - mat2 transform = mat2(prim.layer.transform) * uDevicePixelRatio; + mat2 transform = mat2(prim.scroll_node.transform) * uDevicePixelRatio; // Compute the glyph rect in glyph space. RectWithSize glyph_rect = RectWithSize(res.offset + transform * (text.offset + glyph.offset), @@ -87,15 +87,12 @@ void main(void) { // Select the corner of the glyph's local space rect that we are processing. vec2 local_pos = local_rect.p0 + local_rect.size * aPosition.xy; - // Calculate a combined local clip rect. - RectWithSize local_clip_rect = intersect_rects(prim.local_clip_rect, prim.layer.local_clip_rect); - // If the glyph's local rect would fit inside the local clip rect, then select a corner from // the device space glyph rect to reduce overdraw of clipped pixels in the fragment shader. // Otherwise, fall back to clamping the glyph's local rect to the local clip rect. - local_pos = rect_inside_rect(local_rect, local_clip_rect) ? + local_pos = rect_inside_rect(local_rect, prim.local_clip_rect) ? inv * (glyph_rect.p0 + glyph_rect.size * aPosition.xy) : - clamp_rect(local_pos, local_clip_rect); + clamp_rect(local_pos, prim.local_clip_rect); #else // Scale from glyph space to local space. float scale = res.scale / uDevicePixelRatio; @@ -107,14 +104,14 @@ void main(void) { // Select the corner of the glyph rect that we are processing. vec2 local_pos = glyph_rect.p0 + glyph_rect.size * aPosition.xy; - // Clamp to the two local clip rects. - local_pos = clamp_rect(clamp_rect(local_pos, prim.local_clip_rect), prim.layer.local_clip_rect); + // Clamp to the local clip rect. + local_pos = clamp_rect(local_pos, prim.local_clip_rect); #endif VertexInfo vi = write_text_vertex(local_pos, prim.local_clip_rect, prim.z, - prim.layer, + prim.scroll_node, prim.task, glyph_rect); diff --git a/webrender/res/ps_yuv_image.glsl b/webrender/res/ps_yuv_image.glsl index a82ea142f6..e8a51016e6 100644 --- a/webrender/res/ps_yuv_image.glsl +++ b/webrender/res/ps_yuv_image.glsl @@ -43,7 +43,7 @@ void main(void) { VertexInfo vi = write_vertex(prim.local_rect, prim.local_clip_rect, prim.z, - prim.layer, + prim.scroll_node, prim.task, prim.local_rect); vLocalPos = vi.local_pos - prim.local_rect.p0; diff --git a/webrender/src/clip_scroll_node.rs b/webrender/src/clip_scroll_node.rs index a9bad59dbc..f43d358778 100644 --- a/webrender/src/clip_scroll_node.rs +++ b/webrender/src/clip_scroll_node.rs @@ -2,10 +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/. */ -use api::{ClipId, DeviceIntRect, LayerPixel, LayerPoint, LayerRect, LayerSize}; -use api::{LayerToScrollTransform, LayerToWorldTransform, LayerVector2D, LayoutVector2D, PipelineId}; -use api::{ScrollClamping, ScrollEventPhase, ScrollLocation, ScrollSensitivity}; -use api::{DevicePixelScale, LayoutTransform, PropertyBinding, StickyOffsetBounds, WorldPoint}; +use api::{ClipId, DeviceIntRect, DevicePixelScale, LayerPixel, LayerPoint, LayerRect, LayerSize}; +use api::{LayerToWorldTransform, LayerTransform, LayerVector2D, LayoutTransform, LayoutVector2D}; +use api::{PipelineId, PropertyBinding, ScrollClamping, ScrollEventPhase, ScrollLocation}; +use api::{ScrollSensitivity, StickyOffsetBounds, WorldPoint}; use clip::{ClipSourcesHandle, ClipStore}; use clip_scroll_tree::{CoordinateSystemId, TransformUpdateState}; use euclid::SideOffsets2D; @@ -17,7 +17,7 @@ use resource_cache::ResourceCache; use scene::SceneProperties; use spring::{DAMPING, STIFFNESS, Spring}; use std::rc::Rc; -use util::{MatrixHelpers, MaxRect, TransformedRectKind}; +use util::{MatrixHelpers, MaxRect, TransformOrOffset, TransformedRectKind}; #[cfg(target_os = "macos")] const CAN_OVERSCROLL: bool = true; @@ -122,6 +122,11 @@ pub struct ClipScrollNode { /// The axis-aligned coordinate system id of this node. pub coordinate_system_id: CoordinateSystemId, + /// The transformation from the coordinate system which established our compatible coordinate + /// system (same coordinate system id) and us. This can change via scroll offsets and via new + /// reference frame transforms. + pub coordinate_system_relative_transform: TransformOrOffset, + /// A linear ID / index of this clip-scroll node. Used as a reference to /// pass to shaders, to allow them to fetch a given clip-scroll node. pub node_data_index: ClipScrollNodeIndex, @@ -146,6 +151,7 @@ impl ClipScrollNode { clip_chain_node: None, combined_clip_outer_bounds: DeviceIntRect::max_rect(), coordinate_system_id: CoordinateSystemId(0), + coordinate_system_relative_transform: TransformOrOffset::zero(), node_data_index: ClipScrollNodeIndex(0), } } @@ -187,10 +193,11 @@ impl ClipScrollNode { ) -> Self { let identity = LayoutTransform::identity(); let info = ReferenceFrameInfo { - resolved_transform: LayerToScrollTransform::identity(), + resolved_transform: LayerTransform::identity(), source_transform: source_transform.unwrap_or(PropertyBinding::Value(identity)), source_perspective: source_perspective.unwrap_or(identity), origin_in_parent_reference_frame, + invertible: true, }; Self::new(pipeline_id, parent_id, frame_rect, NodeType::ReferenceFrame(info)) } @@ -271,53 +278,19 @@ impl ClipScrollNode { self.clip_chain_node = None; } - pub fn push_gpu_node_data( - &mut self, - state: &TransformUpdateState, - node_data: &mut Vec - ) { + pub fn push_gpu_node_data(&mut self, node_data: &mut Vec) { if self.combined_clip_outer_bounds.is_empty() { node_data.push(ClipScrollNodeData::invalid()); return; } - let local_clip_rect = match self.node_type { - _ if self.world_content_transform.has_perspective_component() => LayerRect::max_rect(), - NodeType::ReferenceFrame(ref info) => { - info.resolved_transform.with_destination::() - .inverse_rect_footprint(&state.parent_combined_viewport_rect) - } - NodeType::Clip(_) | NodeType::ScrollFrame(_) => { - state.parent_combined_viewport_rect - .intersection(&self.local_clip_rect) - .unwrap_or(LayerRect::zero()) - } - NodeType::StickyFrame(ref sticky_info) => { - state.parent_combined_viewport_rect - .translate(&-sticky_info.current_offset) - .intersection(&self.local_clip_rect) - .unwrap_or(LayerRect::zero()) - } - }; - let transform_kind = if self.world_content_transform.preserves_2d_axis_alignment() { TransformedRectKind::AxisAligned } else { TransformedRectKind::Complex }; - - let reference_frame_relative_scroll_offset = match self.node_type { - NodeType::ReferenceFrame(_) => LayerVector2D::zero(), - NodeType::Clip(_) | NodeType::ScrollFrame(_) => state.parent_accumulated_scroll_offset, - NodeType::StickyFrame(ref sticky_info) => - state.parent_accumulated_scroll_offset + sticky_info.current_offset, - }; - let data = ClipScrollNodeData { transform: self.world_content_transform, - local_clip_rect, - reference_frame_relative_scroll_offset, - scroll_offset: self.scroll_offset(), transform_kind: transform_kind as u32 as f32, padding: [0.0; 3], }; @@ -343,17 +316,19 @@ impl ClipScrollNode { return; } + self.update_transform(state, next_coordinate_system_id, scene_properties); + // If this node is a reference frame, we check if the determinant is 0, which means it // has a non-invertible matrix. For non-reference-frames we assume that they will // produce only additional translations which should be invertible. - if self.node_type.is_reference_frame() { - if self.world_content_transform.determinant() == 0.0 { + match self.node_type { + NodeType::ReferenceFrame(info) if !info.invertible => { self.update_to_empty_rect(); return; } + _ => {}, } - self.update_transform(state, next_coordinate_system_id, scene_properties); self.update_clip_work_item( state, device_pixel_scale, @@ -424,12 +399,15 @@ impl ClipScrollNode { state.combined_inner_clip_bounds = combined_inner_screen_rect; self.combined_clip_outer_bounds = combined_outer_screen_rect; + let local_clip_rect = self.coordinate_system_relative_transform.apply(&self.local_clip_rect); + self.clip_chain_node = Some(Rc::new(ClipChainNode { work_item: ClipWorkItem { scroll_node_data_index: self.node_data_index, clip_sources: clip_sources_handle.weak(), coordinate_system_id: state.current_coordinate_system_id, }, + local_clip_rect, screen_inner_rect, combined_outer_screen_rect, combined_inner_screen_rect, @@ -479,6 +457,10 @@ impl ClipScrollNode { self.world_viewport_transform }; + let added_offset = state.parent_accumulated_scroll_offset + sticky_offset + scroll_offset; + self.coordinate_system_relative_transform = + state.coordinate_system_relative_transform.offset(added_offset); + match self.node_type { NodeType::StickyFrame(ref mut info) => info.current_offset = sticky_offset, _ => {}, @@ -500,30 +482,41 @@ impl ClipScrollNode { // Resolve the transform against any property bindings. let source_transform = scene_properties.resolve_layout_transform(&info.source_transform); - info.resolved_transform = LayerToScrollTransform::create_translation( + info.resolved_transform = LayerTransform::create_translation( info.origin_in_parent_reference_frame.x, info.origin_in_parent_reference_frame.y, 0.0 ).pre_mul(&source_transform) .pre_mul(&info.source_perspective); - if !info.resolved_transform.preserves_2d_axis_alignment() || - info.resolved_transform.has_perspective_component() { - state.current_coordinate_system_id = *next_coordinate_system_id; - next_coordinate_system_id.advance(); - } - self.coordinate_system_id = state.current_coordinate_system_id; - // The transformation for this viewport in world coordinates is the transformation for // our parent reference frame, plus any accumulated scrolling offsets from nodes // between our reference frame and this node. Finally, we also include // whatever local transformation this reference frame provides. This can be combined // with the local_viewport_rect to get its position in world space. - self.world_viewport_transform = state - .parent_reference_frame_transform - .pre_translate(state.parent_accumulated_scroll_offset.to_3d()) - .pre_mul(&info.resolved_transform.with_destination::()); + let relative_transform = info.resolved_transform + .post_translate(state.parent_accumulated_scroll_offset.to_3d()); + self.world_viewport_transform = state.parent_reference_frame_transform + .pre_mul(&relative_transform.with_destination::()); self.world_content_transform = self.world_viewport_transform; + + info.invertible = relative_transform.determinant() != 0.0; + if !info.invertible { + return; + } + + // Try to update our compatible coordinate system transform. If we cannot, start a new + // incompatible coordinate system. + match state.coordinate_system_relative_transform.update(relative_transform) { + Some(offset) => self.coordinate_system_relative_transform = offset, + None => { + self.coordinate_system_relative_transform = TransformOrOffset::zero(); + state.current_coordinate_system_id = *next_coordinate_system_id; + next_coordinate_system_id.advance(); + } + } + + self.coordinate_system_id = state.current_coordinate_system_id; } fn calculate_sticky_offset( @@ -635,21 +628,13 @@ impl ClipScrollNode { sticky_offset } - pub fn prepare_state_for_children( - &self, - state: &mut TransformUpdateState, - node_data: &Vec - ) { + pub fn prepare_state_for_children(&self, state: &mut TransformUpdateState) { if self.combined_clip_outer_bounds.is_empty() { - state.parent_combined_viewport_rect = LayerRect::zero(); state.combined_outer_clip_bounds = DeviceIntRect::zero(); state.parent_clip_chain = None; return; } - let combined_local_viewport_rect = - node_data[self.node_data_index.0 as usize].local_clip_rect; - // The transformation we are passing is the transformation of the parent // reference frame and the offset is the accumulated offset of all the nodes // between us and the parent reference frame. If we are a reference frame, @@ -657,19 +642,16 @@ impl ClipScrollNode { match self.node_type { NodeType::ReferenceFrame(ref info) => { state.parent_reference_frame_transform = self.world_viewport_transform; - state.parent_combined_viewport_rect = combined_local_viewport_rect; state.parent_accumulated_scroll_offset = LayerVector2D::zero(); + state.coordinate_system_relative_transform = + self.coordinate_system_relative_transform.clone(); let translation = -info.origin_in_parent_reference_frame; state.nearest_scrolling_ancestor_viewport = state.nearest_scrolling_ancestor_viewport .translate(&translation); } - NodeType::Clip(..) => { - state.parent_combined_viewport_rect = combined_local_viewport_rect; - }, + NodeType::Clip(..) => { } NodeType::ScrollFrame(ref scrolling) => { - state.parent_combined_viewport_rect = - combined_local_viewport_rect.translate(&-scrolling.offset); state.parent_accumulated_scroll_offset = scrolling.offset + state.parent_accumulated_scroll_offset; state.nearest_scrolling_ancestor_offset = scrolling.offset; @@ -679,7 +661,6 @@ impl ClipScrollNode { // We don't translate the combined rect by the sticky offset, because sticky // offsets actually adjust the node position itself, whereas scroll offsets // only apply to contents inside the node. - state.parent_combined_viewport_rect = combined_local_viewport_rect; state.parent_accumulated_scroll_offset = info.current_offset + state.parent_accumulated_scroll_offset; } @@ -898,7 +879,7 @@ impl ScrollingState { pub struct ReferenceFrameInfo { /// The transformation that establishes this reference frame, relative to the parent /// reference frame. The origin of the reference frame is included in the transformation. - pub resolved_transform: LayerToScrollTransform, + pub resolved_transform: LayerTransform, /// The source transform and perspective matrices provided by the stacking context /// that forms this reference frame. We maintain the property binding information @@ -911,4 +892,7 @@ pub struct ReferenceFrameInfo { /// origin of this reference frame. This is already rolled into the `transform' property, but /// we also store it here to properly transform the viewport for sticky positioning. pub origin_in_parent_reference_frame: LayerVector2D, + + /// True if the resolved transform is invertible. + pub invertible: bool, } diff --git a/webrender/src/clip_scroll_tree.rs b/webrender/src/clip_scroll_tree.rs index 09c67dfe0f..243173319e 100644 --- a/webrender/src/clip_scroll_tree.rs +++ b/webrender/src/clip_scroll_tree.rs @@ -14,7 +14,7 @@ use print_tree::{PrintTree, PrintTreePrinter}; use render_task::ClipChain; use resource_cache::ResourceCache; use scene::SceneProperties; -use util::MaxRect; +use util::{MaxRect, TransformOrOffset}; pub type ScrollStates = FastHashMap; @@ -69,7 +69,6 @@ pub struct ClipScrollTree { #[derive(Clone)] pub struct TransformUpdateState { pub parent_reference_frame_transform: LayerToWorldTransform, - pub parent_combined_viewport_rect: LayerRect, pub parent_accumulated_scroll_offset: LayerVector2D, pub nearest_scrolling_ancestor_offset: LayerVector2D, pub nearest_scrolling_ancestor_viewport: LayerRect, @@ -82,6 +81,9 @@ pub struct TransformUpdateState { /// display list item, since optimizations can usually only be done among /// coordinate systems which are relatively axis aligned. pub current_coordinate_system_id: CoordinateSystemId, + + /// Transform from the coordinate system that started this compatible coordinate system. + pub coordinate_system_relative_transform: TransformOrOffset, } impl ClipScrollTree { @@ -350,15 +352,12 @@ impl ClipScrollTree { } let root_reference_frame_id = self.root_reference_frame_id(); - let root_viewport = self.nodes[&root_reference_frame_id].local_clip_rect; - let mut state = TransformUpdateState { parent_reference_frame_transform: LayerToWorldTransform::create_translation( pan.x, pan.y, 0.0, ), - parent_combined_viewport_rect: root_viewport, parent_accumulated_scroll_offset: LayerVector2D::zero(), nearest_scrolling_ancestor_offset: LayerVector2D::zero(), nearest_scrolling_ancestor_viewport: LayerRect::zero(), @@ -366,6 +365,7 @@ impl ClipScrollTree { combined_outer_clip_bounds: *screen_rect, combined_inner_clip_bounds: DeviceIntRect::max_rect(), current_coordinate_system_id: CoordinateSystemId::root(), + coordinate_system_relative_transform: TransformOrOffset::zero(), }; let mut next_coordinate_system_id = state.current_coordinate_system_id.next(); self.update_node( @@ -415,13 +415,13 @@ impl ClipScrollTree { scene_properties, ); - node.push_gpu_node_data(&state, gpu_node_data); + node.push_gpu_node_data(gpu_node_data); - if node.children.is_empty() { - return; + if node.children.is_empty() || node.combined_clip_outer_bounds.is_empty() { + return } - node.prepare_state_for_children(&mut state, gpu_node_data); + node.prepare_state_for_children(&mut state); node.children.clone() }; diff --git a/webrender/src/frame_builder.rs b/webrender/src/frame_builder.rs index 6bf0d72d87..783b66d4b2 100644 --- a/webrender/src/frame_builder.rs +++ b/webrender/src/frame_builder.rs @@ -2,17 +2,15 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{BorderDetails, BorderDisplayItem, BuiltDisplayList}; -use api::{ClipAndScrollInfo, ClipId, ColorF, ColorU, PropertyBinding}; -use api::{DeviceIntPoint, DeviceUintPoint, DeviceUintRect, DeviceUintSize}; -use api::{DevicePixelScale, LayerToWorldScale, WorldRect}; -use api::{DocumentLayer, ExtendMode, FontRenderMode, LayoutTransform}; -use api::{GlyphInstance, GlyphOptions, GradientStop, HitTestFlags, HitTestItem, HitTestResult}; -use api::{ImageKey, ImageRendering, ItemRange, ItemTag, LayerPoint, LayerPrimitiveInfo, LayerRect}; -use api::{LayerSize, LayerToScrollTransform, LayerVector2D, LayoutVector2D, LineOrientation}; -use api::{LineStyle, LocalClip, PipelineId, RepeatMode}; -use api::{ScrollSensitivity, Shadow, TileOffset, TransformStyle}; -use api::{PremultipliedColorF, WorldPoint, YuvColorSpace, YuvData}; +use api::{BorderDetails, BorderDisplayItem, BuiltDisplayList, ClipAndScrollInfo, ClipId, ColorF}; +use api::{ColorU, DeviceIntPoint, DevicePixelScale, DeviceUintPoint, DeviceUintRect}; +use api::{DeviceUintSize, DocumentLayer, ExtendMode, FontRenderMode, GlyphInstance, GlyphOptions}; +use api::{GradientStop, HitTestFlags, HitTestItem, HitTestResult, ImageKey, ImageRendering}; +use api::{ItemRange, ItemTag, LayerPoint, LayerPrimitiveInfo, LayerRect, LayerSize}; +use api::{LayerToWorldScale, LayerTransform, LayerVector2D, LayoutTransform, LayoutVector2D}; +use api::{LineOrientation, LineStyle, LocalClip, PipelineId, PremultipliedColorF, PropertyBinding}; +use api::{RepeatMode, ScrollSensitivity, Shadow, TileOffset, TransformStyle, WorldPoint}; +use api::{WorldRect, YuvColorSpace, YuvData}; use app_units::Au; use border::ImageBorderSegment; use clip::{ClipRegion, ClipSource, ClipSources, ClipStore, Contains}; @@ -609,7 +607,7 @@ impl FrameBuilder { let root_id = clip_scroll_tree.root_reference_frame_id(); if let Some(root_node) = clip_scroll_tree.nodes.get_mut(&root_id) { if let NodeType::ReferenceFrame(ref mut info) = root_node.node_type { - info.resolved_transform = LayerToScrollTransform::create_translation( + info.resolved_transform = LayerTransform::create_translation( viewport_offset.x, viewport_offset.y, 0.0, @@ -1578,6 +1576,7 @@ impl FrameBuilder { device_pixel_scale: DevicePixelScale, scene_properties: &SceneProperties, node_data: &[ClipScrollNodeData], + local_rects: &mut Vec, ) -> Option { profile_scope!("cull"); @@ -1621,6 +1620,7 @@ impl FrameBuilder { SpecificPrimitiveIndex(0), &self.screen_rect.to_i32(), node_data, + local_rects, ); let pic = &mut self.prim_store.cpu_pictures[0]; @@ -1700,6 +1700,11 @@ impl FrameBuilder { gpu_cache.begin_frame(); let mut node_data = Vec::with_capacity(clip_scroll_tree.nodes.len()); + let total_prim_runs = + self.prim_store.cpu_pictures.iter().fold(1, |count, ref pic| count + pic.runs.len()); + let mut clip_chain_local_clip_rects = Vec::with_capacity(total_prim_runs); + clip_chain_local_clip_rects.push(LayerRect::max_rect()); + clip_scroll_tree.update_tree( &self.screen_rect.to_i32(), device_pixel_scale, @@ -1725,6 +1730,7 @@ impl FrameBuilder { device_pixel_scale, scene_properties, &node_data, + &mut clip_chain_local_clip_rects, ); let mut passes = Vec::new(); @@ -1788,6 +1794,7 @@ impl FrameBuilder { profile_counters, passes, node_data, + clip_chain_local_clip_rects, render_tasks, deferred_resolves, gpu_cache_updates: Some(gpu_cache_updates), diff --git a/webrender/src/gpu_types.rs b/webrender/src/gpu_types.rs index 3a6483c6de..ac48a32dda 100644 --- a/webrender/src/gpu_types.rs +++ b/webrender/src/gpu_types.rs @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{LayerVector2D, LayerRect, LayerToWorldTransform}; +use api::{LayerRect, LayerToWorldTransform}; use gpu_cache::GpuCacheAddress; use render_task::RenderTaskAddress; @@ -47,7 +47,7 @@ pub struct SimplePrimitiveInstance { pub specific_prim_address: GpuCacheAddress, pub task_address: RenderTaskAddress, pub clip_task_address: RenderTaskAddress, - pub clip_id: ClipScrollNodeIndex, + pub clip_chain_rect_index: ClipChainRectIndex, pub scroll_id: ClipScrollNodeIndex, pub z_sort_index: i32, } @@ -57,7 +57,7 @@ impl SimplePrimitiveInstance { specific_prim_address: GpuCacheAddress, task_address: RenderTaskAddress, clip_task_address: RenderTaskAddress, - clip_id: ClipScrollNodeIndex, + clip_chain_rect_index: ClipChainRectIndex, scroll_id: ClipScrollNodeIndex, z_sort_index: i32, ) -> SimplePrimitiveInstance { @@ -65,7 +65,7 @@ impl SimplePrimitiveInstance { specific_prim_address, task_address, clip_task_address, - clip_id, + clip_chain_rect_index, scroll_id, z_sort_index, } @@ -77,7 +77,7 @@ impl SimplePrimitiveInstance { self.specific_prim_address.as_int(), self.task_address.0 as i32, self.clip_task_address.0 as i32, - ((self.clip_id.0 as i32) << 16) | self.scroll_id.0 as i32, + ((self.clip_chain_rect_index.0 as i32) << 16) | self.scroll_id.0 as i32, self.z_sort_index, data0, data1, @@ -150,7 +150,7 @@ impl From for PrimitiveInstance { pub struct BrushInstance { pub picture_address: RenderTaskAddress, pub prim_address: GpuCacheAddress, - pub clip_id: ClipScrollNodeIndex, + pub clip_chain_rect_index: ClipChainRectIndex, pub scroll_id: ClipScrollNodeIndex, pub clip_task_address: RenderTaskAddress, pub z: i32, @@ -165,7 +165,7 @@ impl From for PrimitiveInstance { data: [ instance.picture_address.0 as i32, instance.prim_address.as_int(), - ((instance.clip_id.0 as i32) << 16) | instance.scroll_id.0 as i32, + ((instance.clip_chain_rect_index.0 as i32) << 16) | instance.scroll_id.0 as i32, instance.clip_task_address.0 as i32, instance.z, instance.segment_index, @@ -195,20 +195,6 @@ pub struct ClipScrollNodeIndex(pub u32); #[repr(C)] pub struct ClipScrollNodeData { pub transform: LayerToWorldTransform, - - /// Viewport rectangle clipped against parent viewport rectangles. This is - /// in the coordinate system of the node origin. Precisely, it combines the - /// local clipping rectangles of all the parent nodes on the way to the root, - /// including those of `ClipRegion` rectangles. The combined clip is reset to - /// maximum when an incompatible coordinate system is encountered. - pub local_clip_rect: LayerRect, - - /// The scroll offset of all the nodes between us and our parent reference frame. - /// This is used to calculate intersections between us and content or nodes that - /// are also direct children of our reference frame. - pub reference_frame_relative_scroll_offset: LayerVector2D, - - pub scroll_offset: LayerVector2D, pub transform_kind: f32, pub padding: [f32; 3], } @@ -217,11 +203,12 @@ impl ClipScrollNodeData { pub fn invalid() -> ClipScrollNodeData { ClipScrollNodeData { transform: LayerToWorldTransform::identity(), - local_clip_rect: LayerRect::zero(), - reference_frame_relative_scroll_offset: LayerVector2D::zero(), - scroll_offset: LayerVector2D::zero(), transform_kind: 0.0, padding: [0.0; 3], } } } + +#[derive(Copy, Debug, Clone, PartialEq)] +#[repr(C)] +pub struct ClipChainRectIndex(pub usize); diff --git a/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index 02c7d00ba2..2d33c56bdc 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -11,13 +11,14 @@ use api::{LineStyle, PipelineId, PremultipliedColorF, TileOffset, WorldToLayerTr use api::{YuvColorSpace, YuvFormat}; use border::BorderCornerInstance; use clip_scroll_tree::{CoordinateSystemId, ClipScrollTree}; +use clip_scroll_node::ClipScrollNode; use clip::{ClipSource, ClipSourcesHandle, ClipStore}; use frame_builder::PrimitiveContext; use glyph_rasterizer::{FontInstance, FontTransform}; use internal_types::{FastHashMap}; use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest, ToGpuBlocks}; -use gpu_types::ClipScrollNodeData; +use gpu_types::{ClipChainRectIndex, ClipScrollNodeData}; use picture::{PictureKind, PicturePrimitive}; use profiler::FrameProfileCounters; use render_task::{ClipChain, ClipChainNode, ClipChainNodeIter, ClipWorkItem, RenderTask}; @@ -185,6 +186,7 @@ pub struct PrimitiveMetadata { // storing them here. pub local_rect: LayerRect, pub local_clip_rect: LayerRect, + pub clip_chain_rect_index: ClipChainRectIndex, pub is_backface_visible: bool, pub screen_rect: Option, @@ -1007,6 +1009,7 @@ impl PrimitiveStore { clip_task_id: None, local_rect: *local_rect, local_clip_rect: *local_clip_rect, + clip_chain_rect_index: ClipChainRectIndex(0), is_backface_visible: is_backface_visible, screen_rect: None, tag, @@ -1560,6 +1563,11 @@ impl PrimitiveStore { clip_sources: metadata.clip_sources.weak(), coordinate_system_id: prim_coordinate_system_id, }, + // The local_clip_rect a property of ClipChain nodes that are ClipScrollNodes. + // It's used to calculate a local clipping rectangle before we reach this + // point, so we can set it to zero here. It should be unused from this point + // on. + local_clip_rect: LayerRect::zero(), screen_inner_rect, combined_outer_screen_rect: combined_outer_rect.unwrap_or_else(DeviceIntRect::zero), @@ -1655,7 +1663,9 @@ impl PrimitiveStore { profile_counters: &mut FrameProfileCounters, pic_index: SpecificPrimitiveIndex, screen_rect: &DeviceIntRect, + clip_chain_rect_index: ClipChainRectIndex, node_data: &[ClipScrollNodeData], + local_rects: &mut Vec, ) -> Option { // Reset the visibility of this primitive. // Do some basic checks first, that can early out @@ -1723,6 +1733,7 @@ impl PrimitiveStore { cpu_prim_index, screen_rect, node_data, + local_rects, ); let metadata = &mut self.cpu_metadata[prim_index.0]; @@ -1745,7 +1756,7 @@ impl PrimitiveStore { return None; } - let local_rect = metadata.local_rect.intersection(&metadata.local_clip_rect); + let local_rect = metadata.local_clip_rect.intersection(&metadata.local_rect); let local_rect = match local_rect { Some(local_rect) => local_rect, None if perform_culling => return None, @@ -1765,6 +1776,8 @@ impl PrimitiveStore { return None; } + metadata.clip_chain_rect_index = clip_chain_rect_index; + (local_rect, screen_bounding_rect) }; @@ -1824,6 +1837,7 @@ impl PrimitiveStore { pic_index: SpecificPrimitiveIndex, screen_rect: &DeviceIntRect, node_data: &[ClipScrollNodeData], + local_rects: &mut Vec, ) -> PrimitiveRunLocalRect { let mut result = PrimitiveRunLocalRect { local_rect_in_actual_parent_space: LayerRect::zero(), @@ -1873,6 +1887,22 @@ impl PrimitiveStore { scroll_node, ); + + let clip_chain_rect = match perform_culling { + true => get_local_clip_rect_for_nodes(scroll_node, clip_node), + false => None, + }; + + let clip_chain_rect_index = match clip_chain_rect { + Some(rect) if rect.is_empty() => continue, + Some(rect) => { + local_rects.push(rect); + ClipChainRectIndex(local_rects.len() - 1) + } + None => ClipChainRectIndex(0), // This is no clipping. + }; + + for i in 0 .. run.count { let prim_index = PrimitiveIndex(run.base_prim_index.0 + i); @@ -1891,7 +1921,9 @@ impl PrimitiveStore { profile_counters, pic_index, screen_rect, + clip_chain_rect_index, node_data, + local_rects, ) { profile_counters.visible_primitives.inc(); @@ -1968,3 +2000,29 @@ fn convert_clip_chain_to_clip_vector( }) .collect() } + +fn get_local_clip_rect_for_nodes( + scroll_node: &ClipScrollNode, + clip_node: &ClipScrollNode, +) -> Option { + let local_rect = ClipChainNodeIter { current: clip_node.clip_chain_node.clone() }.fold( + None, + |combined_local_clip_rect: Option, node| { + if node.work_item.coordinate_system_id != scroll_node.coordinate_system_id { + return combined_local_clip_rect; + } + + Some(match combined_local_clip_rect { + Some(combined_rect) => + combined_rect.intersection(&node.local_clip_rect).unwrap_or_else(LayerRect::zero), + None => node.local_clip_rect, + }) + } + ); + + match local_rect { + Some(local_rect) => + Some(scroll_node.coordinate_system_relative_transform.unapply(&local_rect)), + None => None, + } +} diff --git a/webrender/src/render_task.rs b/webrender/src/render_task.rs index 76c79ad823..af04d6cac8 100644 --- a/webrender/src/render_task.rs +++ b/webrender/src/render_task.rs @@ -40,6 +40,7 @@ pub type ClipChain = Option>; #[derive(Debug)] pub struct ClipChainNode { pub work_item: ClipWorkItem, + pub local_clip_rect: LayerRect, pub screen_inner_rect: DeviceIntRect, pub combined_outer_screen_rect: DeviceIntRect, pub combined_inner_screen_rect: DeviceIntRect, diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index f9b93bb4c0..8c7372f995 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -9,13 +9,10 @@ //! //! [renderer]: struct.Renderer.html -use api::{channel, BlobImageRenderer, FontRenderMode}; -use api::{ColorF, DocumentId, Epoch, PipelineId, RenderApiSender, RenderNotifier}; -use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize}; -use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, ColorU}; -use api::{ExternalImageId, ExternalImageType, ImageFormat}; -use api::{YUV_COLOR_SPACES, YUV_FORMATS}; -use api::{YuvColorSpace, YuvFormat}; +use api::{BlobImageRenderer, ColorF, ColorU, DeviceIntPoint, DeviceIntRect, DeviceIntSize}; +use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, DocumentId, Epoch, ExternalImageId}; +use api::{ExternalImageType, FontRenderMode, ImageFormat, PipelineId, RenderApiSender}; +use api::{RenderNotifier, YUV_COLOR_SPACES, YUV_FORMATS, YuvColorSpace, YuvFormat, channel}; #[cfg(not(feature = "debugger"))] use api::ApiMsg; use api::DebugCommand; @@ -328,6 +325,7 @@ enum TextureSampler { // the *first* pass. Items rendered in this target are // available as inputs to tasks in any subsequent pass. SharedCacheA8, + LocalClipRects } impl TextureSampler { @@ -356,6 +354,7 @@ impl Into for TextureSampler { TextureSampler::RenderTasks => TextureSlot(7), TextureSampler::Dither => TextureSlot(8), TextureSampler::SharedCacheA8 => TextureSlot(9), + TextureSampler::LocalClipRects => TextureSlot(10), } } } @@ -435,7 +434,7 @@ const DESC_CLIP: VertexDescriptor = VertexDescriptor { kind: VertexAttributeKind::I32, }, VertexAttribute { - name: "aClipLayerAddress", + name: "aScrollNodeId", count: 1, kind: VertexAttributeKind::I32, }, @@ -1495,6 +1494,7 @@ fn create_prim_shader( ("sRenderTasks", TextureSampler::RenderTasks), ("sResourceCache", TextureSampler::ResourceCache), ("sSharedCacheA8", TextureSampler::SharedCacheA8), + ("sLocalClipRects", TextureSampler::LocalClipRects), ], ); } @@ -1522,6 +1522,7 @@ fn create_clip_shader(name: &'static str, device: &mut Device) -> Result, + pub clip_chain_local_clip_rects: Vec, pub render_tasks: RenderTaskTree, // List of updates that need to be pushed to the diff --git a/webrender/src/util.rs b/webrender/src/util.rs index 85b64078be..49056f5197 100644 --- a/webrender/src/util.rs +++ b/webrender/src/util.rs @@ -2,11 +2,11 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{BorderRadius, DeviceIntPoint, DeviceIntRect, DeviceIntSize, DevicePoint, DeviceRect}; -use api::{DeviceSize, LayerPoint, LayerRect, LayerSize}; -use api::{DevicePixelScale, LayerToWorldTransform, WorldRect}; -use euclid::{Point2D, Rect, Size2D, TypedPoint2D, TypedRect, TypedSize2D}; -use euclid::{TypedTransform2D, TypedTransform3D}; +use api::{BorderRadius, DeviceIntPoint, DeviceIntRect, DeviceIntSize, DevicePixelScale}; +use api::{DevicePoint, DeviceRect, DeviceSize, LayerPoint, LayerRect, LayerSize}; +use api::{LayerToWorldTransform, LayerTransform, LayerVector2D, WorldRect}; +use euclid::{Point2D, Rect, Size2D, TypedPoint2D, TypedRect, TypedSize2D, TypedTransform2D}; +use euclid::TypedTransform3D; use num_traits::Zero; use std::{i32, f32}; @@ -21,6 +21,7 @@ pub trait MatrixHelpers { fn inverse_project(&self, target: &TypedPoint2D) -> Option>; fn inverse_rect_footprint(&self, rect: &TypedRect) -> TypedRect; fn transform_kind(&self) -> TransformedRectKind; + fn is_simple_translation(&self) -> bool; } impl MatrixHelpers for TypedTransform3D { @@ -93,6 +94,17 @@ impl MatrixHelpers for TypedTransform3D { TransformedRectKind::Complex } } + + fn is_simple_translation(&self) -> bool { + if self.m11 != 1. || self.m22 != 1. || self.m33 != 1. { + return false; + } + self.m12.abs() < NEARLY_ZERO || self.m13.abs() < NEARLY_ZERO || + self.m14.abs() < NEARLY_ZERO || self.m21.abs() < NEARLY_ZERO || + self.m23.abs() < NEARLY_ZERO || self.m24.abs() < NEARLY_ZERO || + self.m31.abs() < NEARLY_ZERO || self.m32.abs() < NEARLY_ZERO || + self.m34.abs() < NEARLY_ZERO + } } pub trait RectHelpers @@ -323,3 +335,84 @@ impl MaxRect for DeviceRect { ) } } + +/// An enum that tries to avoid expensive transformation matrix calculations +/// when possible when dealing with non-perspective axis-aligned transformations. +#[derive(Debug, Clone)] +pub enum TransformOrOffset { + /// A simple offset, which can be used without doing any matrix math. + Offset(LayerVector2D), + + /// A transformation with an inverse. If the inverse isn't present, this isn't a 2D + /// transformation, which means we need to fall back to using inverse_rect_footprint. + /// Since this operation is so expensive, we avoid it for the 2D case. + Transform { + transform: LayerTransform, + inverse: Option, + } +} + +impl TransformOrOffset { + pub fn zero() -> TransformOrOffset { + TransformOrOffset::Offset(LayerVector2D::zero()) + } + + fn new_transform(transform: LayerTransform) -> TransformOrOffset { + if transform.is_2d() { + TransformOrOffset::Transform { + transform, + inverse: Some(transform.inverse().expect("Expected invertible matrix.")) + } + } else { + TransformOrOffset::Transform { transform, inverse: None } + } + } + + pub fn apply(&self, rect: &LayerRect) -> LayerRect { + match *self { + TransformOrOffset::Offset(offset) => rect.translate(&offset), + TransformOrOffset::Transform {transform, .. } => transform.transform_rect(&rect), + } + } + + pub fn unapply(&self, rect: &LayerRect) -> LayerRect { + match *self { + TransformOrOffset::Offset(offset) => rect.translate(&-offset), + TransformOrOffset::Transform { inverse: Some(inverse), .. } => + inverse.transform_rect(&rect), + TransformOrOffset::Transform { transform, inverse: None } => + transform.inverse_rect_footprint(rect), + } + } + + pub fn offset(&self, new_offset: LayerVector2D) -> TransformOrOffset { + match *self { + TransformOrOffset::Offset(offset) => TransformOrOffset::Offset(offset + new_offset), + TransformOrOffset::Transform { transform, .. } => { + let transform = transform.pre_translate(new_offset.to_3d()); + TransformOrOffset::new_transform(transform) + } + } + } + + pub fn update(&self, transform: LayerTransform) -> Option { + if transform.is_simple_translation() { + let offset = LayerVector2D::new(transform.m41, transform.m42); + return Some(self.offset(offset)); + } + + // If we break 2D axis alignment or have a perspective component, we need to start a + // new incompatible coordinate system with which we cannot share clips without masking. + if !transform.preserves_2d_axis_alignment() || transform.has_perspective_component() { + return None; + } + + let transform = match *self { + TransformOrOffset::Offset(offset) => transform.post_translate(offset.to_3d()), + TransformOrOffset::Transform { transform: existing_transform, .. } => + transform.post_mul(&existing_transform), + }; + + Some(TransformOrOffset::new_transform(transform)) + } +} diff --git a/wrench/reftests/boxshadow/overlap1.png b/wrench/reftests/boxshadow/overlap1.png index 3558bfaa60..e0518aceff 100644 Binary files a/wrench/reftests/boxshadow/overlap1.png and b/wrench/reftests/boxshadow/overlap1.png differ diff --git a/wrench/reftests/clip/clip-3d-transform-ref.yaml b/wrench/reftests/clip/clip-3d-transform-ref.yaml new file mode 100644 index 0000000000..58d66bec5a --- /dev/null +++ b/wrench/reftests/clip/clip-3d-transform-ref.yaml @@ -0,0 +1,8 @@ +--- +root: + items: + - + bounds: [0, 0, 200, 200] + "clip-rect": [0, 0, 200, 200] + type: rect + color: green diff --git a/wrench/reftests/clip/clip-3d-transform.yaml b/wrench/reftests/clip/clip-3d-transform.yaml new file mode 100644 index 0000000000..bae83dffe3 --- /dev/null +++ b/wrench/reftests/clip/clip-3d-transform.yaml @@ -0,0 +1,30 @@ +# This tests that clipping works well inside of a transformation with a 3d component that +# is still axis-aligned and lacking a perspective component. These two spaces should exist +# within a compatible coordinate system for local clipping (no masking). +--- +root: + items: + - + bounds: [0, 0, 0, 0] + "clip-rect": [0, 0, 0, 0] + "backface-visible": true + type: "stacking-context" + "scroll-policy": scrollable + transform: [0.5, 0, -0.8660254, 0, 0, 1, 0, 0, 0.8660254, 0, 0.5, 0, 0, 0, 0, 1] + "transform-style": flat + filters: [] + items: + - + bounds: [0, 0, 800, 400] + "clip-rect": [0, 0, 800, 400] + "backface-visible": true + type: clip + id: 1 + "content-size": [800, 400] + - + bounds: [0, 0, 400, 200] + "clip-rect": [0, 0, 400, 200] + "backface-visible": true + type: rect + "clip-and-scroll": 1 + color: green diff --git a/wrench/reftests/clip/clip-45-degree-rotation-ref.png b/wrench/reftests/clip/clip-45-degree-rotation-ref.png index 5a5e9ac10b..41d72d634d 100644 Binary files a/wrench/reftests/clip/clip-45-degree-rotation-ref.png and b/wrench/reftests/clip/clip-45-degree-rotation-ref.png differ diff --git a/wrench/reftests/clip/reftest.list b/wrench/reftests/clip/reftest.list index c414cde627..d62ac02c2f 100644 --- a/wrench/reftests/clip/reftest.list +++ b/wrench/reftests/clip/reftest.list @@ -1,3 +1,4 @@ == clip-mode.yaml clip-mode.png == clip-ellipse.yaml clip-ellipse.png == clip-45-degree-rotation.yaml clip-45-degree-rotation-ref.png +== clip-3d-transform.yaml clip-3d-transform-ref.yaml