diff --git a/gfx/webrender/Cargo.toml b/gfx/webrender/Cargo.toml index 13f475aeb7e8..707909693cfc 100644 --- a/gfx/webrender/Cargo.toml +++ b/gfx/webrender/Cargo.toml @@ -27,12 +27,12 @@ cfg-if = "0.1.2" euclid = "0.17.3" fxhash = "0.2.1" gleam = "0.5" -image = { optional = true, version = "0.18" } +image = { optional = true, version = "0.19" } lazy_static = "1" log = "0.4" num-traits = "0.1.43" plane-split = "0.9.1" -png = { optional = true, version = "0.11" } +png = { optional = true, version = "0.12" } rayon = "1" ron = { optional = true, version = "0.1.7" } serde = { optional = true, version = "1.0", features = ["serde_derive"] } diff --git a/gfx/webrender/res/brush.glsl b/gfx/webrender/res/brush.glsl index efd603c17514..c59992c5f6ec 100644 --- a/gfx/webrender/res/brush.glsl +++ b/gfx/webrender/res/brush.glsl @@ -16,7 +16,6 @@ void brush_vs( vec4 segment_data ); -#define VECS_PER_BRUSH_PRIM 2 #define VECS_PER_SEGMENT 2 #define BRUSH_FLAG_PERSPECTIVE_INTERPOLATION 1 @@ -24,76 +23,19 @@ void brush_vs( #define BRUSH_FLAG_SEGMENT_REPEAT_X 4 #define BRUSH_FLAG_SEGMENT_REPEAT_Y 8 -//Note: these have to match `gpu_types` constants -#define INT_BITS (31) -#define CLIP_CHAIN_RECT_BITS (22) -#define SEGMENT_BITS (INT_BITS - CLIP_CHAIN_RECT_BITS) -#define EDGE_FLAG_BITS (4) -#define BRUSH_FLAG_BITS (4) -#define CLIP_SCROLL_INDEX_BITS (INT_BITS - EDGE_FLAG_BITS - BRUSH_FLAG_BITS) - -struct BrushInstance { - int picture_address; - int prim_address; - int clip_chain_rect_index; - int scroll_node_id; - int clip_address; - int z; - int segment_index; - int edge_mask; - int flags; - ivec3 user_data; -}; - -BrushInstance load_brush() { - BrushInstance bi; - - bi.picture_address = aData0.x & 0xffff; - bi.clip_address = aData0.x >> 16; - bi.prim_address = aData0.y; - bi.clip_chain_rect_index = aData0.z & ((1 << CLIP_CHAIN_RECT_BITS) - 1); - bi.segment_index = aData0.z >> CLIP_CHAIN_RECT_BITS; - bi.z = aData0.w; - bi.scroll_node_id = aData1.x & ((1 << CLIP_SCROLL_INDEX_BITS) - 1); - bi.edge_mask = (aData1.x >> CLIP_SCROLL_INDEX_BITS) & 0xf; - bi.flags = (aData1.x >> (CLIP_SCROLL_INDEX_BITS + EDGE_FLAG_BITS)) & 0xf; - bi.user_data = aData1.yzw; - - return bi; -} - -struct BrushPrimitive { - RectWithSize local_rect; - RectWithSize local_clip_rect; -}; - -BrushPrimitive fetch_brush_primitive(int address, int clip_chain_rect_index) { - vec4 data[2] = fetch_from_resource_cache_2(address); - - 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; -} - void main(void) { // Load the brush instance from vertex attributes. - BrushInstance brush = load_brush(); - - // 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, brush.clip_chain_rect_index); + int prim_header_address = aData.x; + int clip_address = aData.y; + int segment_index = aData.z & 0xffff; + int edge_flags = (aData.z >> 16) & 0xff; + int brush_flags = (aData.z >> 24) & 0xff; + PrimitiveHeader ph = fetch_prim_header(prim_header_address); // Fetch the segment of this brush primitive we are drawing. - int segment_address = brush.prim_address + - VECS_PER_BRUSH_PRIM + + int segment_address = ph.specific_prim_address + VECS_PER_SPECIFIC_BRUSH + - brush.segment_index * VECS_PER_SEGMENT; + segment_index * VECS_PER_SEGMENT; vec4[2] segment_data = fetch_from_resource_cache_2(segment_address); RectWithSize local_segment_rect = RectWithSize(segment_data[0].xy, segment_data[0].zw); @@ -101,20 +43,20 @@ void main(void) { VertexInfo vi; // Fetch the dynamic picture that we are drawing on. - PictureTask pic_task = fetch_picture_task(brush.picture_address); - ClipArea clip_area = fetch_clip_area(brush.clip_address); + PictureTask pic_task = fetch_picture_task(ph.render_task_index); + ClipArea clip_area = fetch_clip_area(clip_address); - ClipScrollNode scroll_node = fetch_clip_scroll_node(brush.scroll_node_id); + Transform transform = fetch_transform(ph.transform_id); // Write the normal vertex information out. - if (scroll_node.is_axis_aligned) { + if (transform.is_axis_aligned) { vi = write_vertex( local_segment_rect, - brush_prim.local_clip_rect, - float(brush.z), - scroll_node, + ph.local_clip_rect, + ph.z, + transform, pic_task, - brush_prim.local_rect + ph.local_rect ); // TODO(gw): transform bounds may be referenced by @@ -127,16 +69,16 @@ void main(void) { init_transform_vs(vec4(vec2(-1000000.0), vec2(1000000.0))); #endif } else { - bvec4 edge_mask = notEqual(brush.edge_mask & ivec4(1, 2, 4, 8), ivec4(0)); - bool do_perspective_interpolation = (brush.flags & BRUSH_FLAG_PERSPECTIVE_INTERPOLATION) != 0; + bvec4 edge_mask = notEqual(edge_flags & ivec4(1, 2, 4, 8), ivec4(0)); + bool do_perspective_interpolation = (brush_flags & BRUSH_FLAG_PERSPECTIVE_INTERPOLATION) != 0; vi = write_transform_vertex( local_segment_rect, - brush_prim.local_rect, - brush_prim.local_clip_rect, + ph.local_rect, + ph.local_clip_rect, mix(vec4(0.0), vec4(1.0), edge_mask), - float(brush.z), - scroll_node, + ph.z, + transform, pic_task, do_perspective_interpolation ); @@ -158,13 +100,13 @@ void main(void) { // Run the specific brush VS code to write interpolators. brush_vs( vi, - brush.prim_address + VECS_PER_BRUSH_PRIM, - brush_prim.local_rect, + ph.specific_prim_address, + ph.local_rect, local_segment_rect, - brush.user_data, - scroll_node.transform, + ph.user_data, + transform.m, pic_task, - brush.flags, + brush_flags, segment_data[1] ); } diff --git a/gfx/webrender/res/brush_linear_gradient.glsl b/gfx/webrender/res/brush_linear_gradient.glsl index d731043b0908..7499c7171548 100644 --- a/gfx/webrender/res/brush_linear_gradient.glsl +++ b/gfx/webrender/res/brush_linear_gradient.glsl @@ -49,11 +49,16 @@ void brush_vs( mat4 transform, PictureTask pic_task, int brush_flags, - vec4 unused + vec4 texel_rect ) { Gradient gradient = fetch_gradient(prim_address); - vPos = vi.local_pos - local_rect.p0; + if ((brush_flags & BRUSH_FLAG_SEGMENT_RELATIVE) != 0) { + vPos = (vi.local_pos - segment_rect.p0) / segment_rect.size; + vPos = vPos * (texel_rect.zw - texel_rect.xy) + texel_rect.xy; + } else { + vPos = vi.local_pos - local_rect.p0; + } vec2 start_point = gradient.start_end_point.xy; vec2 end_point = gradient.start_end_point.zw; diff --git a/gfx/webrender/res/brush_radial_gradient.glsl b/gfx/webrender/res/brush_radial_gradient.glsl index fe820691e0ac..6169213bc1b1 100644 --- a/gfx/webrender/res/brush_radial_gradient.glsl +++ b/gfx/webrender/res/brush_radial_gradient.glsl @@ -49,11 +49,16 @@ void brush_vs( mat4 transform, PictureTask pic_task, int brush_flags, - vec4 unused + vec4 texel_rect ) { RadialGradient gradient = fetch_radial_gradient(prim_address); - vPos = vi.local_pos - local_rect.p0; + if ((brush_flags & BRUSH_FLAG_SEGMENT_RELATIVE) != 0) { + vPos = (vi.local_pos - segment_rect.p0) / segment_rect.size; + vPos = vPos * (texel_rect.zw - texel_rect.xy) + texel_rect.xy; + } else { + vPos = vi.local_pos - local_rect.p0; + } vCenter = gradient.center_start_end_radius.xy; vStartRadius = gradient.center_start_end_radius.z; diff --git a/gfx/webrender/res/clip_scroll.glsl b/gfx/webrender/res/clip_scroll.glsl deleted file mode 100644 index 2ef897098829..000000000000 --- a/gfx/webrender/res/clip_scroll.glsl +++ /dev/null @@ -1,88 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifdef WR_VERTEX_SHADER -#define VECS_PER_CLIP_SCROLL_NODE 9 - -uniform HIGHP_SAMPLER_FLOAT sampler2D sClipScrollNodes; - -struct ClipScrollNode { - mat4 transform; - mat4 inv_transform; - bool is_axis_aligned; -}; - -ClipScrollNode fetch_clip_scroll_node(int index) { - ClipScrollNode node; - - // Create a UV base coord for each 8 texels. - // 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_CLIP_SCROLL_NODE); - ivec2 uv0 = ivec2(uv.x + 0, uv.y); - ivec2 uv1 = ivec2(uv.x + 8, uv.y); - - node.transform[0] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(0, 0)); - node.transform[1] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(1, 0)); - node.transform[2] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(2, 0)); - node.transform[3] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(3, 0)); - - node.inv_transform[0] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(4, 0)); - node.inv_transform[1] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(5, 0)); - node.inv_transform[2] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(6, 0)); - node.inv_transform[3] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(7, 0)); - - vec4 misc = TEXEL_FETCH(sClipScrollNodes, uv1, 0, ivec2(0, 0)); - node.is_axis_aligned = misc.x == 0.0; - - return node; -} - -// Return the intersection of the plane (set up by "normal" and "point") -// with the ray (set up by "ray_origin" and "ray_dir"), -// writing the resulting scaler into "t". -bool ray_plane(vec3 normal, vec3 pt, vec3 ray_origin, vec3 ray_dir, out float t) -{ - float denom = dot(normal, ray_dir); - if (abs(denom) > 1e-6) { - vec3 d = pt - ray_origin; - t = dot(d, normal) / denom; - return t >= 0.0; - } - - return false; -} - -// Apply the inverse transform "inv_transform" -// to the reference point "ref" in CSS space, -// 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 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 ClipScrollNode - - vec4 r = inv_transform * vec4(ref, z, 1.0); - return r; -} - -// 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 scroll node plane - vec3 n = transpose(mat3(node.inv_transform)) * vec3(0.0, 0.0, 1.0); - return untransform(pos, n, a, node.inv_transform); -} - -#endif //WR_VERTEX_SHADER - diff --git a/gfx/webrender/res/clip_shared.glsl b/gfx/webrender/res/clip_shared.glsl index 7f28a5eef7c5..debe5bc4cc2f 100644 --- a/gfx/webrender/res/clip_shared.glsl +++ b/gfx/webrender/res/clip_shared.glsl @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include rect,clip_scroll,render_task,resource_cache,snap,transform +#include rect,render_task,resource_cache,snap,transform #ifdef WR_VERTEX_SHADER @@ -19,7 +19,7 @@ in ivec4 aClipDataResourceAddress; struct ClipMaskInstance { int render_task_address; - int scroll_node_id; + int transform_id; int segment; ivec2 clip_data_address; ivec2 resource_address; @@ -29,7 +29,7 @@ ClipMaskInstance fetch_clip_item() { ClipMaskInstance cmi; cmi.render_task_address = aClipRenderTaskAddress; - cmi.scroll_node_id = aScrollNodeId; + cmi.transform_id = aScrollNodeId; cmi.segment = aClipSegment; cmi.clip_data_address = aClipDataResourceAddress.xy; cmi.resource_address = aClipDataResourceAddress.zw; @@ -51,20 +51,20 @@ 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, - ClipScrollNode scroll_node, + Transform transform, ClipArea area) { vec2 device_pos = area.screen_origin + aPosition.xy * area.common_data.task_rect.size; vec2 actual_pos = device_pos; - if (scroll_node.is_axis_aligned) { + if (transform.is_axis_aligned) { vec4 snap_positions = compute_snap_positions( - scroll_node.transform, + transform.m, local_clip_rect ); vec2 snap_offsets = compute_snap_offset_impl( device_pos, - scroll_node.transform, + transform.m, local_clip_rect, RectWithSize(snap_positions.xy, snap_positions.zw - snap_positions.xy), snap_positions, @@ -81,7 +81,7 @@ ClipVertexInfo write_clip_tile_vertex(RectWithSize local_clip_rect, if (area.local_space) { node_pos = vec4(actual_pos / uDevicePixelRatio, 0.0, 1.0); } else { - node_pos = get_node_pos(actual_pos / uDevicePixelRatio, scroll_node); + node_pos = get_node_pos(actual_pos / uDevicePixelRatio, transform); } // compute the point position inside the scroll node, in CSS space diff --git a/gfx/webrender/res/cs_clip_box_shadow.glsl b/gfx/webrender/res/cs_clip_box_shadow.glsl index 4d6b623b6344..b431e4262c0c 100644 --- a/gfx/webrender/res/cs_clip_box_shadow.glsl +++ b/gfx/webrender/res/cs_clip_box_shadow.glsl @@ -41,12 +41,12 @@ BoxShadowData fetch_data(ivec2 address) { 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); + Transform transform = fetch_transform(cmi.transform_id); BoxShadowData bs_data = fetch_data(cmi.clip_data_address); ImageResource res = fetch_image_resource_direct(cmi.resource_address); ClipVertexInfo vi = write_clip_tile_vertex(bs_data.dest_rect, - scroll_node, + transform, area); vLayer = res.layer; diff --git a/gfx/webrender/res/cs_clip_image.glsl b/gfx/webrender/res/cs_clip_image.glsl index d14d36c0a411..5c9221c12c02 100644 --- a/gfx/webrender/res/cs_clip_image.glsl +++ b/gfx/webrender/res/cs_clip_image.glsl @@ -26,13 +26,13 @@ ImageMaskData fetch_mask_data(ivec2 address) { 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); + Transform transform = fetch_transform(cmi.transform_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, - scroll_node, + transform, area); vPos = vi.local_pos; diff --git a/gfx/webrender/res/cs_clip_line.glsl b/gfx/webrender/res/cs_clip_line.glsl index eb1956f9eb9e..16091767d9e1 100644 --- a/gfx/webrender/res/cs_clip_line.glsl +++ b/gfx/webrender/res/cs_clip_line.glsl @@ -43,11 +43,11 @@ LineDecorationData fetch_data(ivec2 address) { 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); + Transform transform = fetch_transform(cmi.transform_id); LineDecorationData data = fetch_data(cmi.clip_data_address); ClipVertexInfo vi = write_clip_tile_vertex(data.local_rect, - scroll_node, + transform, area); diff --git a/gfx/webrender/res/cs_clip_rectangle.glsl b/gfx/webrender/res/cs_clip_rectangle.glsl index a76892287389..410ded9bbffb 100644 --- a/gfx/webrender/res/cs_clip_rectangle.glsl +++ b/gfx/webrender/res/cs_clip_rectangle.glsl @@ -60,11 +60,11 @@ ClipData fetch_clip(ivec2 address) { 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); + Transform transform = fetch_transform(cmi.transform_id); ClipData clip = fetch_clip(cmi.clip_data_address); RectWithSize local_rect = clip.rect.rect; - ClipVertexInfo vi = write_clip_tile_vertex(local_rect, scroll_node, area); + ClipVertexInfo vi = write_clip_tile_vertex(local_rect, transform, area); vPos = vi.local_pos; vClipMode = clip.rect.mode.x; diff --git a/gfx/webrender/res/prim_shared.glsl b/gfx/webrender/res/prim_shared.glsl index fa81f2932779..5bad278de160 100644 --- a/gfx/webrender/res/prim_shared.glsl +++ b/gfx/webrender/res/prim_shared.glsl @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include rect,clip_scroll,render_task,resource_cache,snap,transform +#include rect,render_task,resource_cache,snap,transform #define EXTEND_MODE_CLAMP 0 #define EXTEND_MODE_REPEAT 1 @@ -32,11 +32,6 @@ varying vec3 vClipMaskUv; #ifdef WR_VERTEX_SHADER -#define VECS_PER_LOCAL_CLIP_RECT 1 -#define VECS_PER_PRIM_HEADER 2 -#define VECS_PER_TEXT_RUN 3 -#define VECS_PER_GRADIENT_STOP 2 - #define COLOR_MODE_FROM_PASS 0 #define COLOR_MODE_ALPHA 1 #define COLOR_MODE_SUBPX_CONST_COLOR 2 @@ -47,16 +42,46 @@ varying vec3 vClipMaskUv; #define COLOR_MODE_BITMAP 7 #define COLOR_MODE_COLOR_BITMAP 8 -uniform HIGHP_SAMPLER_FLOAT sampler2D sLocalClipRects; +uniform HIGHP_SAMPLER_FLOAT sampler2D sPrimitiveHeadersF; +uniform HIGHP_SAMPLER_FLOAT isampler2D sPrimitiveHeadersI; // Instanced attributes -in ivec4 aData0; -in ivec4 aData1; +in ivec4 aData; -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); +#define VECS_PER_PRIM_HEADER_F 2 +#define VECS_PER_PRIM_HEADER_I 2 + +struct PrimitiveHeader { + RectWithSize local_rect; + RectWithSize local_clip_rect; + float z; + int specific_prim_address; + int render_task_index; + int clip_task_index; + int transform_id; + ivec3 user_data; +}; + +PrimitiveHeader fetch_prim_header(int index) { + PrimitiveHeader ph; + + ivec2 uv_f = get_fetch_uv(index, VECS_PER_PRIM_HEADER_F); + vec4 local_rect = TEXEL_FETCH(sPrimitiveHeadersF, uv_f, 0, ivec2(0, 0)); + vec4 local_clip_rect = TEXEL_FETCH(sPrimitiveHeadersF, uv_f, 0, ivec2(1, 0)); + ph.local_rect = RectWithSize(local_rect.xy, local_rect.zw); + ph.local_clip_rect = RectWithSize(local_clip_rect.xy, local_clip_rect.zw); + + ivec2 uv_i = get_fetch_uv(index, VECS_PER_PRIM_HEADER_I); + ivec4 data0 = TEXEL_FETCH(sPrimitiveHeadersI, uv_i, 0, ivec2(0, 0)); + ivec4 data1 = TEXEL_FETCH(sPrimitiveHeadersI, uv_i, 0, ivec2(1, 0)); + ph.z = float(data0.x); + ph.render_task_index = data0.y; + ph.specific_prim_address = data0.z; + ph.clip_task_index = data0.w; + ph.transform_id = data1.x; + ph.user_data = data1.yzw; + + return ph; } struct VertexInfo { @@ -69,7 +94,7 @@ struct VertexInfo { VertexInfo write_vertex(RectWithSize instance_rect, RectWithSize local_clip_rect, float z, - ClipScrollNode scroll_node, + Transform transform, PictureTask task, RectWithSize snap_rect) { @@ -82,13 +107,13 @@ VertexInfo write_vertex(RectWithSize instance_rect, /// Compute the snapping offset. vec2 snap_offset = compute_snap_offset( clamped_local_pos, - scroll_node.transform, + transform.m, snap_rect, vec2(0.5) ); // Transform the current vertex to world space. - vec4 world_pos = scroll_node.transform * vec4(clamped_local_pos, 0.0, 1.0); + vec4 world_pos = transform.m * 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; @@ -135,7 +160,7 @@ VertexInfo write_transform_vertex(RectWithSize local_segment_rect, RectWithSize local_clip_rect, vec4 clip_edge_mask, float z, - ClipScrollNode scroll_node, + Transform transform, PictureTask task, bool do_perspective_interpolation) { // Calculate a clip rect from local_rect + local clip @@ -168,7 +193,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 = scroll_node.transform * vec4(local_pos, 0.0, 1.0); + vec4 world_pos = transform.m * 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; diff --git a/gfx/webrender/res/ps_split_composite.glsl b/gfx/webrender/res/ps_split_composite.glsl index 8026b84e908e..2a90e9f28c30 100644 --- a/gfx/webrender/res/ps_split_composite.glsl +++ b/gfx/webrender/res/ps_split_composite.glsl @@ -44,10 +44,10 @@ struct SplitCompositeInstance { SplitCompositeInstance fetch_composite_instance() { SplitCompositeInstance ci; - ci.render_task_index = aData0.x; - ci.src_task_index = aData0.y; - ci.polygons_address = aData0.z; - ci.z = float(aData0.w); + ci.render_task_index = aData.x; + ci.src_task_index = aData.y; + ci.polygons_address = aData.z; + ci.z = float(aData.w); return ci; } diff --git a/gfx/webrender/res/ps_text_run.glsl b/gfx/webrender/res/ps_text_run.glsl index e1e08db57a3e..81770befb96b 100644 --- a/gfx/webrender/res/ps_text_run.glsl +++ b/gfx/webrender/res/ps_text_run.glsl @@ -15,6 +15,8 @@ varying vec4 vUvClip; #ifdef WR_VERTEX_SHADER +#define VECS_PER_TEXT_RUN 3 + struct Glyph { vec2 offset; }; @@ -57,94 +59,16 @@ TextRun fetch_text_run(int address) { return TextRun(data[0], data[1], data[2].xy); } -struct PrimitiveInstance { - int prim_address; - int specific_prim_address; - int render_task_index; - int clip_task_index; - int scroll_node_id; - int clip_chain_rect_index; - int z; - int user_data0; - int user_data1; - int user_data2; -}; - -PrimitiveInstance fetch_prim_instance() { - PrimitiveInstance pi; - - pi.prim_address = aData0.x; - pi.specific_prim_address = pi.prim_address + VECS_PER_PRIM_HEADER; - pi.render_task_index = aData0.y % 0x10000; - pi.clip_task_index = aData0.y / 0x10000; - pi.clip_chain_rect_index = aData0.z; - pi.scroll_node_id = aData0.w; - pi.z = aData1.x; - pi.user_data0 = aData1.y; - pi.user_data1 = aData1.z; - pi.user_data2 = aData1.w; - - return pi; -} - -struct Primitive { - ClipScrollNode scroll_node; - ClipArea clip_area; - PictureTask task; - RectWithSize local_rect; - RectWithSize local_clip_rect; - int specific_prim_address; - int user_data0; - int user_data1; - int user_data2; - float z; -}; - -struct PrimitiveGeometry { - RectWithSize local_rect; - RectWithSize local_clip_rect; -}; - -PrimitiveGeometry fetch_primitive_geometry(int address) { - vec4 geom[2] = fetch_from_resource_cache_2(address); - return PrimitiveGeometry(RectWithSize(geom[0].xy, geom[0].zw), - RectWithSize(geom[1].xy, geom[1].zw)); -} - -Primitive load_primitive() { - PrimitiveInstance pi = fetch_prim_instance(); - - Primitive prim; - - 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 = intersect_rects(clip_chain_rect, geom.local_clip_rect); - - prim.specific_prim_address = pi.specific_prim_address; - prim.user_data0 = pi.user_data0; - prim.user_data1 = pi.user_data1; - prim.user_data2 = pi.user_data2; - prim.z = float(pi.z); - - return prim; -} - VertexInfo write_text_vertex(vec2 clamped_local_pos, RectWithSize local_clip_rect, float z, - ClipScrollNode scroll_node, + Transform transform, PictureTask task, vec2 text_offset, RectWithSize snap_rect, vec2 snap_bias) { // Transform the current vertex to world space. - vec4 world_pos = scroll_node.transform * vec4(clamped_local_pos, 0.0, 1.0); + vec4 world_pos = transform.m * vec4(clamped_local_pos, 0.0, 1.0); // Convert the world positions to device pixel space. float device_scale = uDevicePixelRatio / world_pos.w; @@ -159,13 +83,13 @@ VertexInfo write_text_vertex(vec2 clamped_local_pos, bool remove_subpx_offset = true; #else // Compute the snapping offset only if the scroll node transform is axis-aligned. - bool remove_subpx_offset = scroll_node.is_axis_aligned; + bool remove_subpx_offset = transform.is_axis_aligned; #endif if (remove_subpx_offset) { // Ensure the transformed text offset does not contain a subpixel translation // such that glyph snapping is stable for equivalent glyph subpixel positions. - vec2 world_text_offset = mat2(scroll_node.transform) * text_offset; - vec2 device_text_pos = (scroll_node.transform[3].xy + world_text_offset) * device_scale; + vec2 world_text_offset = mat2(transform.m) * text_offset; + vec2 device_text_pos = (transform.m[3].xy + world_text_offset) * device_scale; final_pos += floor(device_text_pos + 0.5) - device_text_pos; #ifdef WR_FEATURE_GLYPH_TRANSFORM @@ -177,7 +101,7 @@ VertexInfo write_text_vertex(vec2 clamped_local_pos, #else // The transformed text offset has already been snapped, so remove it from the transform // when snapping the glyph. - mat4 snap_transform = scroll_node.transform; + mat4 snap_transform = transform.m; snap_transform[3].xy = -world_text_offset; final_pos += compute_snap_offset( clamped_local_pos, @@ -201,31 +125,37 @@ VertexInfo write_text_vertex(vec2 clamped_local_pos, } void main(void) { - Primitive prim = load_primitive(); - TextRun text = fetch_text_run(prim.specific_prim_address); + int prim_header_address = aData.x; + int glyph_index = aData.y; + int resource_address = aData.z; + int subpx_dir = aData.w >> 16; + int color_mode = aData.w & 0xffff; - int glyph_index = prim.user_data0; - int resource_address = prim.user_data1; - int subpx_dir = prim.user_data2 >> 16; - int color_mode = prim.user_data2 & 0xffff; + PrimitiveHeader ph = fetch_prim_header(prim_header_address); + + Transform transform = fetch_transform(ph.transform_id); + ClipArea clip_area = fetch_clip_area(ph.clip_task_index); + PictureTask task = fetch_picture_task(ph.render_task_index); + + TextRun text = fetch_text_run(ph.specific_prim_address); if (color_mode == COLOR_MODE_FROM_PASS) { color_mode = uMode; } - Glyph glyph = fetch_glyph(prim.specific_prim_address, glyph_index); + Glyph glyph = fetch_glyph(ph.specific_prim_address, glyph_index); GlyphResource res = fetch_glyph_resource(resource_address); #ifdef WR_FEATURE_GLYPH_TRANSFORM // Transform from local space to glyph space. - mat2 transform = mat2(prim.scroll_node.transform) * uDevicePixelRatio; + mat2 glyph_transform = mat2(transform.m) * uDevicePixelRatio; // Compute the glyph rect in glyph space. - RectWithSize glyph_rect = RectWithSize(res.offset + transform * (text.offset + glyph.offset), + RectWithSize glyph_rect = RectWithSize(res.offset + glyph_transform * (text.offset + glyph.offset), res.uv_rect.zw - res.uv_rect.xy); // Transform the glyph rect back to local space. - mat2 inv = inverse(transform); + mat2 inv = inverse(glyph_transform); RectWithSize local_rect = transform_rect(glyph_rect, inv); // Select the corner of the glyph's local space rect that we are processing. @@ -234,9 +164,9 @@ void main(void) { // 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, prim.local_clip_rect) ? + local_pos = rect_inside_rect(local_rect, ph.local_clip_rect) ? inv * (glyph_rect.p0 + glyph_rect.size * aPosition.xy) : - clamp_rect(local_pos, prim.local_clip_rect); + clamp_rect(local_pos, ph.local_clip_rect); #else // Scale from glyph space to local space. float scale = res.scale / uDevicePixelRatio; @@ -249,7 +179,7 @@ void main(void) { vec2 local_pos = glyph_rect.p0 + glyph_rect.size * aPosition.xy; // Clamp to the local clip rect. - local_pos = clamp_rect(local_pos, prim.local_clip_rect); + local_pos = clamp_rect(local_pos, ph.local_clip_rect); #endif vec2 snap_bias; @@ -278,22 +208,22 @@ void main(void) { } VertexInfo vi = write_text_vertex(local_pos, - prim.local_clip_rect, - prim.z, - prim.scroll_node, - prim.task, + ph.local_clip_rect, + ph.z, + transform, + task, text.offset, glyph_rect, snap_bias); #ifdef WR_FEATURE_GLYPH_TRANSFORM - vec2 f = (transform * vi.local_pos - glyph_rect.p0) / glyph_rect.size; + vec2 f = (glyph_transform * vi.local_pos - glyph_rect.p0) / glyph_rect.size; vUvClip = vec4(f, 1.0 - f); #else vec2 f = (vi.local_pos - glyph_rect.p0) / glyph_rect.size; #endif - write_clip(vi.screen_pos, prim.clip_area); + write_clip(vi.screen_pos, clip_area); switch (color_mode) { case COLOR_MODE_ALPHA: diff --git a/gfx/webrender/res/snap.glsl b/gfx/webrender/res/snap.glsl index 10661319024d..9b8214fd016c 100644 --- a/gfx/webrender/res/snap.glsl +++ b/gfx/webrender/res/snap.glsl @@ -8,7 +8,7 @@ vec4 compute_snap_positions(mat4 transform, 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 scroll_node transform with scale present? But it does fix + // is a 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)); @@ -41,7 +41,7 @@ vec2 compute_snap_offset_impl( } // Compute a snapping offset in world space (adjusted to pixel ratio), -// given local position on the scroll_node and a snap rectangle. +// given local position on the transform and a snap rectangle. vec2 compute_snap_offset(vec2 local_pos, mat4 transform, RectWithSize snap_rect, diff --git a/gfx/webrender/res/transform.glsl b/gfx/webrender/res/transform.glsl index 16dbd83dc93c..0cb940999df4 100644 --- a/gfx/webrender/res/transform.glsl +++ b/gfx/webrender/res/transform.glsl @@ -6,10 +6,89 @@ flat varying vec4 vTransformBounds; #ifdef WR_VERTEX_SHADER +#define VECS_PER_TRANSFORM 8 +uniform HIGHP_SAMPLER_FLOAT sampler2D sTransformPalette; + void init_transform_vs(vec4 local_bounds) { vTransformBounds = local_bounds; } +struct Transform { + mat4 m; + mat4 inv_m; + bool is_axis_aligned; +}; + +Transform fetch_transform(int id) { + Transform transform; + + transform.is_axis_aligned = (id >> 24) == 0; + int index = id & 0x00ffffff; + + // Create a UV base coord for each 8 texels. + // 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_TRANSFORM); + ivec2 uv0 = ivec2(uv.x + 0, uv.y); + + transform.m[0] = TEXEL_FETCH(sTransformPalette, uv0, 0, ivec2(0, 0)); + transform.m[1] = TEXEL_FETCH(sTransformPalette, uv0, 0, ivec2(1, 0)); + transform.m[2] = TEXEL_FETCH(sTransformPalette, uv0, 0, ivec2(2, 0)); + transform.m[3] = TEXEL_FETCH(sTransformPalette, uv0, 0, ivec2(3, 0)); + + transform.inv_m[0] = TEXEL_FETCH(sTransformPalette, uv0, 0, ivec2(4, 0)); + transform.inv_m[1] = TEXEL_FETCH(sTransformPalette, uv0, 0, ivec2(5, 0)); + transform.inv_m[2] = TEXEL_FETCH(sTransformPalette, uv0, 0, ivec2(6, 0)); + transform.inv_m[3] = TEXEL_FETCH(sTransformPalette, uv0, 0, ivec2(7, 0)); + + return transform; +} + +// Return the intersection of the plane (set up by "normal" and "point") +// with the ray (set up by "ray_origin" and "ray_dir"), +// writing the resulting scaler into "t". +bool ray_plane(vec3 normal, vec3 pt, vec3 ray_origin, vec3 ray_dir, out float t) +{ + float denom = dot(normal, ray_dir); + if (abs(denom) > 1e-6) { + vec3 d = pt - ray_origin; + t = dot(d, normal) / denom; + return t >= 0.0; + } + + return false; +} + +// Apply the inverse transform "inv_transform" +// to the reference point "ref" in CSS space, +// producing a local point on a Transform 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 Transform 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 Transform + + vec4 r = inv_transform * vec4(ref, z, 1.0); + return r; +} + +// Given a CSS space position, transform it back into the Transform space. +vec4 get_node_pos(vec2 pos, Transform transform) { + // get a point on the scroll node plane + vec4 ah = transform.m * vec4(0.0, 0.0, 0.0, 1.0); + vec3 a = ah.xyz / ah.w; + + // get the normal to the scroll node plane + vec3 n = transpose(mat3(transform.inv_m)) * vec3(0.0, 0.0, 1.0); + return untransform(pos, n, a, transform.inv_m); +} + #endif //WR_VERTEX_SHADER #ifdef WR_FRAGMENT_SHADER diff --git a/gfx/webrender/src/batch.rs b/gfx/webrender/src/batch.rs index e7eb5fcf0e7f..610b42c77812 100644 --- a/gfx/webrender/src/batch.rs +++ b/gfx/webrender/src/batch.rs @@ -11,10 +11,10 @@ use clip_scroll_tree::{CoordinateSystemId}; use euclid::{TypedTransform3D, vec3}; use glyph_rasterizer::GlyphFormat; use gpu_cache::{GpuCache, GpuCacheHandle, GpuCacheAddress}; -use gpu_types::{BrushFlags, BrushInstance, ClipChainRectIndex}; -use gpu_types::{ClipMaskInstance, ClipScrollNodeIndex, SplitCompositeInstance}; -use gpu_types::{PrimitiveInstance, RasterizationSpace, GlyphInstance, ZBufferId}; -use gpu_types::ZBufferIdGenerator; +use gpu_types::{BrushFlags, BrushInstance, PrimitiveHeaders}; +use gpu_types::{ClipMaskInstance, SplitCompositeInstance}; +use gpu_types::{PrimitiveInstance, RasterizationSpace, GlyphInstance}; +use gpu_types::{PrimitiveHeader, PrimitiveHeaderIndex, TransformPaletteId, TransformPalette}; use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture}; use picture::{PictureCompositeMode, PicturePrimitive, PictureSurface}; use plane_split::{BspSplitter, Polygon, Splitter}; @@ -29,7 +29,7 @@ use resource_cache::{CacheItem, GlyphFetchResult, ImageRequest, ResourceCache}; use scene::FilterOpHelpers; use std::{usize, f32, i32}; use tiling::{RenderTargetContext}; -use util::{MatrixHelpers, TransformedRectKind}; +use util::{TransformedRectKind}; // Special sentinel value recognized by the shader. It is considered to be // a dummy task that doesn't mask out anything. @@ -455,7 +455,7 @@ impl AlphaBatchBuilder { gpu_cache: &mut GpuCache, render_tasks: &RenderTaskTree, deferred_resolves: &mut Vec, - z_generator: &mut ZBufferIdGenerator, + prim_headers: &mut PrimitiveHeaders, ) { let task_address = render_tasks.get_task_address(task_id); @@ -476,10 +476,10 @@ impl AlphaBatchBuilder { // Add each run in this picture to the batch. for run in &pic.runs { let scroll_node = &ctx.clip_scroll_tree.nodes[run.clip_and_scroll.scroll_node_id.0]; - let scroll_id = scroll_node.node_data_index; + let transform_id = ctx.transforms.get_id(scroll_node.transform_index); self.add_run_to_batch( run, - scroll_id, + transform_id, ctx, gpu_cache, render_tasks, @@ -488,7 +488,7 @@ impl AlphaBatchBuilder { deferred_resolves, &mut splitter, content_origin, - z_generator, + prim_headers, ); } @@ -526,7 +526,7 @@ impl AlphaBatchBuilder { task_address, source_task_address, gpu_address, - z_generator.next(), + prim_headers.z_generator.next(), ); batch.push(PrimitiveInstance::from(instance)); @@ -539,7 +539,7 @@ impl AlphaBatchBuilder { fn add_run_to_batch( &mut self, run: &PrimitiveRun, - scroll_id: ClipScrollNodeIndex, + transform_id: TransformPaletteId, ctx: &RenderTargetContext, gpu_cache: &mut GpuCache, render_tasks: &RenderTaskTree, @@ -548,7 +548,7 @@ impl AlphaBatchBuilder { deferred_resolves: &mut Vec, splitter: &mut BspSplitter, content_origin: DeviceIntPoint, - z_generator: &mut ZBufferIdGenerator, + prim_headers: &mut PrimitiveHeaders, ) { for i in 0 .. run.count { let prim_index = PrimitiveIndex(run.base_prim_index.0 + i); @@ -556,8 +556,7 @@ impl AlphaBatchBuilder { if metadata.screen_rect.is_some() { self.add_prim_to_batch( - metadata.clip_chain_rect_index, - scroll_id, + transform_id, prim_index, ctx, gpu_cache, @@ -567,7 +566,7 @@ impl AlphaBatchBuilder { deferred_resolves, splitter, content_origin, - z_generator, + prim_headers, ); } } @@ -579,8 +578,7 @@ impl AlphaBatchBuilder { // in that picture are being drawn into the same target. fn add_prim_to_batch( &mut self, - clip_chain_rect_index: ClipChainRectIndex, - scroll_id: ClipScrollNodeIndex, + transform_id: TransformPaletteId, prim_index: PrimitiveIndex, ctx: &RenderTargetContext, gpu_cache: &mut GpuCache, @@ -590,18 +588,16 @@ impl AlphaBatchBuilder { deferred_resolves: &mut Vec, splitter: &mut BspSplitter, content_origin: DeviceIntPoint, - z_generator: &mut ZBufferIdGenerator, + prim_headers: &mut PrimitiveHeaders, ) { - let z = z_generator.next(); let prim_metadata = ctx.prim_store.get_metadata(prim_index); #[cfg(debug_assertions)] //TODO: why is this needed? debug_assert_eq!(prim_metadata.prepared_frame_id, render_tasks.frame_id()); - let scroll_node = &ctx.node_data[scroll_id.0 as usize]; // TODO(gw): Calculating this for every primitive is a bit // wasteful. We should probably cache this in // the scroll node... - let transform_kind = scroll_node.transform.transform_kind(); + let transform_kind = transform_id.transform_kind(); let screen_rect = prim_metadata.screen_rect.expect("bug"); let task_relative_bounding_rect = DeviceIntRect::new( @@ -648,6 +644,15 @@ impl AlphaBatchBuilder { BlendMode::None }; + let prim_header = PrimitiveHeader { + local_rect: prim_metadata.local_rect, + local_clip_rect: prim_metadata.combined_local_clip_rect, + task_address, + specific_prim_address: prim_cache_address, + clip_task_address, + transform_id, + }; + match prim_metadata.prim_kind { PrimitiveKind::Brush => { let brush = &ctx.prim_store.cpu_brushes[prim_metadata.cpu_prim_index.0]; @@ -701,23 +706,19 @@ impl AlphaBatchBuilder { textures, ); let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect); + let prim_header_index = prim_headers.push(&prim_header, [ + uv_rect_address.as_int(), + (ShaderColorMode::ColorBitmap as i32) << 16 | + RasterizationSpace::Screen as i32, + 0, + ]); let instance = BrushInstance { - picture_address: task_address, - prim_address: prim_cache_address, - clip_chain_rect_index, - scroll_id, - clip_task_address, - z, + prim_header_index, segment_index: 0, edge_flags: EdgeAaSegmentMask::empty(), brush_flags: BrushFlags::empty(), - user_data: [ - uv_rect_address.as_int(), - (ShaderColorMode::ColorBitmap as i32) << 16 | - RasterizationSpace::Screen as i32, - 0, - ], + clip_task_address, }; batch.push(PrimitiveInstance::from(instance)); false @@ -727,7 +728,7 @@ impl AlphaBatchBuilder { } } } - FilterOp::DropShadow(..) => { + FilterOp::DropShadow(offset, ..) => { // Draw an instance of the shadow first, following by the content. // Both the shadow and the content get drawn as a brush image. @@ -768,33 +769,44 @@ impl AlphaBatchBuilder { // Get the GPU cache address of the extra data handle. let shadow_prim_address = gpu_cache.get_address(&picture.extra_gpu_data_handle); + let content_prim_header_index = prim_headers.push(&prim_header, [ + content_uv_rect_address, + (ShaderColorMode::ColorBitmap as i32) << 16 | + RasterizationSpace::Screen as i32, + 0, + ]); + + let shadow_rect = prim_metadata.local_rect.translate(&offset); + let shadow_clip_rect = prim_metadata.local_clip_rect.translate(&offset); + + let shadow_prim_header = PrimitiveHeader { + local_rect: shadow_rect, + local_clip_rect: shadow_clip_rect, + specific_prim_address: shadow_prim_address, + ..prim_header + }; + + let shadow_prim_header_index = prim_headers.push(&shadow_prim_header, [ + shadow_uv_rect_address, + (ShaderColorMode::Alpha as i32) << 16 | + RasterizationSpace::Screen as i32, + 0, + ]); + let shadow_instance = BrushInstance { - picture_address: task_address, - prim_address: shadow_prim_address, - clip_chain_rect_index, - scroll_id, + prim_header_index: shadow_prim_header_index, clip_task_address, - z, segment_index: 0, edge_flags: EdgeAaSegmentMask::empty(), brush_flags: BrushFlags::empty(), - user_data: [ - shadow_uv_rect_address, - (ShaderColorMode::Alpha as i32) << 16 | - RasterizationSpace::Screen as i32, - 0, - ], }; let content_instance = BrushInstance { - prim_address: prim_cache_address, - user_data: [ - content_uv_rect_address, - (ShaderColorMode::ColorBitmap as i32) << 16 | - RasterizationSpace::Screen as i32, - 0, - ], - ..shadow_instance + prim_header_index: content_prim_header_index, + clip_task_address, + segment_index: 0, + edge_flags: EdgeAaSegmentMask::empty(), + brush_flags: BrushFlags::empty(), }; self.batch_list @@ -856,22 +868,18 @@ impl AlphaBatchBuilder { let cache_task_id = surface.resolve_render_task_id(); let cache_task_address = render_tasks.get_task_address(cache_task_id); + let prim_header_index = prim_headers.push(&prim_header, [ + cache_task_address.0 as i32, + filter_mode, + user_data, + ]); let instance = BrushInstance { - picture_address: task_address, - prim_address: prim_cache_address, - clip_chain_rect_index, - scroll_id, + prim_header_index, 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); @@ -907,22 +915,18 @@ impl AlphaBatchBuilder { 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(cache_task_id); + let prim_header_index = prim_headers.push(&prim_header, [ + mode as u32 as i32, + backdrop_task_address.0 as i32, + source_task_address.0 as i32, + ]); let instance = BrushInstance { - picture_address: task_address, - prim_address: prim_cache_address, - clip_chain_rect_index, - scroll_id, + prim_header_index, 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)); @@ -950,23 +954,19 @@ impl AlphaBatchBuilder { let uv_rect_address = render_tasks[cache_task_id] .get_texture_address(gpu_cache) .as_int(); + let prim_header_index = prim_headers.push(&prim_header, [ + uv_rect_address, + (ShaderColorMode::ColorBitmap as i32) << 16 | + RasterizationSpace::Screen as i32, + 0, + ]); let instance = BrushInstance { - picture_address: task_address, - prim_address: prim_cache_address, - clip_chain_rect_index, - scroll_id, + prim_header_index, clip_task_address, - z, segment_index: 0, edge_flags: EdgeAaSegmentMask::empty(), brush_flags: BrushFlags::empty(), - user_data: [ - uv_rect_address, - (ShaderColorMode::ColorBitmap as i32) << 16 | - RasterizationSpace::Screen as i32, - 0, - ], }; batch.push(PrimitiveInstance::from(instance)); false @@ -986,7 +986,7 @@ impl AlphaBatchBuilder { gpu_cache, render_tasks, deferred_resolves, - z_generator, + prim_headers, ); } } @@ -999,18 +999,21 @@ impl AlphaBatchBuilder { request.with_tile(tile.tile_offset), ) { let prim_cache_address = gpu_cache.get_address(&tile.handle); + let prim_header = PrimitiveHeader { + specific_prim_address: prim_cache_address, + local_rect: tile.local_rect, + local_clip_rect: tile.local_clip_rect, + ..prim_header + }; + let prim_header_index = prim_headers.push(&prim_header, user_data); + self.add_image_tile_to_batch( batch_kind, specified_blend_mode, textures, - clip_chain_rect_index, + prim_header_index, clip_task_address, &task_relative_bounding_rect, - prim_cache_address, - scroll_id, - task_address, - z, - user_data, tile.edge_flags ); } @@ -1023,13 +1026,11 @@ impl AlphaBatchBuilder { BrushBatchKind::LinearGradient, specified_blend_mode, &task_relative_bounding_rect, - clip_chain_rect_index, - scroll_id, - task_address, clip_task_address, - z, gpu_cache, &mut self.batch_list, + &prim_header, + prim_headers, ); } BrushKind::RadialGradient { ref stops_handle, ref visible_tiles, .. } if !visible_tiles.is_empty() => { @@ -1039,13 +1040,11 @@ impl AlphaBatchBuilder { BrushBatchKind::RadialGradient, specified_blend_mode, &task_relative_bounding_rect, - clip_chain_rect_index, - scroll_id, - task_address, clip_task_address, - z, gpu_cache, &mut self.batch_list, + &prim_header, + prim_headers, ); } _ => { @@ -1054,6 +1053,8 @@ impl AlphaBatchBuilder { gpu_cache, deferred_resolves, ) { + let prim_header_index = prim_headers.push(&prim_header, user_data); + self.add_brush_to_batch( brush, prim_metadata, @@ -1061,16 +1062,11 @@ impl AlphaBatchBuilder { specified_blend_mode, non_segmented_blend_mode, textures, - clip_chain_rect_index, + prim_header_index, clip_task_address, &task_relative_bounding_rect, - prim_cache_address, - scroll_id, - task_address, transform_kind, - z, render_tasks, - user_data, ); } } @@ -1080,17 +1076,13 @@ impl AlphaBatchBuilder { let text_cpu = &ctx.prim_store.cpu_text_runs[prim_metadata.cpu_prim_index.0]; - let font = text_cpu.get_font( - ctx.device_pixel_scale, - scroll_node.transform, - ); - let subpx_dir = font.get_subpx_dir(); + let subpx_dir = text_cpu.used_font.get_subpx_dir(); let glyph_fetch_buffer = &mut self.glyph_fetch_buffer; let batch_list = &mut self.batch_list; ctx.resource_cache.fetch_glyphs( - font, + text_cpu.used_font.clone(), &text_cpu.glyph_keys, glyph_fetch_buffer, gpu_cache, @@ -1117,7 +1109,7 @@ impl AlphaBatchBuilder { let (blend_mode, color_mode) = match glyph_format { GlyphFormat::Subpixel | GlyphFormat::TransformedSubpixel => { - if text_cpu.font.bg_color.a != 0 { + if text_cpu.used_font.bg_color.a != 0 { ( BlendMode::SubpixelWithBgColor, ShaderColorMode::FromRenderPassMode, @@ -1129,7 +1121,7 @@ impl AlphaBatchBuilder { ) } else { ( - BlendMode::SubpixelConstantTextColor(text_cpu.font.color.into()), + BlendMode::SubpixelConstantTextColor(text_cpu.used_font.color.into()), ShaderColorMode::SubpixelConstantTextColor, ) } @@ -1155,15 +1147,11 @@ impl AlphaBatchBuilder { } }; + let prim_header_index = prim_headers.push(&prim_header, [0; 3]); let key = BatchKey::new(kind, blend_mode, textures); let batch = batch_list.get_suitable_batch(key, &task_relative_bounding_rect); let base_instance = GlyphInstance::new( - prim_cache_address, - task_address, - clip_task_address, - clip_chain_rect_index, - scroll_id, - z, + prim_header_index, ); for glyph in glyphs { @@ -1185,27 +1173,17 @@ impl AlphaBatchBuilder { batch_kind: BrushBatchKind, blend_mode: BlendMode, textures: BatchTextures, - clip_chain_rect_index: ClipChainRectIndex, + prim_header_index: PrimitiveHeaderIndex, clip_task_address: RenderTaskAddress, task_relative_bounding_rect: &DeviceIntRect, - prim_cache_address: GpuCacheAddress, - scroll_id: ClipScrollNodeIndex, - task_address: RenderTaskAddress, - z: ZBufferId, - user_data: [i32; 3], edge_flags: EdgeAaSegmentMask, ) { let base_instance = BrushInstance { - picture_address: task_address, - prim_address: prim_cache_address, - clip_chain_rect_index, - scroll_id, + prim_header_index, clip_task_address, - z, segment_index: 0, edge_flags, brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION, - user_data, }; self.batch_list.add_bounding_rect(task_relative_bounding_rect); @@ -1227,28 +1205,18 @@ impl AlphaBatchBuilder { alpha_blend_mode: BlendMode, non_segmented_blend_mode: BlendMode, textures: BatchTextures, - clip_chain_rect_index: ClipChainRectIndex, + prim_header_index: PrimitiveHeaderIndex, clip_task_address: RenderTaskAddress, task_relative_bounding_rect: &DeviceIntRect, - prim_cache_address: GpuCacheAddress, - scroll_id: ClipScrollNodeIndex, - task_address: RenderTaskAddress, transform_kind: TransformedRectKind, - z: ZBufferId, render_tasks: &RenderTaskTree, - user_data: [i32; 3], ) { let base_instance = BrushInstance { - picture_address: task_address, - prim_address: prim_cache_address, - clip_chain_rect_index, - scroll_id, + prim_header_index, clip_task_address, - z, segment_index: 0, edge_flags: EdgeAaSegmentMask::all(), brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION, - user_data, }; self.batch_list.add_bounding_rect(task_relative_bounding_rect); @@ -1326,13 +1294,11 @@ fn add_gradient_tiles( kind: BrushBatchKind, blend_mode: BlendMode, task_relative_bounding_rect: &DeviceIntRect, - clip_chain_rect_index: ClipChainRectIndex, - scroll_id: ClipScrollNodeIndex, - task_address: RenderTaskAddress, clip_task_address: RenderTaskAddress, - z: ZBufferId, gpu_cache: &GpuCache, batch_list: &mut BatchList, + base_prim_header: &PrimitiveHeader, + prim_headers: &mut PrimitiveHeaders, ) { batch_list.add_bounding_rect(task_relative_bounding_rect); let batch = batch_list.get_suitable_batch( @@ -1346,24 +1312,22 @@ fn add_gradient_tiles( let user_data = [stops_handle.as_int(gpu_cache), 0, 0]; - let base_instance = BrushInstance { - picture_address: task_address, - prim_address: GpuCacheAddress::invalid(), - clip_chain_rect_index, - scroll_id, - clip_task_address, - z, - segment_index: 0, - edge_flags: EdgeAaSegmentMask::all(), - brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION, - user_data, - }; - for tile in visible_tiles { + let prim_header = PrimitiveHeader { + specific_prim_address: gpu_cache.get_address(&tile.handle), + local_rect: tile.local_rect, + local_clip_rect: tile.local_clip_rect, + ..*base_prim_header + }; + let prim_header_index = prim_headers.push(&prim_header, user_data); + batch.push(PrimitiveInstance::from( BrushInstance { - prim_address: gpu_cache.get_address(&tile.handle), - ..base_instance + prim_header_index, + clip_task_address, + segment_index: 0, + edge_flags: EdgeAaSegmentMask::all(), + brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION, } )); } @@ -1783,7 +1747,7 @@ impl ClipBatcher { ) { let instance = ClipMaskInstance { render_task_address: task_address, - scroll_node_data_index: ClipScrollNodeIndex(0), + transform_id: TransformPaletteId::identity(), segment: 0, clip_data_address, resource_address: GpuCacheAddress::invalid(), @@ -1800,12 +1764,13 @@ impl ClipBatcher { resource_cache: &ResourceCache, gpu_cache: &GpuCache, clip_store: &ClipStore, + transforms: &TransformPalette, ) { let mut coordinate_system_id = coordinate_system_id; for work_item in clips.iter() { let instance = ClipMaskInstance { render_task_address: task_address, - scroll_node_data_index: work_item.scroll_node_data_index, + transform_id: transforms.get_id(work_item.transform_index), segment: 0, clip_data_address: GpuCacheAddress::invalid(), resource_address: GpuCacheAddress::invalid(), diff --git a/gfx/webrender/src/clip.rs b/gfx/webrender/src/clip.rs index 388d89da74b4..dc02748a8bee 100644 --- a/gfx/webrender/src/clip.rs +++ b/gfx/webrender/src/clip.rs @@ -7,11 +7,11 @@ use api::{ImageRendering, LayoutRect, LayoutSize, LayoutPoint, LayoutVector2D, L use api::{BoxShadowClipMode, LayoutToWorldScale, LineOrientation, LineStyle}; use border::{ensure_no_corner_overlap}; use box_shadow::{BLUR_SAMPLE_SCALE, BoxShadowClipSource, BoxShadowCacheKey}; -use clip_scroll_tree::{ClipChainIndex, CoordinateSystemId}; +use clip_scroll_tree::{ClipChainIndex, CoordinateSystemId, TransformIndex}; use ellipse::Ellipse; use freelist::{FreeList, FreeListHandle, WeakFreeListHandle}; use gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks}; -use gpu_types::{BoxShadowStretchMode, ClipScrollNodeIndex}; +use gpu_types::{BoxShadowStretchMode}; use prim_store::{ClipData, ImageMaskData}; use render_task::to_cache_size; use resource_cache::{ImageRequest, ResourceCache}; @@ -625,7 +625,7 @@ impl Iterator for ClipChainNodeIter { #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct ClipWorkItem { - pub scroll_node_data_index: ClipScrollNodeIndex, + pub transform_index: TransformIndex, pub clip_sources: ClipSourcesWeakHandle, pub coordinate_system_id: CoordinateSystemId, } diff --git a/gfx/webrender/src/clip_scroll_node.rs b/gfx/webrender/src/clip_scroll_node.rs index e5a524e227d4..17ba99068a7e 100644 --- a/gfx/webrender/src/clip_scroll_node.rs +++ b/gfx/webrender/src/clip_scroll_node.rs @@ -7,10 +7,10 @@ use api::{LayoutVector2D, LayoutTransform, PipelineId, PropertyBinding}; use api::{ScrollClamping, ScrollLocation, ScrollSensitivity, StickyOffsetBounds}; use clip::{ClipChain, ClipChainNode, ClipSourcesHandle, ClipStore, ClipWorkItem}; use clip_scroll_tree::{ClipChainIndex, ClipScrollNodeIndex, CoordinateSystemId}; -use clip_scroll_tree::TransformUpdateState; +use clip_scroll_tree::{TransformUpdateState, TransformIndex}; use euclid::SideOffsets2D; use gpu_cache::GpuCache; -use gpu_types::{ClipScrollNodeIndex as GPUClipScrollNodeIndex, ClipScrollNodeData}; +use gpu_types::{TransformData, TransformPalette}; use resource_cache::ResourceCache; use scene::SceneProperties; use util::{LayoutToWorldFastTransform, LayoutFastTransform}; @@ -46,9 +46,26 @@ impl StickyFrameInfo { } #[derive(Debug)] -pub enum NodeType { +pub enum SpatialNodeKind { + /// A special kind of node that adjusts its position based on the position + /// of its parent node and a given set of sticky positioning offset bounds. + /// Sticky positioned is described in the CSS Positioned Layout Module Level 3 here: + /// https://www.w3.org/TR/css-position-3/#sticky-pos + StickyFrame(StickyFrameInfo), + + /// Transforms it's content, but doesn't clip it. Can also be adjusted + /// by scroll events or setting scroll offsets. + ScrollFrame(ScrollFrameInfo), + /// A reference frame establishes a new coordinate space in the tree. ReferenceFrame(ReferenceFrameInfo), +} + +#[derive(Debug)] +pub enum NodeType { + Spatial { + kind: SpatialNodeKind, + }, /// Other nodes just do clipping, but no transformation. Clip { @@ -61,16 +78,6 @@ pub enum NodeType { clip_chain_node: Option, }, - /// Transforms it's content, but doesn't clip it. Can also be adjusted - /// by scroll events or setting scroll offsets. - ScrollFrame(ScrollFrameInfo), - - /// A special kind of node that adjusts its position based on the position - /// of its parent node and a given set of sticky positioning offset bounds. - /// Sticky positioned is described in the CSS Positioned Layout Module Level 3 here: - /// https://www.w3.org/TR/css-position-3/#sticky-pos - StickyFrame(StickyFrameInfo), - /// An empty node, used to pad the ClipScrollTree's array of nodes so that /// we can immediately use each assigned ClipScrollNodeIndex. After display /// list flattening this node type should never be used. @@ -80,7 +87,7 @@ pub enum NodeType { impl NodeType { fn is_reference_frame(&self) -> bool { match *self { - NodeType::ReferenceFrame(_) => true, + NodeType::Spatial { kind: SpatialNodeKind::ReferenceFrame(_), .. } => true, _ => false, } } @@ -126,16 +133,18 @@ pub struct ClipScrollNode { /// reference frame transforms. pub coordinate_system_relative_transform: LayoutFastTransform, - /// 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: GPUClipScrollNodeIndex, + /// The index of the spatial node that provides positioning information for this node. + /// For reference frames, scroll and sticky frames it is a unique identfier. + /// For clip nodes, this is the nearest ancestor spatial node. + pub transform_index: TransformIndex, } impl ClipScrollNode { pub fn new( pipeline_id: PipelineId, parent_index: Option, - node_type: NodeType + node_type: NodeType, + transform_index: TransformIndex, ) -> Self { ClipScrollNode { world_viewport_transform: LayoutToWorldFastTransform::identity(), @@ -148,12 +157,17 @@ impl ClipScrollNode { invertible: true, coordinate_system_id: CoordinateSystemId(0), coordinate_system_relative_transform: LayoutFastTransform::identity(), - node_data_index: GPUClipScrollNodeIndex(0), + transform_index, } } pub fn empty() -> ClipScrollNode { - Self::new(PipelineId::dummy(), None, NodeType::Empty) + Self::new( + PipelineId::dummy(), + None, + NodeType::Empty, + TransformIndex(0), + ) } pub fn new_scroll_frame( @@ -163,18 +177,26 @@ impl ClipScrollNode { frame_rect: &LayoutRect, content_size: &LayoutSize, scroll_sensitivity: ScrollSensitivity, + transform_index: TransformIndex, ) -> Self { - let node_type = NodeType::ScrollFrame(ScrollFrameInfo::new( - *frame_rect, - scroll_sensitivity, - LayoutSize::new( - (content_size.width - frame_rect.size.width).max(0.0), - (content_size.height - frame_rect.size.height).max(0.0) - ), - external_id, - )); + let node_type = NodeType::Spatial { + kind: SpatialNodeKind::ScrollFrame(ScrollFrameInfo::new( + *frame_rect, + scroll_sensitivity, + LayoutSize::new( + (content_size.width - frame_rect.size.width).max(0.0), + (content_size.height - frame_rect.size.height).max(0.0) + ), + external_id, + )), + }; - Self::new(pipeline_id, Some(parent_index), node_type) + Self::new( + pipeline_id, + Some(parent_index), + node_type, + transform_index, + ) } pub fn new_reference_frame( @@ -183,6 +205,7 @@ impl ClipScrollNode { source_perspective: Option, origin_in_parent_reference_frame: LayoutVector2D, pipeline_id: PipelineId, + transform_index: TransformIndex, ) -> Self { let identity = LayoutTransform::identity(); let source_perspective = source_perspective.map_or_else( @@ -194,16 +217,31 @@ impl ClipScrollNode { origin_in_parent_reference_frame, invertible: true, }; - Self::new(pipeline_id, parent_index, NodeType::ReferenceFrame(info)) + Self::new( + pipeline_id, + parent_index, + NodeType::Spatial { + kind: SpatialNodeKind::ReferenceFrame(info), + }, + transform_index, + ) } pub fn new_sticky_frame( parent_index: ClipScrollNodeIndex, sticky_frame_info: StickyFrameInfo, pipeline_id: PipelineId, + transform_index: TransformIndex, ) -> Self { - let node_type = NodeType::StickyFrame(sticky_frame_info); - Self::new(pipeline_id, Some(parent_index), node_type) + let node_type = NodeType::Spatial { + kind: SpatialNodeKind::StickyFrame(sticky_frame_info), + }; + Self::new( + pipeline_id, + Some(parent_index), + node_type, + transform_index, + ) } @@ -213,7 +251,7 @@ impl ClipScrollNode { pub fn apply_old_scrolling_state(&mut self, old_scroll_info: &ScrollFrameInfo) { match self.node_type { - NodeType::ScrollFrame(ref mut scrolling) => { + NodeType::Spatial { kind: SpatialNodeKind::ScrollFrame(ref mut scrolling), .. } => { *scrolling = scrolling.combine_with_old_scroll_info(old_scroll_info); } _ if old_scroll_info.offset != LayoutVector2D::zero() => { @@ -229,7 +267,7 @@ impl ClipScrollNode { let scrollable_height = scrollable_size.height; let scrolling = match self.node_type { - NodeType::ScrollFrame(ref mut scrolling) => scrolling, + NodeType::Spatial { kind: SpatialNodeKind::ScrollFrame(ref mut scrolling), .. } => scrolling, _ => { warn!("Tried to scroll a non-scroll node."); return false; @@ -265,29 +303,32 @@ impl ClipScrollNode { self.world_viewport_transform = LayoutToWorldFastTransform::identity(); } - pub fn push_gpu_node_data(&mut self, node_data: &mut Vec) { - if !self.invertible { - node_data.push(ClipScrollNodeData::invalid()); - return; - } - - let inv_transform = match self.world_content_transform.inverse() { - Some(inverted) => inverted.to_transform(), - None => { - node_data.push(ClipScrollNodeData::invalid()); + pub fn push_gpu_data( + &mut self, + transform_palette: &mut TransformPalette, + ) { + if let NodeType::Spatial { .. } = self.node_type { + if !self.invertible { + transform_palette.set(self.transform_index, TransformData::invalid()); return; } - }; - let data = ClipScrollNodeData { - transform: self.world_content_transform.into(), - inv_transform, - transform_kind: self.transform_kind as u32 as f32, - padding: [0.0; 3], - }; + let inv_transform = match self.world_content_transform.inverse() { + Some(inverted) => inverted.to_transform(), + None => { + transform_palette.set(self.transform_index, TransformData::invalid()); + return; + } + }; - // Write the data that will be made available to the GPU for this node. - node_data.push(data); + let data = TransformData { + transform: self.world_content_transform.into(), + inv_transform, + }; + + // Write the data that will be made available to the GPU for this node. + transform_palette.set(self.transform_index, data); + } } pub fn update( @@ -320,7 +361,7 @@ impl ClipScrollNode { // For non-reference-frames we assume that they will produce only additional // translations which should be invertible. match self.node_type { - NodeType::ReferenceFrame(info) if !info.invertible => { + NodeType::Spatial { kind: SpatialNodeKind::ReferenceFrame(info), .. } if !info.invertible => { self.mark_uninvertible(); return; } @@ -347,7 +388,7 @@ impl ClipScrollNode { clip_chains: &mut [ClipChain], ) { let (clip_sources_handle, clip_chain_index, stored_clip_chain_node) = match self.node_type { - NodeType::Clip { ref handle, clip_chain_index, ref mut clip_chain_node } => + NodeType::Clip { ref handle, clip_chain_index, ref mut clip_chain_node, .. } => (handle, clip_chain_index, clip_chain_node), _ => { self.invertible = true; @@ -378,7 +419,7 @@ impl ClipScrollNode { let new_node = ClipChainNode { work_item: ClipWorkItem { - scroll_node_data_index: self.node_data_index, + transform_index: self.transform_index, clip_sources: clip_sources_handle.weak(), coordinate_system_id: state.current_coordinate_system_id, }, @@ -442,9 +483,8 @@ impl ClipScrollNode { 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, - _ => {}, + if let NodeType::Spatial { kind: SpatialNodeKind::StickyFrame(ref mut info), .. } = self.node_type { + info.current_offset = sticky_offset; } self.coordinate_system_id = state.current_coordinate_system_id; @@ -457,7 +497,7 @@ impl ClipScrollNode { scene_properties: &SceneProperties, ) { let info = match self.node_type { - NodeType::ReferenceFrame(ref mut info) => info, + NodeType::Spatial { kind: SpatialNodeKind::ReferenceFrame(ref mut info), .. } => info, _ => unreachable!("Called update_transform_for_reference_frame on non-ReferenceFrame"), }; @@ -505,7 +545,7 @@ impl ClipScrollNode { viewport_rect: &LayoutRect, ) -> LayoutVector2D { let info = match self.node_type { - NodeType::StickyFrame(ref info) => info, + NodeType::Spatial { kind: SpatialNodeKind::StickyFrame(ref info), .. } => info, _ => return LayoutVector2D::zero(), }; @@ -619,45 +659,48 @@ impl ClipScrollNode { // between us and the parent reference frame. If we are a reference frame, // we need to reset both these values. match self.node_type { - NodeType::ReferenceFrame(ref info) => { - state.parent_reference_frame_transform = self.world_viewport_transform; - state.parent_accumulated_scroll_offset = LayoutVector2D::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::Spatial { ref kind, .. } => { + match *kind { + SpatialNodeKind::StickyFrame(ref info) => { + // 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_accumulated_scroll_offset = + info.current_offset + state.parent_accumulated_scroll_offset; + } + SpatialNodeKind::ScrollFrame(ref scrolling) => { + state.parent_accumulated_scroll_offset = + scrolling.offset + state.parent_accumulated_scroll_offset; + state.nearest_scrolling_ancestor_offset = scrolling.offset; + state.nearest_scrolling_ancestor_viewport = scrolling.viewport_rect; + } + SpatialNodeKind::ReferenceFrame(ref info) => { + state.parent_reference_frame_transform = self.world_viewport_transform; + state.parent_accumulated_scroll_offset = LayoutVector2D::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{ .. } => { } - NodeType::ScrollFrame(ref scrolling) => { - state.parent_accumulated_scroll_offset = - scrolling.offset + state.parent_accumulated_scroll_offset; - state.nearest_scrolling_ancestor_offset = scrolling.offset; - state.nearest_scrolling_ancestor_viewport = scrolling.viewport_rect; - } - NodeType::StickyFrame(ref info) => { - // 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_accumulated_scroll_offset = - info.current_offset + state.parent_accumulated_scroll_offset; - } NodeType::Empty => unreachable!("Empty node remaining in ClipScrollTree."), } } pub fn scrollable_size(&self) -> LayoutSize { match self.node_type { - NodeType:: ScrollFrame(state) => state.scrollable_size, + NodeType::Spatial { kind: SpatialNodeKind::ScrollFrame(state), .. } => state.scrollable_size, _ => LayoutSize::zero(), } } - pub fn scroll(&mut self, scroll_location: ScrollLocation) -> bool { let scrolling = match self.node_type { - NodeType::ScrollFrame(ref mut scrolling) => scrolling, + NodeType::Spatial { kind: SpatialNodeKind::ScrollFrame(ref mut scrolling), .. } => scrolling, _ => return false, }; @@ -707,14 +750,14 @@ impl ClipScrollNode { pub fn scroll_offset(&self) -> LayoutVector2D { match self.node_type { - NodeType::ScrollFrame(ref scrolling) => scrolling.offset, + NodeType::Spatial { kind: SpatialNodeKind::ScrollFrame(ref scrolling), .. } => scrolling.offset, _ => LayoutVector2D::zero(), } } pub fn matches_external_id(&self, external_id: ExternalScrollId) -> bool { match self.node_type { - NodeType::ScrollFrame(info) if info.external_id == Some(external_id) => true, + NodeType::Spatial { kind: SpatialNodeKind::ScrollFrame(info), .. } if info.external_id == Some(external_id) => true, _ => false, } } diff --git a/gfx/webrender/src/clip_scroll_tree.rs b/gfx/webrender/src/clip_scroll_tree.rs index dfddc87c10bb..16c6042f5a55 100644 --- a/gfx/webrender/src/clip_scroll_tree.rs +++ b/gfx/webrender/src/clip_scroll_tree.rs @@ -4,11 +4,11 @@ use api::{DeviceIntRect, DevicePixelScale, ExternalScrollId, LayoutPoint, LayoutRect, LayoutVector2D}; use api::{PipelineId, ScrollClamping, ScrollLocation, ScrollNodeState}; -use api::WorldPoint; +use api::{LayoutSize, LayoutTransform, PropertyBinding, ScrollSensitivity, WorldPoint}; use clip::{ClipChain, ClipSourcesHandle, ClipStore}; -use clip_scroll_node::{ClipScrollNode, NodeType, ScrollFrameInfo, StickyFrameInfo}; +use clip_scroll_node::{ClipScrollNode, NodeType, SpatialNodeKind, ScrollFrameInfo, StickyFrameInfo}; use gpu_cache::GpuCache; -use gpu_types::{ClipScrollNodeIndex as GPUClipScrollNodeIndex, ClipScrollNodeData}; +use gpu_types::TransformPalette; use internal_types::{FastHashMap, FastHashSet}; use print_tree::{PrintTree, PrintTreePrinter}; use resource_cache::ResourceCache; @@ -29,6 +29,15 @@ pub struct CoordinateSystemId(pub u32); #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)] pub struct ClipScrollNodeIndex(pub usize); +// Used to index the smaller subset of nodes in the CST that define +// new transform / positioning. +// TODO(gw): In the future if we split the CST into a positioning and +// clipping tree, this can be tidied up a bit. +#[derive(Copy, Debug, Clone, PartialEq)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct TransformIndex(pub u32); + const ROOT_REFERENCE_FRAME_INDEX: ClipScrollNodeIndex = ClipScrollNodeIndex(0); const TOPMOST_SCROLL_NODE_INDEX: ClipScrollNodeIndex = ClipScrollNodeIndex(1); @@ -70,14 +79,13 @@ pub struct ClipScrollTree { pub pending_scroll_offsets: FastHashMap, - /// The current frame id, used for giving a unique id to all new dynamically - /// added frames and clips. The ClipScrollTree increments this by one every - /// time a new dynamic frame is created. - current_new_node_item: u64, - /// A set of pipelines which should be discarded the next time this /// tree is drained. pub pipelines_to_discard: FastHashSet, + + /// The number of nodes in the CST that are spatial. Currently, this is all + /// nodes that are not clip nodes. + spatial_node_count: usize, } #[derive(Clone)] @@ -112,8 +120,8 @@ impl ClipScrollTree { clip_chains_descriptors: Vec::new(), clip_chains: vec![ClipChain::empty(&DeviceIntRect::zero())], pending_scroll_offsets: FastHashMap::default(), - current_new_node_item: 1, pipelines_to_discard: FastHashSet::default(), + spatial_node_count: 0, } } @@ -136,7 +144,7 @@ impl ClipScrollTree { pub fn get_scroll_node_state(&self) -> Vec { let mut result = vec![]; for node in &self.nodes { - if let NodeType::ScrollFrame(info) = node.node_type { + if let NodeType::Spatial { kind: SpatialNodeKind::ScrollFrame(info), .. } = node.node_type { if let Some(id) = info.external_id { result.push(ScrollNodeState { id, scroll_offset: info.offset }) } @@ -146,8 +154,6 @@ impl ClipScrollTree { } pub fn drain(&mut self) -> ScrollStates { - self.current_new_node_item = 1; - let mut scroll_states = FastHashMap::default(); for old_node in &mut self.nodes.drain(..) { if self.pipelines_to_discard.contains(&old_node.pipeline_id) { @@ -155,13 +161,14 @@ impl ClipScrollTree { } match old_node.node_type { - NodeType::ScrollFrame(info) if info.external_id.is_some() => { + NodeType::Spatial { kind: SpatialNodeKind::ScrollFrame(info), .. } if info.external_id.is_some() => { scroll_states.insert(info.external_id.unwrap(), info); } _ => {} } } + self.spatial_node_count = 0; self.pipelines_to_discard.clear(); self.clip_chains = vec![ClipChain::empty(&DeviceIntRect::zero())]; self.clip_chains_descriptors.clear(); @@ -195,7 +202,7 @@ impl ClipScrollTree { let node = &self.nodes[index.0]; match node.node_type { - NodeType::ScrollFrame(state) if state.sensitive_to_input_events() => index, + NodeType::Spatial { kind: SpatialNodeKind::ScrollFrame(state), .. } if state.sensitive_to_input_events() => index, _ => self.find_nearest_scrolling_ancestor(node.parent) } } @@ -220,40 +227,41 @@ impl ClipScrollTree { resource_cache: &mut ResourceCache, gpu_cache: &mut GpuCache, pan: WorldPoint, - node_data: &mut Vec, scene_properties: &SceneProperties, - ) { - if self.nodes.is_empty() { - return; + ) -> TransformPalette { + let mut transform_palette = TransformPalette::new(self.spatial_node_count); + + if !self.nodes.is_empty() { + self.clip_chains[0] = ClipChain::empty(screen_rect); + + let root_reference_frame_index = self.root_reference_frame_index(); + let mut state = TransformUpdateState { + parent_reference_frame_transform: LayoutVector2D::new(pan.x, pan.y).into(), + parent_accumulated_scroll_offset: LayoutVector2D::zero(), + nearest_scrolling_ancestor_offset: LayoutVector2D::zero(), + nearest_scrolling_ancestor_viewport: LayoutRect::zero(), + parent_clip_chain_index: ClipChainIndex(0), + current_coordinate_system_id: CoordinateSystemId::root(), + coordinate_system_relative_transform: LayoutFastTransform::identity(), + invertible: true, + }; + let mut next_coordinate_system_id = state.current_coordinate_system_id.next(); + self.update_node( + root_reference_frame_index, + &mut state, + &mut next_coordinate_system_id, + device_pixel_scale, + clip_store, + resource_cache, + gpu_cache, + &mut transform_palette, + scene_properties, + ); + + self.build_clip_chains(screen_rect); } - self.clip_chains[0] = ClipChain::empty(screen_rect); - - let root_reference_frame_index = self.root_reference_frame_index(); - let mut state = TransformUpdateState { - parent_reference_frame_transform: LayoutVector2D::new(pan.x, pan.y).into(), - parent_accumulated_scroll_offset: LayoutVector2D::zero(), - nearest_scrolling_ancestor_offset: LayoutVector2D::zero(), - nearest_scrolling_ancestor_viewport: LayoutRect::zero(), - parent_clip_chain_index: ClipChainIndex(0), - current_coordinate_system_id: CoordinateSystemId::root(), - coordinate_system_relative_transform: LayoutFastTransform::identity(), - invertible: true, - }; - let mut next_coordinate_system_id = state.current_coordinate_system_id.next(); - self.update_node( - root_reference_frame_index, - &mut state, - &mut next_coordinate_system_id, - device_pixel_scale, - clip_store, - resource_cache, - gpu_cache, - node_data, - scene_properties, - ); - - self.build_clip_chains(screen_rect); + transform_palette } fn update_node( @@ -265,7 +273,7 @@ impl ClipScrollTree { clip_store: &mut ClipStore, resource_cache: &mut ResourceCache, gpu_cache: &mut GpuCache, - gpu_node_data: &mut Vec, + transform_palette: &mut TransformPalette, scene_properties: &SceneProperties, ) { // TODO(gw): This is an ugly borrow check workaround to clone these. @@ -277,9 +285,6 @@ impl ClipScrollTree { None => return, }; - // We set this early so that we can use it to populate the ClipChain. - node.node_data_index = GPUClipScrollNodeIndex(gpu_node_data.len() as u32); - node.update( &mut state, next_coordinate_system_id, @@ -291,7 +296,7 @@ impl ClipScrollTree { &mut self.clip_chains, ); - node.push_gpu_node_data(gpu_node_data); + node.push_gpu_data(transform_palette); if node.children.is_empty() { return; @@ -310,7 +315,7 @@ impl ClipScrollTree { clip_store, resource_cache, gpu_cache, - gpu_node_data, + transform_palette, scene_properties, ); } @@ -346,7 +351,7 @@ impl ClipScrollTree { pub fn finalize_and_apply_pending_scroll_offsets(&mut self, old_states: ScrollStates) { for node in &mut self.nodes { let external_id = match node.node_type { - NodeType::ScrollFrame(ScrollFrameInfo { external_id: Some(id), ..} ) => id, + NodeType::Spatial { kind: SpatialNodeKind::ScrollFrame(ScrollFrameInfo { external_id: Some(id), ..} ), .. } => id, _ => continue, }; @@ -360,6 +365,13 @@ impl ClipScrollTree { } } + // Generate the next valid TransformIndex for the CST. + fn next_transform_index(&mut self) -> TransformIndex { + let transform_index = TransformIndex(self.spatial_node_count as u32); + self.spatial_node_count += 1; + transform_index + } + pub fn add_clip_node( &mut self, index: ClipScrollNodeIndex, @@ -368,12 +380,65 @@ impl ClipScrollTree { pipeline_id: PipelineId, ) -> ClipChainIndex { let clip_chain_index = self.allocate_clip_chain(); - let node_type = NodeType::Clip { handle, clip_chain_index, clip_chain_node: None }; - let node = ClipScrollNode::new(pipeline_id, Some(parent_index), node_type); + let transform_index = self.nodes[parent_index.0].transform_index; + + let node_type = NodeType::Clip { + handle, + clip_chain_index, + clip_chain_node: None, + }; + let node = ClipScrollNode::new( + pipeline_id, + Some(parent_index), + node_type, + transform_index, + ); self.add_node(node, index); clip_chain_index } + pub fn add_scroll_frame( + &mut self, + index: ClipScrollNodeIndex, + parent_index: ClipScrollNodeIndex, + external_id: Option, + pipeline_id: PipelineId, + frame_rect: &LayoutRect, + content_size: &LayoutSize, + scroll_sensitivity: ScrollSensitivity, + ) { + let node = ClipScrollNode::new_scroll_frame( + pipeline_id, + parent_index, + external_id, + frame_rect, + content_size, + scroll_sensitivity, + self.next_transform_index(), + ); + self.add_node(node, index); + } + + pub fn add_reference_frame( + &mut self, + index: ClipScrollNodeIndex, + parent_index: Option, + source_transform: Option>, + source_perspective: Option, + origin_in_parent_reference_frame: LayoutVector2D, + pipeline_id: PipelineId, + ) { + let node = ClipScrollNode::new_reference_frame( + parent_index, + source_transform, + source_perspective, + origin_in_parent_reference_frame, + pipeline_id, + self.next_transform_index(), + ); + self.add_node(node, index); + } + pub fn add_sticky_frame( &mut self, index: ClipScrollNodeIndex, @@ -381,7 +446,12 @@ impl ClipScrollTree { sticky_frame_info: StickyFrameInfo, pipeline_id: PipelineId, ) { - let node = ClipScrollNode::new_sticky_frame(parent_index, sticky_frame_info, pipeline_id); + let node = ClipScrollNode::new_sticky_frame( + parent_index, + sticky_frame_info, + pipeline_id, + self.next_transform_index(), + ); self.add_node(node, index); } @@ -436,6 +506,26 @@ impl ClipScrollTree { ) { let node = &self.nodes[index.0]; match node.node_type { + NodeType::Spatial { ref kind, .. } => { + match *kind { + SpatialNodeKind::StickyFrame(ref sticky_frame_info) => { + pt.new_level(format!("StickyFrame")); + pt.add_item(format!("index: {:?}", index)); + pt.add_item(format!("sticky info: {:?}", sticky_frame_info)); + } + SpatialNodeKind::ScrollFrame(scrolling_info) => { + pt.new_level(format!("ScrollFrame")); + pt.add_item(format!("index: {:?}", index)); + pt.add_item(format!("viewport: {:?}", scrolling_info.viewport_rect)); + pt.add_item(format!("scrollable_size: {:?}", scrolling_info.scrollable_size)); + pt.add_item(format!("scroll offset: {:?}", scrolling_info.offset)); + } + SpatialNodeKind::ReferenceFrame(ref info) => { + pt.new_level(format!("ReferenceFrame {:?}", info.resolved_transform)); + pt.add_item(format!("index: {:?}", index)); + } + } + } NodeType::Clip { ref handle, .. } => { pt.new_level("Clip".to_owned()); @@ -447,22 +537,6 @@ impl ClipScrollTree { } pt.end_level(); } - NodeType::ReferenceFrame(ref info) => { - pt.new_level(format!("ReferenceFrame {:?}", info.resolved_transform)); - pt.add_item(format!("index: {:?}", index)); - } - NodeType::ScrollFrame(scrolling_info) => { - pt.new_level(format!("ScrollFrame")); - pt.add_item(format!("index: {:?}", index)); - pt.add_item(format!("viewport: {:?}", scrolling_info.viewport_rect)); - pt.add_item(format!("scrollable_size: {:?}", scrolling_info.scrollable_size)); - pt.add_item(format!("scroll offset: {:?}", scrolling_info.offset)); - } - NodeType::StickyFrame(ref sticky_frame_info) => { - pt.new_level(format!("StickyFrame")); - pt.add_item(format!("index: {:?}", index)); - pt.add_item(format!("sticky info: {:?}", sticky_frame_info)); - } NodeType::Empty => unreachable!("Empty node remaining in ClipScrollTree."), } diff --git a/gfx/webrender/src/device.rs b/gfx/webrender/src/device/gl.rs similarity index 99% rename from gfx/webrender/src/device.rs rename to gfx/webrender/src/device/gl.rs index 5e1f189c9d08..5ce6b00b4001 100644 --- a/gfx/webrender/src/device.rs +++ b/gfx/webrender/src/device/gl.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 super::shader_source; +use super::super::shader_source; use api::{ColorF, ImageFormat}; use api::{DeviceIntPoint, DeviceIntRect, DeviceUintRect, DeviceUintSize}; use api::TextureTarget; @@ -2234,6 +2234,11 @@ impl Device { external: gl::RGBA, pixel_type: gl::FLOAT, }, + ImageFormat::RGBAI32 => FormatDesc { + internal: gl::RGBA32I as _, + external: gl::RGBA_INTEGER, + pixel_type: gl::INT, + }, ImageFormat::RG8 => FormatDesc { internal: gl::RG8 as _, external: gl::RG, @@ -2360,6 +2365,7 @@ impl<'a> UploadTarget<'a> { ImageFormat::BGRA8 => (self.bgra_format, 4, gl::UNSIGNED_BYTE), ImageFormat::RG8 => (gl::RG, 2, gl::UNSIGNED_BYTE), ImageFormat::RGBAF32 => (gl::RGBA, 16, gl::FLOAT), + ImageFormat::RGBAI32 => (gl::RGBA_INTEGER, 16, gl::INT), }; let row_length = match chunk.stride { diff --git a/gfx/webrender/src/device/mod.rs b/gfx/webrender/src/device/mod.rs new file mode 100644 index 000000000000..c35c356e1cbb --- /dev/null +++ b/gfx/webrender/src/device/mod.rs @@ -0,0 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +mod gl; + +pub use self::gl::*; diff --git a/gfx/webrender/src/display_list_flattener.rs b/gfx/webrender/src/display_list_flattener.rs index 4a8a35084aa8..6a4abaaa50ab 100644 --- a/gfx/webrender/src/display_list_flattener.rs +++ b/gfx/webrender/src/display_list_flattener.rs @@ -14,10 +14,10 @@ use api::{PropertyBinding, ReferenceFrame, RepeatMode, ScrollFrameDisplayItem, S use api::{Shadow, SpecificDisplayItem, StackingContext, StickyFrameDisplayItem, TexelRect}; use api::{TransformStyle, YuvColorSpace, YuvData}; use clip::{ClipRegion, ClipSource, ClipSources, ClipStore}; -use clip_scroll_node::{ClipScrollNode, NodeType, StickyFrameInfo}; +use clip_scroll_node::{NodeType, SpatialNodeKind, StickyFrameInfo}; use clip_scroll_tree::{ClipChainIndex, ClipScrollNodeIndex, ClipScrollTree}; -use euclid::{SideOffsets2D, vec2}; -use frame_builder::{FrameBuilder, FrameBuilderConfig}; +use euclid::vec2; +use frame_builder::{ChasePrimitive, FrameBuilder, FrameBuilderConfig}; use glyph_rasterizer::FontInstance; use gpu_cache::GpuCacheHandle; use gpu_types::BrushFlags; @@ -628,8 +628,7 @@ impl<'a> DisplayListFlattener<'a> { ); } SpecificDisplayItem::Gradient(ref info) => { - self.add_gradient( - clip_and_scroll, + let brush_kind = self.create_brush_kind_for_gradient( &prim_info, info.gradient.start_point, info.gradient.end_point, @@ -638,10 +637,11 @@ impl<'a> DisplayListFlattener<'a> { info.tile_size, info.tile_spacing, ); + let prim = PrimitiveContainer::Brush(BrushPrimitive::new(brush_kind, None)); + self.add_primitive(clip_and_scroll, &prim_info, Vec::new(), prim); } SpecificDisplayItem::RadialGradient(ref info) => { - self.add_radial_gradient( - clip_and_scroll, + let brush_kind = self.create_brush_kind_for_radial_gradient( &prim_info, info.gradient.center, info.gradient.start_offset * info.gradient.radius.width, @@ -652,6 +652,8 @@ impl<'a> DisplayListFlattener<'a> { info.tile_size, info.tile_spacing, ); + let prim = PrimitiveContainer::Brush(BrushPrimitive::new(brush_kind, None)); + self.add_primitive(clip_and_scroll, &prim_info, Vec::new(), prim); } SpecificDisplayItem::BoxShadow(ref box_shadow_info) => { let bounds = box_shadow_info @@ -875,6 +877,10 @@ impl<'a> DisplayListFlattener<'a> { if container.is_visible() { let prim_index = self.create_primitive(info, clip_sources, container); + if cfg!(debug_assertions) && ChasePrimitive::LocalRect(info.rect) == self.config.chase_primitive { + println!("Chasing {:?}", prim_index); + self.prim_store.chase_id = Some(prim_index); + } self.add_primitive_to_hit_testing_list(info, clip_and_scroll); self.add_primitive_to_draw_list(prim_index, clip_and_scroll); } @@ -1197,14 +1203,15 @@ impl<'a> DisplayListFlattener<'a> { origin_in_parent_reference_frame: LayoutVector2D, ) -> ClipScrollNodeIndex { let index = self.id_to_index_mapper.get_node_index(reference_frame_id); - let node = ClipScrollNode::new_reference_frame( - parent_id.map(|id| self.id_to_index_mapper.get_node_index(id)), + let parent_index = parent_id.map(|id| self.id_to_index_mapper.get_node_index(id)); + self.clip_scroll_tree.add_reference_frame( + index, + parent_index, source_transform, source_perspective, origin_in_parent_reference_frame, pipeline_id, ); - self.clip_scroll_tree.add_node(node, index); self.reference_frame_stack.push((reference_frame_id, index)); match parent_id { @@ -1227,7 +1234,7 @@ impl<'a> DisplayListFlattener<'a> { let viewport_offset = (inner_rect.origin.to_vector().to_f32() / device_pixel_scale).round(); let root_id = self.clip_scroll_tree.root_reference_frame_index(); let root_node = &mut self.clip_scroll_tree.nodes[root_id.0]; - if let NodeType::ReferenceFrame(ref mut info) = root_node.node_type { + if let NodeType::Spatial { kind: SpatialNodeKind::ReferenceFrame(ref mut info), .. } = root_node.node_type { info.resolved_transform = LayoutVector2D::new(viewport_offset.x, viewport_offset.y).into(); } @@ -1290,16 +1297,15 @@ impl<'a> DisplayListFlattener<'a> { scroll_sensitivity: ScrollSensitivity, ) -> ClipScrollNodeIndex { let node_index = self.id_to_index_mapper.get_node_index(new_node_id); - let node = ClipScrollNode::new_scroll_frame( - pipeline_id, + self.clip_scroll_tree.add_scroll_frame( + node_index, self.id_to_index_mapper.get_node_index(parent_id), external_id, + pipeline_id, frame_rect, content_size, scroll_sensitivity, ); - - self.clip_scroll_tree.add_node(node, node_index); self.id_to_index_mapper.map_to_parent_clip_chain(new_node_id, &parent_id); node_index } @@ -1492,51 +1498,6 @@ impl<'a> DisplayListFlattener<'a> { gradient_stops: ItemRange, ) { let rect = info.rect; - let create_segments = |outset: SideOffsets2D| { - // Calculate the modified rect as specific by border-image-outset - let origin = LayoutPoint::new(rect.origin.x - outset.left, rect.origin.y - outset.top); - let size = LayoutSize::new( - rect.size.width + outset.left + outset.right, - rect.size.height + outset.top + outset.bottom, - ); - let rect = LayoutRect::new(origin, size); - - let tl_outer = LayoutPoint::new(rect.origin.x, rect.origin.y); - let tl_inner = tl_outer + vec2(border_item.widths.left, border_item.widths.top); - - let tr_outer = LayoutPoint::new(rect.origin.x + rect.size.width, rect.origin.y); - let tr_inner = tr_outer + vec2(-border_item.widths.right, border_item.widths.top); - - let bl_outer = LayoutPoint::new(rect.origin.x, rect.origin.y + rect.size.height); - let bl_inner = bl_outer + vec2(border_item.widths.left, -border_item.widths.bottom); - - let br_outer = LayoutPoint::new( - rect.origin.x + rect.size.width, - rect.origin.y + rect.size.height, - ); - let br_inner = br_outer - vec2(border_item.widths.right, border_item.widths.bottom); - - // Build the list of gradient segments - vec![ - // Top left - LayoutRect::from_floats(tl_outer.x, tl_outer.y, tl_inner.x, tl_inner.y), - // Top right - LayoutRect::from_floats(tr_inner.x, tr_outer.y, tr_outer.x, tr_inner.y), - // Bottom right - LayoutRect::from_floats(br_inner.x, br_inner.y, br_outer.x, br_outer.y), - // Bottom left - LayoutRect::from_floats(bl_outer.x, bl_inner.y, bl_inner.x, bl_outer.y), - // Top - LayoutRect::from_floats(tl_inner.x, tl_outer.y, tr_inner.x, tl_inner.y), - // Bottom - LayoutRect::from_floats(bl_inner.x, bl_inner.y, br_inner.x, bl_outer.y), - // Left - LayoutRect::from_floats(tl_outer.x, tl_inner.y, tl_inner.x, bl_inner.y), - // Right - LayoutRect::from_floats(tr_inner.x, tr_inner.y, br_outer.x, br_inner.y), - ] - }; - match border_item.details { BorderDetails::NinePatch(ref border) => { // Calculate the modified rect as specific by border-image-outset @@ -1701,70 +1662,55 @@ impl<'a> DisplayListFlattener<'a> { clip_mask_kind: BrushClipMaskKind::Unknown, }; - let prim = PrimitiveContainer::Brush(match border.source { + let brush_kind = match border.source { NinePatchBorderSource::Image(image_key) => { - let source = BorderSource::Image(ImageRequest { - key: image_key, - rendering: ImageRendering::Auto, - tile: None, - }); - - BrushPrimitive::new( - BrushKind::Border { - source - }, - Some(descriptor), + BrushKind::Border { + source: BorderSource::Image(ImageRequest { + key: image_key, + rendering: ImageRendering::Auto, + tile: None, + }) + } + } + NinePatchBorderSource::Gradient(gradient) => { + self.create_brush_kind_for_gradient( + &info, + gradient.start_point, + gradient.end_point, + gradient_stops, + gradient.extend_mode, + LayoutSize::new(border.height as f32, border.width as f32), + LayoutSize::zero(), ) } - }); + NinePatchBorderSource::RadialGradient(gradient) => { + self.create_brush_kind_for_radial_gradient( + &info, + gradient.center, + gradient.start_offset * gradient.radius.width, + gradient.end_offset * gradient.radius.width, + gradient.radius.width / gradient.radius.height, + gradient_stops, + gradient.extend_mode, + LayoutSize::new(border.height as f32, border.width as f32), + LayoutSize::zero(), + ) + } + }; + let prim = PrimitiveContainer::Brush( + BrushPrimitive::new(brush_kind, Some(descriptor)) + ); self.add_primitive(clip_and_scroll, info, Vec::new(), prim); } BorderDetails::Normal(ref border) => { self.add_normal_border(info, border, &border_item.widths, clip_and_scroll); } - BorderDetails::Gradient(ref border) => for segment in create_segments(border.outset) { - let segment_rel = segment.origin - rect.origin; - let mut info = info.clone(); - info.rect = segment; - - self.add_gradient( - clip_and_scroll, - &info, - border.gradient.start_point - segment_rel, - border.gradient.end_point - segment_rel, - gradient_stops, - border.gradient.extend_mode, - segment.size, - LayoutSize::zero(), - ); - }, - BorderDetails::RadialGradient(ref border) => { - for segment in create_segments(border.outset) { - let segment_rel = segment.origin - rect.origin; - let mut info = info.clone(); - info.rect = segment; - - self.add_radial_gradient( - clip_and_scroll, - &info, - border.gradient.center - segment_rel, - border.gradient.start_offset * border.gradient.radius.width, - border.gradient.end_offset * border.gradient.radius.width, - border.gradient.radius.width / border.gradient.radius.height, - gradient_stops, - border.gradient.extend_mode, - segment.size, - LayoutSize::zero(), - ); - } - } } } - pub fn add_gradient( + pub fn create_brush_kind_for_gradient( &mut self, - clip_and_scroll: ScrollNodeAndClipChain, info: &LayoutPrimitiveInfo, start_point: LayoutPoint, end_point: LayoutPoint, @@ -1772,13 +1718,9 @@ impl<'a> DisplayListFlattener<'a> { extend_mode: ExtendMode, stretch_size: LayoutSize, mut tile_spacing: LayoutSize, - ) { + ) -> BrushKind { let mut prim_rect = info.rect; simplify_repeated_primitive(&stretch_size, &mut tile_spacing, &mut prim_rect); - let info = LayoutPrimitiveInfo { - rect: prim_rect, - .. *info - }; // Try to ensure that if the gradient is specified in reverse, then so long as the stops // are also supplied in reverse that the rendered result will be equivalent. To do this, @@ -1798,29 +1740,21 @@ impl<'a> DisplayListFlattener<'a> { (start_point, end_point) }; - let prim = BrushPrimitive::new( - BrushKind::LinearGradient { - stops_range: stops, - extend_mode, - reverse_stops, - start_point: sp, - end_point: ep, - stops_handle: GpuCacheHandle::new(), - stretch_size, - tile_spacing, - visible_tiles: Vec::new(), - }, - None, - ); - - let prim = PrimitiveContainer::Brush(prim); - - self.add_primitive(clip_and_scroll, &info, Vec::new(), prim); + BrushKind::LinearGradient { + stops_range: stops, + extend_mode, + reverse_stops, + start_point: sp, + end_point: ep, + stops_handle: GpuCacheHandle::new(), + stretch_size, + tile_spacing, + visible_tiles: Vec::new(), + } } - pub fn add_radial_gradient( + pub fn create_brush_kind_for_radial_gradient( &mut self, - clip_and_scroll: ScrollNodeAndClipChain, info: &LayoutPrimitiveInfo, center: LayoutPoint, start_radius: f32, @@ -1830,36 +1764,22 @@ impl<'a> DisplayListFlattener<'a> { extend_mode: ExtendMode, stretch_size: LayoutSize, mut tile_spacing: LayoutSize, - ) { + ) -> BrushKind { let mut prim_rect = info.rect; simplify_repeated_primitive(&stretch_size, &mut tile_spacing, &mut prim_rect); - let info = LayoutPrimitiveInfo { - rect: prim_rect, - .. *info - }; - let prim = BrushPrimitive::new( - BrushKind::RadialGradient { - stops_range: stops, - extend_mode, - center, - start_radius, - end_radius, - ratio_xy, - stops_handle: GpuCacheHandle::new(), - stretch_size, - tile_spacing, - visible_tiles: Vec::new(), - }, - None, - ); - - self.add_primitive( - clip_and_scroll, - &info, - Vec::new(), - PrimitiveContainer::Brush(prim), - ); + BrushKind::RadialGradient { + stops_range: stops, + extend_mode, + center, + start_radius, + end_radius, + ratio_xy, + stops_handle: GpuCacheHandle::new(), + stretch_size, + tile_spacing, + visible_tiles: Vec::new(), + } } pub fn add_text( diff --git a/gfx/webrender/src/frame_builder.rs b/gfx/webrender/src/frame_builder.rs index df3a54ea3690..97b2f7b21095 100644 --- a/gfx/webrender/src/frame_builder.rs +++ b/gfx/webrender/src/frame_builder.rs @@ -4,13 +4,13 @@ use api::{BuiltDisplayList, ColorF, DeviceIntPoint, DeviceIntRect, DevicePixelScale}; use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, DocumentLayer, FontRenderMode}; -use api::{LayoutRect, LayoutSize, PipelineId, WorldPoint}; +use api::{LayoutPoint, LayoutRect, LayoutSize, PipelineId, WorldPoint}; use clip::{ClipChain, ClipStore}; 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, UvRectKind}; +use gpu_types::{PrimitiveHeaders, TransformData, UvRectKind}; use hit_test::{HitTester, HitTestingRun}; use internal_types::{FastHashMap}; use picture::PictureSurface; @@ -24,7 +24,22 @@ use std::{mem, f32}; use std::sync::Arc; use tiling::{Frame, RenderPass, RenderPassKind, RenderTargetContext}; use tiling::{ScrollbarPrimitive, SpecialRenderPasses}; -use util::{self, MaxRect, WorldToLayoutFastTransform}; +use util::{self, WorldToLayoutFastTransform}; + + +#[derive(Clone, Copy, Debug, PartialEq)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub enum ChasePrimitive { + Nothing, + LocalRect(LayoutRect), +} + +impl Default for ChasePrimitive { + fn default() -> Self { + ChasePrimitive::Nothing + } +} #[derive(Clone, Copy)] #[cfg_attr(feature = "capture", derive(Serialize))] @@ -34,6 +49,7 @@ pub struct FrameBuilderConfig { pub default_font_render_mode: FontRenderMode, pub dual_source_blending_is_supported: bool, pub dual_source_blending_is_enabled: bool, + pub chase_primitive: ChasePrimitive, } /// A builder structure for `tiling::Frame` @@ -56,14 +72,14 @@ pub struct FrameBuildingContext<'a> { pub pipelines: &'a FastHashMap>, pub screen_rect: DeviceIntRect, pub clip_scroll_tree: &'a ClipScrollTree, - pub node_data: &'a [ClipScrollNodeData], + pub transforms: &'a [TransformData], + pub max_local_clip: LayoutRect, } pub struct FrameBuildingState<'a> { pub render_tasks: &'a mut RenderTaskTree, pub profile_counters: &'a mut FrameProfileCounters, pub clip_store: &'a mut ClipStore, - pub local_clip_rects: &'a mut Vec, pub resource_cache: &'a mut ResourceCache, pub gpu_cache: &'a mut GpuCache, pub special_render_passes: &'a mut SpecialRenderPasses, @@ -99,19 +115,19 @@ impl PictureState { pub struct PrimitiveRunContext<'a> { pub clip_chain: &'a ClipChain, pub scroll_node: &'a ClipScrollNode, - pub clip_chain_rect_index: ClipChainRectIndex, + pub local_clip_rect: LayoutRect, } impl<'a> PrimitiveRunContext<'a> { pub fn new( clip_chain: &'a ClipChain, scroll_node: &'a ClipScrollNode, - clip_chain_rect_index: ClipChainRectIndex, + local_clip_rect: LayoutRect, ) -> Self { PrimitiveRunContext { clip_chain, scroll_node, - clip_chain_rect_index, + local_clip_rect, } } } @@ -132,6 +148,7 @@ impl FrameBuilder { default_font_render_mode: FontRenderMode::Mono, dual_source_blending_is_enabled: true, dual_source_blending_is_supported: false, + chase_primitive: ChasePrimitive::Nothing, }, } } @@ -169,8 +186,7 @@ impl FrameBuilder { profile_counters: &mut FrameProfileCounters, device_pixel_scale: DevicePixelScale, scene_properties: &SceneProperties, - local_clip_rects: &mut Vec, - node_data: &[ClipScrollNodeData], + transforms: &[TransformData], ) -> Option { profile_scope!("cull"); @@ -187,6 +203,8 @@ impl FrameBuilder { .expect("No display list?") .display_list; + const MAX_CLIP_COORD: f32 = 1.0e9; + let frame_context = FrameBuildingContext { scene_id: self.scene_id, device_pixel_scale, @@ -194,14 +212,17 @@ impl FrameBuilder { pipelines, screen_rect: self.screen_rect.to_i32(), clip_scroll_tree, - node_data, + transforms, + max_local_clip: LayoutRect::new( + LayoutPoint::new(-MAX_CLIP_COORD, -MAX_CLIP_COORD), + LayoutSize::new(2.0 * MAX_CLIP_COORD, 2.0 * MAX_CLIP_COORD), + ), }; let mut frame_state = FrameBuildingState { render_tasks, profile_counters, clip_store: &mut self.clip_store, - local_clip_rects, resource_cache, gpu_cache, special_render_passes, @@ -301,20 +322,13 @@ impl FrameBuilder { resource_cache.begin_frame(frame_id); gpu_cache.begin_frame(); - let mut node_data = Vec::with_capacity(clip_scroll_tree.nodes.len()); - let total_prim_runs = - self.prim_store.pictures.iter().fold(1, |count, pic| count + pic.runs.len()); - let mut clip_chain_local_clip_rects = Vec::with_capacity(total_prim_runs); - clip_chain_local_clip_rects.push(LayoutRect::max_rect()); - - clip_scroll_tree.update_tree( + let transform_palette = clip_scroll_tree.update_tree( &self.screen_rect.to_i32(), device_pixel_scale, &mut self.clip_store, resource_cache, gpu_cache, pan, - &mut node_data, scene_properties, ); @@ -335,8 +349,7 @@ impl FrameBuilder { &mut profile_counters, device_pixel_scale, scene_properties, - &mut clip_chain_local_clip_rects, - &node_data, + &transform_palette.transforms, ); resource_cache.block_until_all_resources_added(gpu_cache, @@ -369,6 +382,7 @@ impl FrameBuilder { let mut deferred_resolves = vec![]; let mut has_texture_cache_tasks = false; + let mut prim_headers = PrimitiveHeaders::new(); let use_dual_source_blending = self.config.dual_source_blending_is_enabled && self.config.dual_source_blending_is_supported; @@ -379,7 +393,7 @@ impl FrameBuilder { resource_cache, clip_scroll_tree, use_dual_source_blending, - node_data: &node_data, + transforms: &transform_palette, }; pass.build( @@ -388,6 +402,7 @@ impl FrameBuilder { &mut render_tasks, &mut deferred_resolves, &self.clip_store, + &mut prim_headers, ); if let RenderPassKind::OffScreen { ref texture_cache, .. } = pass.kind { @@ -409,13 +424,13 @@ impl FrameBuilder { layer, profile_counters, passes, - node_data, - clip_chain_local_clip_rects, + transform_palette: transform_palette.transforms, render_tasks, deferred_resolves, gpu_cache_frame_id, has_been_rendered: false, has_texture_cache_tasks, + prim_headers, } } diff --git a/gfx/webrender/src/gpu_types.rs b/gfx/webrender/src/gpu_types.rs index 5bab553beb5b..4017fbb5131f 100644 --- a/gfx/webrender/src/gpu_types.rs +++ b/gfx/webrender/src/gpu_types.rs @@ -2,29 +2,25 @@ * 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::{DevicePoint, DeviceSize, DeviceRect, LayoutToWorldTransform}; +use api::{DevicePoint, DeviceSize, DeviceRect, LayoutRect, LayoutToWorldTransform}; use api::{PremultipliedColorF, WorldToLayoutTransform}; +use clip_scroll_tree::TransformIndex; use gpu_cache::{GpuCacheAddress, GpuDataRequest}; -use prim_store::{VECS_PER_SEGMENT, EdgeAaSegmentMask}; +use prim_store::{EdgeAaSegmentMask}; use render_task::RenderTaskAddress; -use renderer::MAX_VERTEX_TEXTURE_WIDTH; +use util::{MatrixHelpers, TransformedRectKind}; // Contains type that must exactly match the same structures declared in GLSL. -const INT_BITS: usize = 31; //TODO: convert to unsigned -const CLIP_CHAIN_RECT_BITS: usize = 22; -const SEGMENT_BITS: usize = INT_BITS - CLIP_CHAIN_RECT_BITS; -// The guard ensures (at compile time) that the designated number of bits cover -// the maximum supported segment count for the texture width. -const _SEGMENT_GUARD: usize = (1 << SEGMENT_BITS) * VECS_PER_SEGMENT - MAX_VERTEX_TEXTURE_WIDTH; -const EDGE_FLAG_BITS: usize = 4; -const BRUSH_FLAG_BITS: usize = 4; -const CLIP_SCROLL_INDEX_BITS: usize = INT_BITS - EDGE_FLAG_BITS - BRUSH_FLAG_BITS; - #[derive(Copy, Clone, Debug)] #[repr(C)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] pub struct ZBufferId(i32); +#[derive(Debug)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] pub struct ZBufferIdGenerator { next: i32, } @@ -119,7 +115,7 @@ pub struct BorderInstance { #[repr(C)] pub struct ClipMaskInstance { pub render_task_address: RenderTaskAddress, - pub scroll_node_data_index: ClipScrollNodeIndex, + pub transform_id: TransformPaletteId, pub segment: i32, pub clip_data_address: GpuCacheAddress, pub resource_address: GpuCacheAddress, @@ -135,50 +131,124 @@ pub struct ClipMaskBorderCornerDotDash { pub dot_dash_data: [f32; 8], } -// 32 bytes per instance should be enough for anyone! +// 16 bytes per instance should be enough for anyone! #[derive(Debug, Clone)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct PrimitiveInstance { - data: [i32; 8], + data: [i32; 4], +} + +#[derive(Debug, Copy, Clone)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct PrimitiveHeaderIndex(pub i32); + +#[derive(Debug)] +#[repr(C)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct PrimitiveHeaders { + // The integer-type headers for a primitive. + pub headers_int: Vec, + // The float-type headers for a primitive. + pub headers_float: Vec, + // Used to generated a unique z-buffer value per primitive. + pub z_generator: ZBufferIdGenerator, +} + +impl PrimitiveHeaders { + pub fn new() -> PrimitiveHeaders { + PrimitiveHeaders { + headers_int: Vec::new(), + headers_float: Vec::new(), + z_generator: ZBufferIdGenerator::new(), + } + } + + // Add a new primitive header. + pub fn push( + &mut self, + prim_header: &PrimitiveHeader, + user_data: [i32; 3], + ) -> PrimitiveHeaderIndex { + debug_assert_eq!(self.headers_int.len(), self.headers_float.len()); + let id = self.headers_float.len(); + + self.headers_float.push(PrimitiveHeaderF { + local_rect: prim_header.local_rect, + local_clip_rect: prim_header.local_clip_rect, + }); + + self.headers_int.push(PrimitiveHeaderI { + z: self.z_generator.next(), + task_address: prim_header.task_address, + specific_prim_address: prim_header.specific_prim_address.as_int(), + clip_task_address: prim_header.clip_task_address, + transform_id: prim_header.transform_id, + user_data, + }); + + PrimitiveHeaderIndex(id as i32) + } +} + +// This is a convenience type used to make it easier to pass +// the common parts around during batching. +pub struct PrimitiveHeader { + pub local_rect: LayoutRect, + pub local_clip_rect: LayoutRect, + pub task_address: RenderTaskAddress, + pub specific_prim_address: GpuCacheAddress, + pub clip_task_address: RenderTaskAddress, + pub transform_id: TransformPaletteId, +} + +// f32 parts of a primitive header +#[derive(Debug)] +#[repr(C)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct PrimitiveHeaderF { + pub local_rect: LayoutRect, + pub local_clip_rect: LayoutRect, +} + +// i32 parts of a primitive header +// TODO(gw): Compress parts of these down to u16 +#[derive(Debug)] +#[repr(C)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct PrimitiveHeaderI { + pub z: ZBufferId, + pub task_address: RenderTaskAddress, + pub specific_prim_address: i32, + pub clip_task_address: RenderTaskAddress, + pub transform_id: TransformPaletteId, + pub user_data: [i32; 3], } pub struct GlyphInstance { - pub specific_prim_address: GpuCacheAddress, - pub task_address: RenderTaskAddress, - pub clip_task_address: RenderTaskAddress, - pub clip_chain_rect_index: ClipChainRectIndex, - pub scroll_id: ClipScrollNodeIndex, - pub z: ZBufferId, + pub prim_header_index: PrimitiveHeaderIndex, } impl GlyphInstance { pub fn new( - specific_prim_address: GpuCacheAddress, - task_address: RenderTaskAddress, - clip_task_address: RenderTaskAddress, - clip_chain_rect_index: ClipChainRectIndex, - scroll_id: ClipScrollNodeIndex, - z: ZBufferId, + prim_header_index: PrimitiveHeaderIndex, ) -> Self { GlyphInstance { - specific_prim_address, - task_address, - clip_task_address, - clip_chain_rect_index, - scroll_id, - z, + prim_header_index, } } + // TODO(gw): Some of these fields can be moved to the primitive + // header since they are constant, and some can be + // compressed to a smaller size. pub fn build(&self, data0: i32, data1: i32, data2: i32) -> PrimitiveInstance { PrimitiveInstance { data: [ - self.specific_prim_address.as_int(), - self.task_address.0 as i32 | (self.clip_task_address.0 as i32) << 16, - self.clip_chain_rect_index.0 as i32, - self.scroll_id.0 as i32, - self.z.0, + self.prim_header_index.0 as i32, data0, data1, data2, @@ -218,10 +288,6 @@ impl From for PrimitiveInstance { instance.src_task_address.0 as i32, instance.polygons_address.as_int(), instance.z.0, - 0, - 0, - 0, - 0, ], } } @@ -243,80 +309,141 @@ bitflags! { } } -// TODO(gw): While we are converting things over, we -// need to have the instance be the same -// size as an old PrimitiveInstance. In the -// future, we can compress this vertex -// format a lot - e.g. z, render task -// addresses etc can reasonably become -// a u16 type. +// TODO(gw): Some of these fields can be moved to the primitive +// header since they are constant, and some can be +// compressed to a smaller size. #[repr(C)] pub struct BrushInstance { - pub picture_address: RenderTaskAddress, - pub prim_address: GpuCacheAddress, - pub clip_chain_rect_index: ClipChainRectIndex, - pub scroll_id: ClipScrollNodeIndex, + pub prim_header_index: PrimitiveHeaderIndex, pub clip_task_address: RenderTaskAddress, - pub z: ZBufferId, pub segment_index: i32, pub edge_flags: EdgeAaSegmentMask, pub brush_flags: BrushFlags, - pub user_data: [i32; 3], } impl From for PrimitiveInstance { fn from(instance: BrushInstance) -> Self { - debug_assert_eq!(0, instance.clip_chain_rect_index.0 >> CLIP_CHAIN_RECT_BITS); - debug_assert_eq!(0, instance.scroll_id.0 >> CLIP_SCROLL_INDEX_BITS); - debug_assert_eq!(0, instance.segment_index >> SEGMENT_BITS); PrimitiveInstance { data: [ - 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 | (instance.segment_index << CLIP_CHAIN_RECT_BITS), - instance.z.0, - instance.scroll_id.0 as i32 | - ((instance.edge_flags.bits() as i32) << CLIP_SCROLL_INDEX_BITS) | - ((instance.brush_flags.bits() as i32) << (CLIP_SCROLL_INDEX_BITS + EDGE_FLAG_BITS)), - instance.user_data[0], - instance.user_data[1], - instance.user_data[2], + instance.prim_header_index.0, + instance.clip_task_address.0 as i32, + instance.segment_index | + ((instance.edge_flags.bits() as i32) << 16) | + ((instance.brush_flags.bits() as i32) << 24), + 0, ] } } } +// Represents the information about a transform palette +// entry that is passed to shaders. It includes an index +// into the transform palette, and a set of flags. The +// only flag currently used determines whether the +// transform is axis-aligned (and this should have +// pixel snapping applied). #[derive(Copy, Debug, Clone, PartialEq)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] #[repr(C)] -pub struct ClipScrollNodeIndex(pub u32); +pub struct TransformPaletteId(pub u32); -#[derive(Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -#[repr(C)] -pub struct ClipScrollNodeData { - pub transform: LayoutToWorldTransform, - pub inv_transform: WorldToLayoutTransform, - pub transform_kind: f32, - pub padding: [f32; 3], -} +impl TransformPaletteId { + // Get the palette ID for an identity transform. + pub fn identity() -> TransformPaletteId { + TransformPaletteId(0) + } -impl ClipScrollNodeData { - pub fn invalid() -> Self { - ClipScrollNodeData { - transform: LayoutToWorldTransform::identity(), - inv_transform: WorldToLayoutTransform::identity(), - transform_kind: 0.0, - padding: [0.0; 3], + // Extract the transform kind from the id. + pub fn transform_kind(&self) -> TransformedRectKind { + if (self.0 >> 24) == 0 { + TransformedRectKind::AxisAligned + } else { + TransformedRectKind::Complex } } } -#[derive(Copy, Debug, Clone, PartialEq)] +// The GPU data payload for a transform palette entry. +#[derive(Debug)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] #[repr(C)] -pub struct ClipChainRectIndex(pub usize); +pub struct TransformData { + pub transform: LayoutToWorldTransform, + pub inv_transform: WorldToLayoutTransform, +} + +impl TransformData { + pub fn invalid() -> Self { + TransformData { + transform: LayoutToWorldTransform::identity(), + inv_transform: WorldToLayoutTransform::identity(), + } + } +} + +// Extra data stored about each transform palette entry. +pub struct TransformMetadata { + pub transform_kind: TransformedRectKind, +} + +// Stores a contiguous list of TransformData structs, that +// are ready for upload to the GPU. +// TODO(gw): For now, this only stores the complete local +// to world transform for each spatial node. In +// the future, the transform palette will support +// specifying a coordinate system that the transform +// should be relative to. +pub struct TransformPalette { + pub transforms: Vec, + metadata: Vec, +} + +impl TransformPalette { + pub fn new(spatial_node_count: usize) -> TransformPalette { + TransformPalette { + transforms: Vec::with_capacity(spatial_node_count), + metadata: Vec::with_capacity(spatial_node_count), + } + } + + // Set the local -> world transform for a given spatial + // node in the transform palette. + pub fn set( + &mut self, + index: TransformIndex, + data: TransformData, + ) { + let index = index.0 as usize; + + // Pad the vectors out if they are not long enough to + // account for this index. This can occur, for instance, + // when we stop recursing down the CST due to encountering + // a node with an invalid transform. + while index >= self.transforms.len() { + self.transforms.push(TransformData::invalid()); + self.metadata.push(TransformMetadata { + transform_kind: TransformedRectKind::AxisAligned, + }); + } + + // Store the transform itself, along with metadata about it. + self.metadata[index] = TransformMetadata { + transform_kind: data.transform.transform_kind(), + }; + self.transforms[index] = data; + } + + // Get a transform palette id for the given spatial node. + // TODO(gw): In the future, it will be possible to specify + // a coordinate system id here, to allow retrieving + // transforms in the local space of a given spatial node. + pub fn get_id(&self, index: TransformIndex) -> TransformPaletteId { + let transform_kind = self.metadata[index.0 as usize].transform_kind as u32; + TransformPaletteId(index.0 | (transform_kind << 24)) + } +} // Texture cache resources can be either a simple rect, or define // a polygon within a rect by specifying a UV coordinate for each diff --git a/gfx/webrender/src/lib.rs b/gfx/webrender/src/lib.rs index fe29f4511fa7..80589a5a7eb4 100644 --- a/gfx/webrender/src/lib.rs +++ b/gfx/webrender/src/lib.rs @@ -182,6 +182,7 @@ pub extern crate webrender_api; #[doc(hidden)] pub use device::{build_shader_strings, ReadPixelsFormat, UploadMethod, VertexUsageHint}; pub use device::{ProgramBinary, ProgramCache, ProgramCacheObserver, ProgramSources}; +pub use frame_builder::ChasePrimitive; pub use renderer::{AsyncPropertySampler, CpuProfile, DebugFlags, OutputImageHandler, RendererKind}; pub use renderer::{ExternalImage, ExternalImageHandler, ExternalImageSource, GpuProfile}; pub use renderer::{GraphicsApi, GraphicsApiInfo, PipelineInfo, Renderer, RendererOptions}; diff --git a/gfx/webrender/src/picture.rs b/gfx/webrender/src/picture.rs index e6480c430255..4ee31000cafd 100644 --- a/gfx/webrender/src/picture.rs +++ b/gfx/webrender/src/picture.rs @@ -276,7 +276,7 @@ impl PicturePrimitive { self.can_draw_directly_to_parent_surface() } - pub fn prepare_for_render_inner( + pub fn prepare_for_render( &mut self, prim_index: PrimitiveIndex, prim_metadata: &mut PrimitiveMetadata, @@ -489,16 +489,9 @@ impl PicturePrimitive { // that writes a brush primitive header. // Basic brush primitive header is (see end of prepare_prim_for_render_inner in prim_store.rs) - // local_rect - // clip_rect // [brush specific data] // [segment_rect, segment data] let shadow_rect = prim_metadata.local_rect.translate(&offset); - let shadow_clip_rect = prim_metadata.local_clip_rect.translate(&offset); - - // local_rect, clip_rect - request.push(shadow_rect); - request.push(shadow_clip_rect); // ImageBrush colors request.push(color.premultiplied()); @@ -592,27 +585,6 @@ impl PicturePrimitive { } } } - - pub fn prepare_for_render( - &mut self, - prim_index: PrimitiveIndex, - prim_metadata: &mut PrimitiveMetadata, - prim_run_context: &PrimitiveRunContext, - pic_state_for_children: PictureState, - pic_state: &mut PictureState, - frame_context: &FrameBuildingContext, - frame_state: &mut FrameBuildingState, - ) { - self.prepare_for_render_inner( - prim_index, - prim_metadata, - prim_run_context, - pic_state_for_children, - pic_state, - frame_context, - frame_state, - ); - } } // Calculate a single screen-space UV for a picture. diff --git a/gfx/webrender/src/prim_store.rs b/gfx/webrender/src/prim_store.rs index 5805b389adf7..c5b3f9105528 100644 --- a/gfx/webrender/src/prim_store.rs +++ b/gfx/webrender/src/prim_store.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::{AlphaType, BorderRadius, BoxShadowClipMode, BuiltDisplayList, ClipMode, ColorF, ComplexClipRegion}; +use api::{AlphaType, BorderRadius, BoxShadowClipMode, BuiltDisplayList, ClipMode, ColorF}; use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, Epoch, ExtendMode}; use api::{FilterOp, GlyphInstance, GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag, TileOffset}; use api::{GlyphRasterSpace, LayoutPoint, LayoutRect, LayoutSize, LayoutToWorldTransform, LayoutVector2D}; @@ -20,7 +20,7 @@ use frame_builder::PrimitiveRunContext; use glyph_rasterizer::{FontInstance, FontTransform, GlyphKey, FONT_SIZE_LIMIT}; use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest, ToGpuBlocks}; -use gpu_types::{BrushFlags, ClipChainRectIndex}; +use gpu_types::BrushFlags; use image::{for_each_tile, for_each_repetition}; use picture::{PictureCompositeMode, PictureId, PicturePrimitive}; #[cfg(debug_assertions)] @@ -50,7 +50,7 @@ impl ScrollNodeAndClipChain { pub fn new( scroll_node_id: ClipScrollNodeIndex, clip_chain_index: ClipChainIndex - ) -> ScrollNodeAndClipChain { + ) -> Self { ScrollNodeAndClipChain { scroll_node_id, clip_chain_index } } } @@ -62,6 +62,17 @@ pub struct PrimitiveRun { pub clip_and_scroll: ScrollNodeAndClipChain, } +impl PrimitiveRun { + pub fn is_chasing(&self, index: Option) -> bool { + match index { + Some(id) if cfg!(debug_assertions) => { + self.base_prim_index <= id && id.0 < self.base_prim_index.0 + self.count + } + _ => false, + } + } +} + #[derive(Debug, Copy, Clone)] pub struct PrimitiveOpacity { pub is_opaque: bool, @@ -174,7 +185,11 @@ pub struct PrimitiveMetadata { // storing them here. pub local_rect: LayoutRect, pub local_clip_rect: LayoutRect, - pub clip_chain_rect_index: ClipChainRectIndex, + + // The current combined local clip for this primitive, from + // the primitive local clip above and the current clip chain. + pub combined_local_clip_rect: LayoutRect, + pub is_backface_visible: bool, pub screen_rect: Option, @@ -234,11 +249,15 @@ pub struct VisibleImageTile { pub tile_offset: TileOffset, pub handle: GpuCacheHandle, pub edge_flags: EdgeAaSegmentMask, + pub local_rect: LayoutRect, + pub local_clip_rect: LayoutRect, } #[derive(Debug)] pub struct VisibleGradientTile { pub handle: GpuCacheHandle, + pub local_rect: LayoutRect, + pub local_clip_rect: LayoutRect, } #[derive(Debug)] @@ -418,14 +437,14 @@ impl BrushPrimitive { pub fn new( kind: BrushKind, segment_desc: Option, - ) -> BrushPrimitive { + ) -> Self { BrushPrimitive { kind, segment_desc, } } - pub fn new_picture(pic_index: PictureIndex) -> BrushPrimitive { + pub fn new_picture(pic_index: PictureIndex) -> Self { BrushPrimitive { kind: BrushKind::Picture { pic_index, @@ -719,7 +738,6 @@ impl<'a> GradientGpuBlockBuilder<'a> { self.fill_colors(cur_idx, GRADIENT_DATA_TABLE_END, &PremultipliedColorF::WHITE, &cur_color, &mut entries); } - // Fill in the last entry with the last color stop self.fill_colors( GRADIENT_DATA_LAST_STOP, @@ -739,12 +757,12 @@ impl<'a> GradientGpuBlockBuilder<'a> { #[derive(Debug, Clone)] pub struct TextRunPrimitiveCpu { - pub font: FontInstance, + pub specified_font: FontInstance, + pub used_font: FontInstance, pub offset: LayoutVector2D, pub glyph_range: ItemRange, pub glyph_keys: Vec, pub glyph_gpu_blocks: Vec, - pub glyph_transform: (DevicePixelScale, FontTransform), pub shadow: bool, pub glyph_raster_space: GlyphRasterSpace, } @@ -759,60 +777,97 @@ impl TextRunPrimitiveCpu { glyph_raster_space: GlyphRasterSpace, ) -> Self { TextRunPrimitiveCpu { - font, + specified_font: font.clone(), + used_font: font, offset, glyph_range, glyph_keys, glyph_gpu_blocks: Vec::new(), - glyph_transform: (DevicePixelScale::new(1.0), FontTransform::identity()), shadow, glyph_raster_space, } } - pub fn get_font( - &self, + pub fn update_font_instance( + &mut self, device_pixel_scale: DevicePixelScale, - transform: LayoutToWorldTransform, - ) -> FontInstance { - let mut font = self.font.clone(); - font.size = font.size.scale_by(device_pixel_scale.0); + transform: &LayoutToWorldTransform, + allow_subpixel_aa: bool, + ) -> bool { + // Get the current font size in device pixels + let device_font_size = self.specified_font.size.scale_by(device_pixel_scale.0); + + // Determine if rasterizing glyphs in local or screen space. // Only support transforms that can be coerced to simple 2D transforms. - if transform.has_perspective_component() || + let transform_glyphs = if transform.has_perspective_component() || !transform.has_2d_inverse() || // Font sizes larger than the limit need to be scaled, thus can't use subpixels. - transform.exceeds_2d_scale(FONT_SIZE_LIMIT / font.size.to_f64_px()) || + transform.exceeds_2d_scale(FONT_SIZE_LIMIT / device_font_size.to_f64_px()) || // Otherwise, ensure the font is rasterized in screen-space. self.glyph_raster_space != GlyphRasterSpace::Screen { - font.disable_subpixel_aa(); - font.disable_subpixel_position(); + false } else { + true + }; + + // Get the font transform matrix (skew / scale) from the complete transform. + let font_transform = if transform_glyphs { // Quantize the transform to minimize thrashing of the glyph cache. - font.transform = FontTransform::from(&transform).quantize(); + FontTransform::from(transform).quantize() + } else { + FontTransform::identity() + }; + + // If the transform or device size is different, then the caller of + // this method needs to know to rebuild the glyphs. + let cache_dirty = + self.used_font.transform != font_transform || + self.used_font.size != device_font_size; + + // Construct used font instance from the specified font instance + self.used_font = FontInstance { + transform: font_transform, + size: device_font_size, + ..self.specified_font.clone() + }; + + // If subpixel AA is disabled due to the backing surface the glyphs + // are being drawn onto, disable it (unless we are using the + // specifial subpixel mode that estimates background color). + if !allow_subpixel_aa && self.specified_font.bg_color.a == 0 { + self.used_font.disable_subpixel_aa(); } - font + + // If using local space glyphs, we don't want subpixel AA + // or positioning. + if !transform_glyphs { + self.used_font.disable_subpixel_aa(); + self.used_font.disable_subpixel_position(); + } + + cache_dirty } fn prepare_for_render( &mut self, device_pixel_scale: DevicePixelScale, - transform: LayoutToWorldTransform, + transform: &LayoutToWorldTransform, allow_subpixel_aa: bool, display_list: &BuiltDisplayList, frame_building_state: &mut FrameBuildingState, ) { - if !allow_subpixel_aa && self.font.bg_color.a == 0 { - self.font.disable_subpixel_aa(); - } - - let font = self.get_font(device_pixel_scale, transform); + let cache_dirty = self.update_font_instance( + device_pixel_scale, + transform, + allow_subpixel_aa, + ); // Cache the glyph positions, if not in the cache already. // TODO(gw): In the future, remove `glyph_instances` // completely, and just reference the glyphs // directly from the display list. - if self.glyph_keys.is_empty() || self.glyph_transform != (device_pixel_scale, font.transform) { - let subpx_dir = font.get_subpx_dir(); + if self.glyph_keys.is_empty() || cache_dirty { + let subpx_dir = self.used_font.get_subpx_dir(); let src_glyphs = display_list.get(self.glyph_range); // TODO(gw): If we support chunks() on AuxIter @@ -820,7 +875,7 @@ impl TextRunPrimitiveCpu { // be much simpler... let mut gpu_block = [0.0; 4]; for (i, src) in src_glyphs.enumerate() { - let world_offset = font.transform.transform(&src.point); + let world_offset = self.used_font.transform.transform(&src.point); let device_offset = device_pixel_scale.transform_point(&world_offset); let key = GlyphKey::new(src.index, device_offset, subpx_dir); self.glyph_keys.push(key); @@ -842,12 +897,10 @@ impl TextRunPrimitiveCpu { if (self.glyph_keys.len() & 1) != 0 { self.glyph_gpu_blocks.push(gpu_block.into()); } - - self.glyph_transform = (device_pixel_scale, font.transform); } frame_building_state.resource_cache - .request_glyphs(font, + .request_glyphs(self.used_font.clone(), &self.glyph_keys, frame_building_state.gpu_cache, frame_building_state.render_tasks, @@ -855,9 +908,9 @@ impl TextRunPrimitiveCpu { } fn write_gpu_blocks(&self, request: &mut GpuDataRequest) { - request.push(ColorF::from(self.font.color).premultiplied()); + request.push(ColorF::from(self.used_font.color).premultiplied()); // this is the only case where we need to provide plain color to GPU - let bg_color = ColorF::from(self.font.bg_color); + let bg_color = ColorF::from(self.used_font.bg_color); request.push([bg_color.r, bg_color.g, bg_color.b, 1.0]); request.push([ self.offset.x, @@ -1071,7 +1124,7 @@ impl PrimitiveContainer { pub fn is_visible(&self) -> bool { match *self { PrimitiveContainer::TextRun(ref info) => { - info.font.color.a > 0 + info.specified_font.color.a > 0 } PrimitiveContainer::Brush(ref brush) => { match brush.kind { @@ -1100,7 +1153,7 @@ impl PrimitiveContainer { PrimitiveContainer::TextRun(ref info) => { let mut font = FontInstance { color: shadow.color.into(), - ..info.font.clone() + ..info.specified_font.clone() }; if shadow.blur_radius > 0.0 { font.disable_subpixel_aa(); @@ -1146,6 +1199,9 @@ pub struct PrimitiveStore { pub pictures: Vec, next_picture_id: u64, + + /// A primitive index to chase through debugging. + pub chase_id: Option, } impl PrimitiveStore { @@ -1157,6 +1213,8 @@ impl PrimitiveStore { pictures: Vec::new(), next_picture_id: 0, + + chase_id: None, } } @@ -1168,6 +1226,8 @@ impl PrimitiveStore { pictures: recycle_vec(self.pictures), next_picture_id: self.next_picture_id, + + chase_id: self.chase_id, } } @@ -1213,7 +1273,7 @@ impl PrimitiveStore { clip_task_id: None, local_rect: *local_rect, local_clip_rect: *local_clip_rect, - clip_chain_rect_index: ClipChainRectIndex(0), + combined_local_clip_rect: *local_clip_rect, is_backface_visible, screen_rect: None, tag, @@ -1495,7 +1555,7 @@ impl PrimitiveStore { let transform = prim_run_context.scroll_node.world_content_transform.into(); text.prepare_for_render( frame_context.device_pixel_scale, - transform, + &transform, pic_context.allow_subpixel_aa, pic_context.display_list, frame_state, @@ -1644,7 +1704,9 @@ impl PrimitiveStore { // Tighten the clip rect because decomposing the repeated image can // produce primitives that are partially covering the original image // rect and we want to clip these extra parts out. - let tight_clip_rect = metadata.local_clip_rect.intersection(&metadata.local_rect).unwrap(); + let tight_clip_rect = metadata + .combined_local_clip_rect + .intersection(&metadata.local_rect).unwrap(); let visible_rect = compute_conservative_visible_rect( prim_run_context, @@ -1684,8 +1746,6 @@ impl PrimitiveStore { let mut handle = GpuCacheHandle::new(); if let Some(mut request) = frame_state.gpu_cache.request(&mut handle) { - request.push(*tile_rect); - request.push(tight_clip_rect); request.push(ColorF::new(1.0, 1.0, 1.0, opacity_binding.current).premultiplied()); request.push(PremultipliedColorF::WHITE); request.push([tile_rect.size.width, tile_rect.size.height, 0.0, 0.0]); @@ -1696,6 +1756,8 @@ impl PrimitiveStore { tile_offset, handle, edge_flags: tile_flags & edge_flags, + local_rect: *tile_rect, + local_clip_rect: tight_clip_rect, }); } ); @@ -1789,9 +1851,7 @@ impl PrimitiveStore { prim_run_context, frame_context, frame_state, - &mut |rect, clip_rect, mut request| { - request.push(*rect); - request.push(*clip_rect); + &mut |rect, mut request| { request.push([ center.x, center.y, @@ -1841,9 +1901,7 @@ impl PrimitiveStore { prim_run_context, frame_context, frame_state, - &mut |rect, clip_rect, mut request| { - request.push(*rect); - request.push(*clip_rect); + &mut |rect, mut request| { request.push([ start_point.x, start_point.y, @@ -1895,10 +1953,6 @@ impl PrimitiveStore { // Mark this GPU resource as required for this frame. if let Some(mut request) = frame_state.gpu_cache.request(&mut metadata.gpu_location) { - // has to match VECS_PER_BRUSH_PRIM - request.push(metadata.local_rect); - request.push(metadata.local_clip_rect); - match metadata.prim_kind { PrimitiveKind::TextRun => { let text = &self.cpu_text_runs[metadata.cpu_prim_index.0]; @@ -2042,11 +2096,11 @@ impl PrimitiveStore { // node and the primitive, we need to get the clip rect in the // local space of the primitive, in order to generate correct // local segments. - let local_clip_rect = if clip_item.scroll_node_data_index == prim_run_context.scroll_node.node_data_index { + let local_clip_rect = if clip_item.transform_index == prim_run_context.scroll_node.transform_index { local_clip_rect } else { let clip_transform = frame_context - .node_data[clip_item.scroll_node_data_index.0 as usize] + .transforms[clip_item.transform_index.0 as usize] .transform; let prim_transform = &prim_run_context.scroll_node.world_content_transform; let relative_transform = prim_transform @@ -2193,12 +2247,19 @@ impl PrimitiveStore { frame_context: &FrameBuildingContext, frame_state: &mut FrameBuildingState, ) -> bool { + if cfg!(debug_assertions) && Some(prim_index) == self.chase_id { + println!("\tupdating clip task with screen rect {:?}", prim_screen_rect); + } // 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, None => { + if cfg!(debug_assertions) && Some(prim_index) == self.chase_id { + println!("\tculled by the intersection with frame rect {:?}", + frame_context.screen_rect); + } self.cpu_metadata[prim_index.0].screen_rect = None; return false; } @@ -2207,6 +2268,9 @@ impl PrimitiveStore { let mut combined_outer_rect = prim_screen_rect.intersection(&prim_run_context.clip_chain.combined_outer_screen_rect); let clip_chain = prim_run_context.clip_chain.nodes.clone(); + if cfg!(debug_assertions) && Some(prim_index) == self.chase_id { + println!("\tbase combined outer rect {:?}", combined_outer_rect); + } let prim_coordinate_system_id = prim_run_context.scroll_node.coordinate_system_id; let transform = &prim_run_context.scroll_node.world_content_transform; @@ -2228,10 +2292,13 @@ impl PrimitiveStore { if let Some(outer) = screen_outer_rect { combined_outer_rect = combined_outer_rect.and_then(|r| r.intersection(&outer)); } + if cfg!(debug_assertions) && Some(prim_index) == self.chase_id { + println!("\tfound extra clip with screen bounds {:?}", screen_outer_rect); + } Arc::new(ClipChainNode { work_item: ClipWorkItem { - scroll_node_data_index: prim_run_context.scroll_node.node_data_index, + transform_index: prim_run_context.scroll_node.transform_index, clip_sources: clip_sources.weak(), coordinate_system_id: prim_coordinate_system_id, }, @@ -2251,6 +2318,9 @@ impl PrimitiveStore { let combined_outer_rect = match combined_outer_rect { Some(rect) if !rect.is_empty() => rect, _ => { + if cfg!(debug_assertions) && Some(prim_index) == self.chase_id { + println!("\tculled by the empty combined screen rect"); + } self.cpu_metadata[prim_index.0].screen_rect = None; return false; } @@ -2274,12 +2344,18 @@ impl PrimitiveStore { // If we don't have any clips from other coordinate systems, the local clip // calculated from the clip chain should be sufficient to ensure proper clipping. if !has_clips_from_other_coordinate_systems { + if cfg!(debug_assertions) && Some(prim_index) == self.chase_id { + println!("\tneed no task: all clips are within the coordinate system"); + } return true; } // If we have filtered all clips and the screen rect isn't any smaller, we can just // skip masking entirely. if combined_outer_rect == prim_screen_rect { + if cfg!(debug_assertions) && Some(prim_index) == self.chase_id { + println!("\tneed no task: combined rect is not smaller"); + } return true; } // Otherwise we create an empty mask, but with an empty inner rect to avoid further @@ -2288,7 +2364,10 @@ impl PrimitiveStore { } if combined_inner_rect.contains_rect(&prim_screen_rect) { - return true; + if cfg!(debug_assertions) && Some(prim_index) == self.chase_id { + println!("\tneed no task: contained within the clip inner rect"); + } + return true; } // First try to render this primitive's mask using optimized brush rendering. @@ -2302,6 +2381,9 @@ impl PrimitiveStore { frame_context, frame_state, ) { + if cfg!(debug_assertions) && Some(prim_index) == self.chase_id { + println!("\tsegment tasks have been created for clipping"); + } return true; } @@ -2316,6 +2398,10 @@ impl PrimitiveStore { ); let clip_task_id = frame_state.render_tasks.add(clip_task); + if cfg!(debug_assertions) && Some(prim_index) == self.chase_id { + println!("\tcreated task {:?} with combined outer rect {:?}", + clip_task_id, combined_outer_rect); + } self.cpu_metadata[prim_index.0].clip_task_id = Some(clip_task_id); pic_state.tasks.push(clip_task_id); @@ -2341,6 +2427,9 @@ impl PrimitiveStore { if !metadata.is_backface_visible && prim_run_context.scroll_node.world_content_transform.is_backface_visible() { + if cfg!(debug_assertions) && Some(prim_index) == self.chase_id { + println!("\tculled for not having visible back faces"); + } return None; } @@ -2358,6 +2447,9 @@ impl PrimitiveStore { let pic = &mut self.pictures[pic_index.0]; if !pic.resolve_scene_properties(frame_context.scene_properties) { + if cfg!(debug_assertions) && Some(prim_index) == self.chase_id { + println!("\tculled for carrying an invisible composite filter"); + } return None; } @@ -2429,7 +2521,9 @@ impl PrimitiveStore { let metadata = &mut self.cpu_metadata[prim_index.0]; if metadata.local_rect.size.width <= 0.0 || metadata.local_rect.size.height <= 0.0 { - //warn!("invalid primitive rect {:?}", metadata.local_rect); + if cfg!(debug_assertions) && Some(prim_index) == self.chase_id { + println!("\tculled for zero local rectangle"); + } return None; } @@ -2441,14 +2535,33 @@ impl PrimitiveStore { // taken into account. let local_rect = metadata.local_rect .inflate(pic_context.inflation_factor, pic_context.inflation_factor) - .intersection(&metadata.local_clip_rect)?; + .intersection(&metadata.local_clip_rect); + let local_rect = match local_rect { + Some(local_rect) => local_rect, + None => { + if cfg!(debug_assertions) && Some(prim_index) == self.chase_id { + println!("\tculled for being out of the local clip rectangle: {:?}", + metadata.local_clip_rect); + } + return None + } + }; - let unclipped = calculate_screen_bounding_rect( + let unclipped = match calculate_screen_bounding_rect( &prim_run_context.scroll_node.world_content_transform, &local_rect, frame_context.device_pixel_scale, None, //TODO: inflate `frame_context.screen_rect` appropriately - )?; + ) { + Some(rect) => rect, + None => { + if cfg!(debug_assertions) && Some(prim_index) == self.chase_id { + println!("\tculled for being behind the near plane of transform: {:?}", + prim_run_context.scroll_node.world_content_transform); + } + return None + } + }; let clipped = unclipped .intersection(&prim_run_context.clip_chain.combined_outer_screen_rect)?; @@ -2457,7 +2570,11 @@ impl PrimitiveStore { clipped, unclipped, }); - metadata.clip_chain_rect_index = prim_run_context.clip_chain_rect_index; + + metadata.combined_local_clip_rect = prim_run_context + .local_clip_rect + .intersection(&metadata.local_clip_rect) + .unwrap_or(LayoutRect::zero()); (local_rect, unclipped) }; @@ -2480,6 +2597,10 @@ impl PrimitiveStore { return None; } + if cfg!(debug_assertions) && Some(prim_index) == self.chase_id { + println!("\tconsidered visible and ready with local rect {:?}", local_rect); + } + self.prepare_prim_for_render_inner( prim_index, prim_run_context, @@ -2514,6 +2635,10 @@ impl PrimitiveStore { }; for run in &pic_context.prim_runs { + if run.is_chasing(self.chase_id) { + println!("\tpreparing a run of length {} in pipeline {:?}", + run.count, pic_context.pipeline_id); + } // TODO(gw): Perhaps we can restructure this to not need to create // a new primitive context for every run (if the hash // lookups ever show up in a profile). @@ -2531,12 +2656,16 @@ impl PrimitiveStore { pic_state.has_non_root_coord_system |= clip_chain.has_non_root_coord_system; if !scroll_node.invertible { - debug!("{:?} {:?}: position not invertible", run.base_prim_index, pic_context.pipeline_id); + if run.is_chasing(self.chase_id) { + println!("\tculled for the scroll node transform being invertible"); + } continue; } if clip_chain.combined_outer_screen_rect.is_empty() { - debug!("{:?} {:?}: clipped out", run.base_prim_index, pic_context.pipeline_id); + if run.is_chasing(self.chase_id) { + println!("\tculled for out of screen bounds"); + } continue; } @@ -2565,19 +2694,16 @@ impl PrimitiveStore { None }; - let clip_chain_rect_index = match clip_chain_rect { + let local_clip_chain_rect = match clip_chain_rect { Some(rect) if rect.is_empty() => continue, - Some(rect) => { - frame_state.local_clip_rects.push(rect); - ClipChainRectIndex(frame_state.local_clip_rects.len() - 1) - } - None => ClipChainRectIndex(0), // This is no clipping. + Some(rect) => rect, + None => frame_context.max_local_clip, }; let child_prim_run_context = PrimitiveRunContext::new( clip_chain, scroll_node, - clip_chain_rect_index, + local_clip_chain_rect, ); for i in 0 .. run.count { @@ -2611,6 +2737,12 @@ impl PrimitiveStore { result.local_rect_in_original_parent_space = result.local_rect_in_original_parent_space.union(&bounds); } + + if let Some(ref matrix) = parent_relative_transform { + let bounds = matrix.transform_rect(&prim_local_rect); + result.local_rect_in_actual_parent_space = + result.local_rect_in_actual_parent_space.union(&bounds); + } } } } @@ -2646,14 +2778,16 @@ fn decompose_repeated_primitive( prim_run_context: &PrimitiveRunContext, frame_context: &FrameBuildingContext, frame_state: &mut FrameBuildingState, - callback: &mut FnMut(&LayoutRect, &LayoutRect, GpuDataRequest), + callback: &mut FnMut(&LayoutRect, GpuDataRequest), ) { visible_tiles.clear(); // Tighten the clip rect because decomposing the repeated image can // produce primitives that are partially covering the original image // rect and we want to clip these extra parts out. - let tight_clip_rect = metadata.local_clip_rect.intersection(&metadata.local_rect).unwrap(); + let tight_clip_rect = metadata + .combined_local_clip_rect + .intersection(&metadata.local_rect).unwrap(); let visible_rect = compute_conservative_visible_rect( prim_run_context, @@ -2669,16 +2803,19 @@ fn decompose_repeated_primitive( &mut |origin, _| { let mut handle = GpuCacheHandle::new(); + let rect = LayoutRect { + origin: *origin, + size: *stretch_size, + }; if let Some(request) = frame_state.gpu_cache.request(&mut handle) { - let rect = LayoutRect { - origin: *origin, - size: *stretch_size, - }; - - callback(&rect, &tight_clip_rect, request); + callback(&rect, request); } - visible_tiles.push(VisibleGradientTile { handle }); + visible_tiles.push(VisibleGradientTile { + local_rect: rect, + local_clip_rect: tight_clip_rect, + handle + }); } ); @@ -2725,31 +2862,6 @@ fn edge_flags_for_tile_spacing(tile_spacing: &LayoutSize) -> EdgeAaSegmentMask { flags } -//Test for one clip region contains another -trait InsideTest { - fn might_contain(&self, clip: &T) -> bool; -} - -impl InsideTest for ComplexClipRegion { - // Returns true if clip is inside self, can return false negative - fn might_contain(&self, clip: &ComplexClipRegion) -> bool { - let delta_left = clip.rect.origin.x - self.rect.origin.x; - let delta_top = clip.rect.origin.y - self.rect.origin.y; - let delta_right = self.rect.max_x() - clip.rect.max_x(); - let delta_bottom = self.rect.max_y() - clip.rect.max_y(); - - delta_left >= 0f32 && delta_top >= 0f32 && delta_right >= 0f32 && delta_bottom >= 0f32 && - clip.radii.top_left.width >= self.radii.top_left.width - delta_left && - clip.radii.top_left.height >= self.radii.top_left.height - delta_top && - clip.radii.top_right.width >= self.radii.top_right.width - delta_right && - clip.radii.top_right.height >= self.radii.top_right.height - delta_top && - clip.radii.bottom_left.width >= self.radii.bottom_left.width - delta_left && - clip.radii.bottom_left.height >= self.radii.bottom_left.height - delta_bottom && - clip.radii.bottom_right.width >= self.radii.bottom_right.width - delta_right && - clip.radii.bottom_right.height >= self.radii.bottom_right.height - delta_bottom - } -} - fn convert_clip_chain_to_clip_vector( clip_chain_nodes: ClipChainNodeRef, extra_clip: ClipChainNodeRef, diff --git a/gfx/webrender/src/render_backend.rs b/gfx/webrender/src/render_backend.rs index 30ae069a8fa0..58a02348163e 100644 --- a/gfx/webrender/src/render_backend.rs +++ b/gfx/webrender/src/render_backend.rs @@ -1063,6 +1063,7 @@ impl RenderBackend { debug_assert!(op.render || !op.composite); + let mut render_time = None; if op.render && doc.has_pixels() { profile_scope!("generate frame"); @@ -1071,6 +1072,7 @@ impl RenderBackend { // borrow ck hack for profile_counters let (pending_update, rendered_document) = { let _timer = profile_counters.total_time.timer(); + let render_start_time = precise_time_ns(); let rendered_document = doc.render( &mut self.resource_cache, @@ -1085,6 +1087,8 @@ impl RenderBackend { let msg = ResultMsg::UpdateGpuCache(self.gpu_cache.extract_updates()); self.result_tx.send(msg).unwrap(); + render_time = Some(precise_time_ns() - render_start_time); + let pending_update = self.resource_cache.pending_updates(); (pending_update, rendered_document) }; @@ -1111,7 +1115,7 @@ impl RenderBackend { } if transaction_msg.generate_frame { - self.notifier.new_frame_ready(document_id, op.scroll, op.composite); + self.notifier.new_frame_ready(document_id, op.scroll, op.composite, render_time); } } @@ -1410,7 +1414,7 @@ impl RenderBackend { self.result_tx.send(msg_publish).unwrap(); profile_counters.reset(); - self.notifier.new_frame_ready(id, false, true); + self.notifier.new_frame_ready(id, false, true, None); self.documents.insert(id, doc); } } diff --git a/gfx/webrender/src/renderer.rs b/gfx/webrender/src/renderer.rs index db52731f2e17..0bc01a8224e6 100644 --- a/gfx/webrender/src/renderer.rs +++ b/gfx/webrender/src/renderer.rs @@ -26,7 +26,7 @@ use device::{FileWatcherHandler, ShaderError, TextureFilter, VertexUsageHint, VAO, VBO, CustomVAO}; use device::{ProgramCache, ReadPixelsFormat}; use euclid::{rect, Transform3D}; -use frame_builder::FrameBuilderConfig; +use frame_builder::{ChasePrimitive, FrameBuilderConfig}; use gleam::gl; use glyph_rasterizer::{GlyphFormat, GlyphRasterizer}; use gpu_cache::{GpuBlockData, GpuCacheUpdate, GpuCacheUpdateList}; @@ -270,14 +270,15 @@ pub(crate) enum TextureSampler { CacheA8, CacheRGBA8, ResourceCache, - ClipScrollNodes, + TransformPalette, RenderTasks, Dither, // A special sampler that is bound to the A8 output of // the *first* pass. Items rendered in this target are // available as inputs to tasks in any subsequent pass. SharedCacheA8, - LocalClipRects + PrimitiveHeadersF, + PrimitiveHeadersI, } impl TextureSampler { @@ -302,11 +303,12 @@ impl Into for TextureSampler { TextureSampler::CacheA8 => TextureSlot(3), TextureSampler::CacheRGBA8 => TextureSlot(4), TextureSampler::ResourceCache => TextureSlot(5), - TextureSampler::ClipScrollNodes => TextureSlot(6), + TextureSampler::TransformPalette => TextureSlot(6), TextureSampler::RenderTasks => TextureSlot(7), TextureSampler::Dither => TextureSlot(8), TextureSampler::SharedCacheA8 => TextureSlot(9), - TextureSampler::LocalClipRects => TextureSlot(10), + TextureSampler::PrimitiveHeadersF => TextureSlot(10), + TextureSampler::PrimitiveHeadersI => TextureSlot(11), } } } @@ -330,12 +332,7 @@ pub(crate) mod desc { ], instance_attributes: &[ VertexAttribute { - name: "aData0", - count: 4, - kind: VertexAttributeKind::I32, - }, - VertexAttribute { - name: "aData1", + name: "aData", count: 4, kind: VertexAttributeKind::I32, }, @@ -1215,8 +1212,14 @@ struct VertexDataTexture { } impl VertexDataTexture { - fn new(device: &mut Device) -> VertexDataTexture { - let texture = device.create_texture(TextureTarget::Default, ImageFormat::RGBAF32); + fn new( + device: &mut Device, + format: ImageFormat, + ) -> VertexDataTexture { + let texture = device.create_texture( + TextureTarget::Default, + format, + ); let pbo = device.create_pbo(); VertexDataTexture { texture, pbo } @@ -1369,8 +1372,9 @@ pub struct Renderer { pub gpu_profile: GpuProfiler, vaos: RendererVAOs, - node_data_texture: VertexDataTexture, - local_clip_rects_texture: VertexDataTexture, + prim_header_f_texture: VertexDataTexture, + prim_header_i_texture: VertexDataTexture, + transforms_texture: VertexDataTexture, render_task_texture: VertexDataTexture, gpu_cache_texture: CacheTexture, @@ -1629,9 +1633,10 @@ impl Renderer { let texture_resolver = SourceTextureResolver::new(&mut device); - let node_data_texture = VertexDataTexture::new(&mut device); - let local_clip_rects_texture = VertexDataTexture::new(&mut device); - let render_task_texture = VertexDataTexture::new(&mut device); + let prim_header_f_texture = VertexDataTexture::new(&mut device, ImageFormat::RGBAF32); + let prim_header_i_texture = VertexDataTexture::new(&mut device, ImageFormat::RGBAI32); + let transforms_texture = VertexDataTexture::new(&mut device, ImageFormat::RGBAF32); + let render_task_texture = VertexDataTexture::new(&mut device, ImageFormat::RGBAF32); let gpu_cache_texture = CacheTexture::new( &mut device, @@ -1653,6 +1658,7 @@ impl Renderer { default_font_render_mode, dual_source_blending_is_enabled: true, dual_source_blending_is_supported: ext_dual_source_blending, + chase_primitive: options.chase_primitive, }; let device_pixel_ratio = options.device_pixel_ratio; @@ -1784,8 +1790,9 @@ impl Renderer { dash_and_dot_vao, border_vao, }, - node_data_texture, - local_clip_rects_texture, + transforms_texture, + prim_header_i_texture, + prim_header_f_texture, render_task_texture, pipeline_info: PipelineInfo::default(), dither_matrix_texture, @@ -3533,16 +3540,31 @@ impl Renderer { let _timer = self.gpu_profile.start_timer(GPU_TAG_SETUP_DATA); self.device.set_device_pixel_ratio(frame.device_pixel_ratio); - self.node_data_texture.update(&mut self.device, &mut frame.node_data); - self.device.bind_texture(TextureSampler::ClipScrollNodes, &self.node_data_texture.texture); - - self.local_clip_rects_texture.update( + self.prim_header_f_texture.update( &mut self.device, - &mut frame.clip_chain_local_clip_rects + &mut frame.prim_headers.headers_float, ); self.device.bind_texture( - TextureSampler::LocalClipRects, - &self.local_clip_rects_texture.texture + TextureSampler::PrimitiveHeadersF, + &self.prim_header_f_texture.texture, + ); + + self.prim_header_i_texture.update( + &mut self.device, + &mut frame.prim_headers.headers_int, + ); + self.device.bind_texture( + TextureSampler::PrimitiveHeadersI, + &self.prim_header_i_texture.texture, + ); + + self.transforms_texture.update( + &mut self.device, + &mut frame.transform_palette, + ); + self.device.bind_texture( + TextureSampler::TransformPalette, + &self.transforms_texture.texture, ); self.render_task_texture @@ -3929,8 +3951,9 @@ impl Renderer { if let Some(dither_matrix_texture) = self.dither_matrix_texture { self.device.delete_texture(dither_matrix_texture); } - self.node_data_texture.deinit(&mut self.device); - self.local_clip_rects_texture.deinit(&mut self.device); + self.transforms_texture.deinit(&mut self.device); + self.prim_header_f_texture.deinit(&mut self.device); + self.prim_header_i_texture.deinit(&mut self.device); self.render_task_texture.deinit(&mut self.device); self.device.delete_pbo(self.texture_cache_upload_pbo); self.texture_resolver.deinit(&mut self.device); @@ -4017,11 +4040,11 @@ pub trait SceneBuilderHooks { /// and before it processes anything. fn register(&self); /// This is called before each scene swap occurs. - fn pre_scene_swap(&self); + fn pre_scene_swap(&self, scenebuild_time: u64); /// This is called after each scene swap occurs. The PipelineInfo contains /// the updated epochs and pipelines removed in the new scene compared to /// the old scene. - fn post_scene_swap(&self, info: PipelineInfo); + fn post_scene_swap(&self, info: PipelineInfo, sceneswap_time: u64); /// This is called after a resource update operation on the scene builder /// thread, in the case where resource updates were applied without a scene /// build. @@ -4078,6 +4101,7 @@ pub struct RendererOptions { pub disable_dual_source_blending: bool, pub scene_builder_hooks: Option>, pub sampler: Option>, + pub chase_primitive: ChasePrimitive, } impl Default for RendererOptions { @@ -4111,6 +4135,7 @@ impl Default for RendererOptions { disable_dual_source_blending: false, scene_builder_hooks: None, sampler: None, + chase_primitive: ChasePrimitive::Nothing, } } } diff --git a/gfx/webrender/src/resource_cache.rs b/gfx/webrender/src/resource_cache.rs index 856cd56039b0..0e757ff410e1 100644 --- a/gfx/webrender/src/resource_cache.rs +++ b/gfx/webrender/src/resource_cache.rs @@ -641,13 +641,20 @@ impl ResourceCache { return }; + if !self.pending_image_requests.insert(request) { + return + } + + // If we are tiling, then we need to confirm the dirty rect intersects + // the tile before leaving the request in the pending queue. + // // We can start a worker thread rasterizing right now, if: // - The image is a blob. // - The blob hasn't already been requested this frame. - if self.pending_image_requests.insert(request) && template.data.is_blob() { - let (offset, size) = match template.tiling { - Some(tile_size) => { - let tile_offset = request.tile.unwrap(); + if template.data.is_blob() || dirty_rect.is_some() { + let (offset, size) = match request.tile { + Some(tile_offset) => { + let tile_size = template.tiling.unwrap(); let actual_size = compute_tile_size( &template.descriptor, tile_size, @@ -671,17 +678,19 @@ impl ResourceCache { None => (DevicePoint::zero(), template.descriptor.size), }; - if let Some(ref mut renderer) = self.blob_image_renderer { - renderer.request( - &self.resources, - request.into(), - &BlobImageDescriptor { - size, - offset, - format: template.descriptor.format, - }, - dirty_rect, - ); + if template.data.is_blob() { + if let Some(ref mut renderer) = self.blob_image_renderer { + renderer.request( + &self.resources, + request.into(), + &BlobImageDescriptor { + size, + offset, + format: template.descriptor.format, + }, + dirty_rect, + ); + } } } } @@ -923,6 +932,8 @@ impl ResourceCache { } fn update_texture_cache(&mut self, gpu_cache: &mut GpuCache) { + let mut keys_to_clear_dirty_rect = FastHashSet::default(); + for request in self.pending_image_requests.drain() { let image_template = self.resources.image_templates.get_mut(request.key).unwrap(); debug_assert!(image_template.data.uses_texture_cache()); @@ -963,23 +974,24 @@ impl ResourceCache { let entry = self.cached_images.get_mut(&request).as_mut().unwrap(); let mut descriptor = image_template.descriptor.clone(); - //TODO: erasing the dirty rectangle here is incorrect for tiled images, - // since other tile requests may follow that depend on it - let mut local_dirty_rect = image_template.dirty_rect.take(); + let local_dirty_rect; if let Some(tile) = request.tile { let tile_size = image_template.tiling.unwrap(); let clipped_tile_size = compute_tile_size(&descriptor, tile_size, tile); - if let Some(ref mut rect) = local_dirty_rect { - match intersect_for_tile(*rect, clipped_tile_size, tile_size, tile) { - Some(intersection) => *rect = intersection, - None => { - // if re-uploaded, the dirty rect is ignored anyway - debug_assert!(self.texture_cache.needs_upload(&entry.texture_cache_handle)) - } - } - } + local_dirty_rect = if let Some(ref rect) = image_template.dirty_rect { + keys_to_clear_dirty_rect.insert(request.key.clone()); + + // We should either have a dirty rect, or we are re-uploading where the dirty + // rect is ignored anyway. + let intersection = intersect_for_tile(*rect, clipped_tile_size, tile_size, tile); + debug_assert!(intersection.is_some() || + self.texture_cache.needs_upload(&entry.texture_cache_handle)); + intersection + } else { + None + }; // The tiled image could be stored on the CPU as one large image or be // already broken up into tiles. This affects the way we compute the stride @@ -995,6 +1007,8 @@ impl ResourceCache { } descriptor.size = clipped_tile_size; + } else { + local_dirty_rect = image_template.dirty_rect.take(); } let filter = match request.rendering { @@ -1036,6 +1050,11 @@ impl ResourceCache { UvRectKind::Rect, ); } + + for key in keys_to_clear_dirty_rect.drain() { + let image_template = self.resources.image_templates.get_mut(key).unwrap(); + image_template.dirty_rect.take(); + } } pub fn end_frame(&mut self) { diff --git a/gfx/webrender/src/scene_builder.rs b/gfx/webrender/src/scene_builder.rs index b5b5bdec6470..a40afb440ab0 100644 --- a/gfx/webrender/src/scene_builder.rs +++ b/gfx/webrender/src/scene_builder.rs @@ -13,6 +13,7 @@ use render_backend::DocumentView; use renderer::{PipelineInfo, SceneBuilderHooks}; use scene::Scene; use std::sync::mpsc::{channel, Receiver, Sender}; +use time::precise_time_ns; // Message from render backend to scene builder. pub enum SceneBuilderRequest { @@ -137,6 +138,7 @@ impl SceneBuilder { frame_ops, render, } => { + let scenebuild_start_time = precise_time_ns(); let built_scene = scene.map(|request|{ build_scene(&self.config, request) }); @@ -156,13 +158,15 @@ impl SceneBuilder { }; let (tx, rx) = channel(); - hooks.pre_scene_swap(); + let scenebuild_time = precise_time_ns() - scenebuild_start_time; + hooks.pre_scene_swap(scenebuild_time); (Some(info), Some(tx), Some(rx)) } _ => (None, None, None), }; + let sceneswap_start_time = precise_time_ns(); let has_resources_updates = !resource_updates.is_empty(); self.tx.send(SceneBuilderResult::Transaction { document_id, @@ -178,7 +182,8 @@ impl SceneBuilder { if let Some(pipeline_info) = pipeline_info { // Block until the swap is done, then invoke the hook. let swap_result = result_rx.unwrap().recv(); - self.hooks.as_ref().unwrap().post_scene_swap(pipeline_info); + let sceneswap_time = precise_time_ns() - sceneswap_start_time; + self.hooks.as_ref().unwrap().post_scene_swap(pipeline_info, sceneswap_time); // Once the hook is done, allow the RB thread to resume match swap_result { Ok(SceneSwapResult::Complete(resume_tx)) => { diff --git a/gfx/webrender/src/shade.rs b/gfx/webrender/src/shade.rs index 44f29a496646..7549f92ff2fa 100644 --- a/gfx/webrender/src/shade.rs +++ b/gfx/webrender/src/shade.rs @@ -351,11 +351,12 @@ fn create_prim_shader( ("sDither", TextureSampler::Dither), ("sCacheA8", TextureSampler::CacheA8), ("sCacheRGBA8", TextureSampler::CacheRGBA8), - ("sClipScrollNodes", TextureSampler::ClipScrollNodes), + ("sTransformPalette", TextureSampler::TransformPalette), ("sRenderTasks", TextureSampler::RenderTasks), ("sResourceCache", TextureSampler::ResourceCache), ("sSharedCacheA8", TextureSampler::SharedCacheA8), - ("sLocalClipRects", TextureSampler::LocalClipRects), + ("sPrimitiveHeadersF", TextureSampler::PrimitiveHeadersF), + ("sPrimitiveHeadersI", TextureSampler::PrimitiveHeadersI), ], ); } @@ -379,11 +380,12 @@ fn create_clip_shader(name: &'static str, device: &mut Device) -> Result &mut self.array_rgba8_nearest, (ImageFormat::RGBAF32, _) | (ImageFormat::RG8, _) | + (ImageFormat::RGBAI32, _) | (ImageFormat::R8, TextureFilter::Nearest) | (ImageFormat::R8, TextureFilter::Trilinear) | (ImageFormat::BGRA8, TextureFilter::Trilinear) => unreachable!(), @@ -725,6 +726,7 @@ impl TextureCache { (ImageFormat::BGRA8, TextureFilter::Linear) => &mut self.array_rgba8_linear, (ImageFormat::BGRA8, TextureFilter::Nearest) => &mut self.array_rgba8_nearest, (ImageFormat::RGBAF32, _) | + (ImageFormat::RGBAI32, _) | (ImageFormat::R8, TextureFilter::Nearest) | (ImageFormat::R8, TextureFilter::Trilinear) | (ImageFormat::BGRA8, TextureFilter::Trilinear) | diff --git a/gfx/webrender/src/tiling.rs b/gfx/webrender/src/tiling.rs index a823c625692f..455c0249abce 100644 --- a/gfx/webrender/src/tiling.rs +++ b/gfx/webrender/src/tiling.rs @@ -12,8 +12,7 @@ use device::{FrameId, Texture}; #[cfg(feature = "pathfinder")] use euclid::{TypedPoint2D, TypedVector2D}; use gpu_cache::{GpuCache}; -use gpu_types::{BorderInstance, BlurDirection, BlurInstance}; -use gpu_types::{ClipScrollNodeData, ZBufferIdGenerator}; +use gpu_types::{BorderInstance, BlurDirection, BlurInstance, PrimitiveHeaders, TransformData, TransformPalette}; use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture}; #[cfg(feature = "pathfinder")] use pathfinder_partitioner::mesh::Mesh; @@ -48,7 +47,7 @@ pub struct RenderTargetContext<'a, 'rc> { pub resource_cache: &'rc mut ResourceCache, pub clip_scroll_tree: &'a ClipScrollTree, pub use_dual_source_blending: bool, - pub node_data: &'a [ClipScrollNodeData], + pub transforms: &'a TransformPalette, } #[cfg_attr(feature = "capture", derive(Serialize))] @@ -103,6 +102,7 @@ pub trait RenderTarget { _gpu_cache: &mut GpuCache, _render_tasks: &mut RenderTaskTree, _deferred_resolves: &mut Vec, + _prim_headers: &mut PrimitiveHeaders, ) { } // TODO(gw): It's a bit odd that we need the deferred resolves and mutable @@ -166,12 +166,19 @@ impl RenderTargetList { render_tasks: &mut RenderTaskTree, deferred_resolves: &mut Vec, saved_index: Option, + prim_headers: &mut PrimitiveHeaders, ) { debug_assert_eq!(None, self.saved_index); self.saved_index = saved_index; for target in &mut self.targets { - target.build(ctx, gpu_cache, render_tasks, deferred_resolves); + target.build( + ctx, + gpu_cache, + render_tasks, + deferred_resolves, + prim_headers, + ); } } @@ -333,9 +340,9 @@ impl RenderTarget for ColorRenderTarget { gpu_cache: &mut GpuCache, render_tasks: &mut RenderTaskTree, deferred_resolves: &mut Vec, + prim_headers: &mut PrimitiveHeaders, ) { 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]; @@ -358,7 +365,7 @@ impl RenderTarget for ColorRenderTarget { gpu_cache, render_tasks, deferred_resolves, - &mut z_generator, + prim_headers, ); if let Some(batch_container) = batch_builder.build(&mut merged_batches) { @@ -594,6 +601,7 @@ impl RenderTarget for AlphaRenderTarget { ctx.resource_cache, gpu_cache, clip_store, + ctx.transforms, ); } RenderTaskKind::ClipRegion(ref task) => { @@ -795,6 +803,7 @@ impl RenderPass { render_tasks: &mut RenderTaskTree, deferred_resolves: &mut Vec, clip_store: &ClipStore, + prim_headers: &mut PrimitiveHeaders, ) { profile_scope!("RenderPass::build"); @@ -811,7 +820,13 @@ impl RenderPass { deferred_resolves, ); } - target.build(ctx, gpu_cache, render_tasks, deferred_resolves); + target.build( + ctx, + gpu_cache, + render_tasks, + deferred_resolves, + prim_headers, + ); } RenderPassKind::OffScreen { ref mut color, ref mut alpha, ref mut texture_cache } => { let is_shared_alpha = self.tasks.iter().any(|&task_id| { @@ -911,8 +926,22 @@ impl RenderPass { } } - color.build(ctx, gpu_cache, render_tasks, deferred_resolves, saved_color); - alpha.build(ctx, gpu_cache, render_tasks, deferred_resolves, saved_alpha); + color.build( + ctx, + gpu_cache, + render_tasks, + deferred_resolves, + saved_color, + prim_headers, + ); + alpha.build( + ctx, + gpu_cache, + render_tasks, + deferred_resolves, + saved_alpha, + prim_headers, + ); alpha.is_shared = is_shared_alpha; } } @@ -956,9 +985,9 @@ pub struct Frame { #[cfg_attr(any(feature = "capture", feature = "replay"), serde(default = "FrameProfileCounters::new", skip))] pub profile_counters: FrameProfileCounters, - pub node_data: Vec, - pub clip_chain_local_clip_rects: Vec, + pub transform_palette: Vec, pub render_tasks: RenderTaskTree, + pub prim_headers: PrimitiveHeaders, /// The GPU cache frame that the contents of Self depend on pub gpu_cache_frame_id: FrameId, diff --git a/gfx/webrender_api/src/api.rs b/gfx/webrender_api/src/api.rs index ea966d81c3e7..aa3e042bb3ec 100644 --- a/gfx/webrender_api/src/api.rs +++ b/gfx/webrender_api/src/api.rs @@ -1103,7 +1103,7 @@ pub struct DynamicProperties { pub trait RenderNotifier: Send { fn clone(&self) -> Box; fn wake_up(&self); - fn new_frame_ready(&self, DocumentId, scrolled: bool, composite_needed: bool); + fn new_frame_ready(&self, DocumentId, scrolled: bool, composite_needed: bool, render_time_ns: Option); fn external_event(&self, _evt: ExternalEvent) { unimplemented!() } diff --git a/gfx/webrender_api/src/color.rs b/gfx/webrender_api/src/color.rs index fe26f0f0aff4..40f2fa2a62b0 100644 --- a/gfx/webrender_api/src/color.rs +++ b/gfx/webrender_api/src/color.rs @@ -115,7 +115,7 @@ pub struct ColorU { impl ColorU { /// Constructs a new additive `ColorU` from its components. - pub fn new(r: u8, g: u8, b: u8, a: u8) -> ColorU { + pub fn new(r: u8, g: u8, b: u8, a: u8) -> Self { ColorU { r, g, b, a } } } diff --git a/gfx/webrender_api/src/display_item.rs b/gfx/webrender_api/src/display_item.rs index 72604358a3f3..1de767a991da 100644 --- a/gfx/webrender_api/src/display_item.rs +++ b/gfx/webrender_api/src/display_item.rs @@ -273,22 +273,10 @@ pub enum RepeatMode { } #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] -pub struct GradientBorder { - pub gradient: Gradient, - pub outset: SideOffsets2D, -} - -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] -pub struct RadialGradientBorder { - pub gradient: RadialGradient, - pub outset: SideOffsets2D, -} - -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] -/// TODO(mrobinson): Currently only images are supported, but we will -/// eventually add support for Gradient and RadialGradient. pub enum NinePatchBorderSource { Image(ImageKey), + Gradient(Gradient), + RadialGradient(RadialGradient), } #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] @@ -333,8 +321,6 @@ pub struct NinePatchBorder { pub enum BorderDetails { Normal(NormalBorder), NinePatch(NinePatchBorder), - Gradient(GradientBorder), - RadialGradient(RadialGradientBorder), } #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] diff --git a/gfx/webrender_api/src/display_list.rs b/gfx/webrender_api/src/display_list.rs index a2589ed2a09a..784dd8ad331d 100644 --- a/gfx/webrender_api/src/display_list.rs +++ b/gfx/webrender_api/src/display_list.rs @@ -27,8 +27,8 @@ use {StickyFrameDisplayItem, StickyOffsetBounds, TextDisplayItem, TransformStyle use {YuvData, YuvImageDisplayItem}; // We don't want to push a long text-run. If a text-run is too long, split it into several parts. -// This needs to be set to (renderer::MAX_VERTEX_TEXTURE_WIDTH - VECS_PER_PRIM_HEADER - VECS_PER_TEXT_RUN) * 2 -pub const MAX_TEXT_RUN_LENGTH: usize = 2038; +// This needs to be set to (renderer::MAX_VERTEX_TEXTURE_WIDTH - VECS_PER_TEXT_RUN) * 2 +pub const MAX_TEXT_RUN_LENGTH: usize = 2040; // We start at 2, because the root reference is always 0 and the root scroll node is always 1. const FIRST_CLIP_ID: usize = 2; diff --git a/gfx/webrender_api/src/image.rs b/gfx/webrender_api/src/image.rs index 616b9d64d2d0..6130b0781d3f 100644 --- a/gfx/webrender_api/src/image.rs +++ b/gfx/webrender_api/src/image.rs @@ -59,6 +59,7 @@ pub enum ImageFormat { BGRA8 = 3, RGBAF32 = 4, RG8 = 5, + RGBAI32 = 6, } impl ImageFormat { @@ -68,6 +69,7 @@ impl ImageFormat { ImageFormat::BGRA8 => 4, ImageFormat::RGBAF32 => 16, ImageFormat::RG8 => 2, + ImageFormat::RGBAI32 => 16, } } } diff --git a/gfx/webrender_bindings/revision.txt b/gfx/webrender_bindings/revision.txt index cde0f2bfdfc0..abe2f9a01bb4 100644 --- a/gfx/webrender_bindings/revision.txt +++ b/gfx/webrender_bindings/revision.txt @@ -1 +1 @@ -cf98ad4d63729c678a7575eb9bce36794da5e270 +cdfaaeb5f74e416f39af1081c9a676c752d23896 diff --git a/gfx/webrender_bindings/webrender_ffi_generated.h b/gfx/webrender_bindings/webrender_ffi_generated.h index a58eb0f0811f..28769bbc6b7d 100644 --- a/gfx/webrender_bindings/webrender_ffi_generated.h +++ b/gfx/webrender_bindings/webrender_ffi_generated.h @@ -93,6 +93,7 @@ enum class ImageFormat : uint32_t { BGRA8 = 3, RGBAF32 = 4, RG8 = 5, + RGBAI32 = 6, Sentinel /* this must be last for serialization purposes. */ }; diff --git a/gfx/wrench/Cargo.toml b/gfx/wrench/Cargo.toml index 650746906293..e483cd0a74c4 100644 --- a/gfx/wrench/Cargo.toml +++ b/gfx/wrench/Cargo.toml @@ -14,7 +14,7 @@ euclid = "0.17" gleam = "0.5" glutin = "0.15" app_units = "0.6" -image = "0.18" +image = "0.19" clap = { version = "2", features = ["yaml"] } lazy_static = "1" log = "0.4" diff --git a/gfx/wrench/src/args.yaml b/gfx/wrench/src/args.yaml index 19fe487fcb17..8f154f494687 100644 --- a/gfx/wrench/src/args.yaml +++ b/gfx/wrench/src/args.yaml @@ -67,6 +67,10 @@ args: - no_batch: long: no-batch help: Disable batching of instanced draw calls + - chase: + long: chase + help: Chase a particular primitive matching the local rect + takes_value: true subcommands: - png: diff --git a/gfx/wrench/src/main.rs b/gfx/wrench/src/main.rs index 07758c0ab931..bf5d499c656b 100644 --- a/gfx/wrench/src/main.rs +++ b/gfx/wrench/src/main.rs @@ -257,7 +257,7 @@ fn make_window( }) .with_vsync(vsync); let window_builder = winit::WindowBuilder::new() - .with_title("WRech") + .with_title("WRench") .with_multitouch() .with_dimensions(size.width, size.height); @@ -351,7 +351,11 @@ impl RenderNotifier for Notifier { self.tx.send(NotifierEvent::ShutDown).unwrap(); } - fn new_frame_ready(&self, _: DocumentId, _scrolled: bool, composite_needed: bool) { + fn new_frame_ready(&self, + _: DocumentId, + _scrolled: bool, + composite_needed: bool, + _render_time: Option) { if composite_needed { self.wake_up(); } @@ -424,6 +428,20 @@ fn main() { }) .unwrap_or(DeviceUintSize::new(1920, 1080)); let zoom_factor = args.value_of("zoom").map(|z| z.parse::().unwrap()); + let chase_primitive = match args.value_of("chase") { + Some(s) => { + let mut items = s + .split(',') + .map(|s| s.parse::().unwrap()) + .collect::>(); + let rect = LayoutRect::new( + LayoutPoint::new(items[0], items[1]), + LayoutSize::new(items[2], items[3]), + ); + webrender::ChasePrimitive::LocalRect(rect) + }, + None => webrender::ChasePrimitive::Nothing, + }; let mut events_loop = if args.is_present("headless") { None @@ -462,6 +480,7 @@ fn main() { args.is_present("precache"), args.is_present("slow_subpixel"), zoom_factor.unwrap_or(1.0), + chase_primitive, notifier, ); @@ -634,17 +653,30 @@ fn render<'a>( let path = PathBuf::from("../captures/wrench"); wrench.api.save_capture(path, CaptureBits::all()); } - VirtualKeyCode::Up => { + VirtualKeyCode::Up | VirtualKeyCode::Down => { + let mut txn = Transaction::new(); + + let offset = match vk { + winit::VirtualKeyCode::Up => LayoutVector2D::new(0.0, 10.0), + winit::VirtualKeyCode::Down => LayoutVector2D::new(0.0, -10.0), + _ => unreachable!("Should not see non directional keys here.") + }; + + txn.scroll(ScrollLocation::Delta(offset), cursor_position); + txn.generate_frame(); + wrench.api.send_transaction(wrench.document_id, txn); + + do_frame = true; + } + VirtualKeyCode::Add => { let current_zoom = wrench.get_page_zoom(); let new_zoom_factor = ZoomFactor::new(current_zoom.get() + 0.1); - wrench.set_page_zoom(new_zoom_factor); do_frame = true; } - VirtualKeyCode::Down => { + VirtualKeyCode::Subtract => { let current_zoom = wrench.get_page_zoom(); let new_zoom_factor = ZoomFactor::new((current_zoom.get() - 0.1).max(0.1)); - wrench.set_page_zoom(new_zoom_factor); do_frame = true; } diff --git a/gfx/wrench/src/wrench.rs b/gfx/wrench/src/wrench.rs index 45c17a981425..ef0ce2a5590d 100644 --- a/gfx/wrench/src/wrench.rs +++ b/gfx/wrench/src/wrench.rs @@ -109,7 +109,10 @@ impl RenderNotifier for Notifier { self.update(false); } - fn new_frame_ready(&self, _: DocumentId, scrolled: bool, _composite_needed: bool) { + fn new_frame_ready(&self, _: DocumentId, + scrolled: bool, + _composite_needed: bool, + _render_time: Option) { self.update(!scrolled); } } @@ -180,6 +183,7 @@ impl Wrench { precache_shaders: bool, disable_dual_source_blending: bool, zoom_factor: f32, + chase_primitive: webrender::ChasePrimitive, notifier: Option>, ) -> Self { println!("Shader override path: {:?}", shader_override_path); @@ -212,6 +216,7 @@ impl Wrench { precache_shaders, blob_image_renderer: Some(Box::new(blob::CheckerboardRenderer::new(callbacks.clone()))), disable_dual_source_blending, + chase_primitive, ..Default::default() }; diff --git a/gfx/wrench/src/yaml_frame_reader.rs b/gfx/wrench/src/yaml_frame_reader.rs index 16891c3a4840..0818a51f189d 100644 --- a/gfx/wrench/src/yaml_frame_reader.rs +++ b/gfx/wrench/src/yaml_frame_reader.rs @@ -173,7 +173,8 @@ fn is_image_opaque(format: ImageFormat, bytes: &[u8]) -> bool { } ImageFormat::RG8 => true, ImageFormat::R8 => false, - ImageFormat::RGBAF32 => unreachable!(), + ImageFormat::RGBAF32 | + ImageFormat::RGBAI32 => unreachable!(), } } @@ -880,26 +881,28 @@ impl YamlFrameReader { radius, })) } - "image" => { - let file = rsrc_path(&item["image-source"], &self.aux_dir); - let (image_key, _) = self - .add_or_get_image(&file, None, wrench); + "image" | "gradient" | "radial-gradient" => { let image_width = item["image-width"] .as_i64() - .expect("border must have image-width"); + .unwrap_or(info.rect.size.width as i64); let image_height = item["image-height"] .as_i64() - .expect("border must have image-height"); + .unwrap_or(info.rect.size.height as i64); let fill = item["fill"].as_bool().unwrap_or(false); - let slice = item["slice"].as_vec_u32().expect("border must have slice"); - let slice = broadcast(&slice, 4); + + let slice = item["slice"].as_vec_u32(); + let slice = match slice { + Some(slice) => broadcast(&slice, 4), + None => vec![widths.top as u32, widths.left as u32, widths.bottom as u32, widths.right as u32], + }; + let outset = item["outset"] .as_vec_f32() .expect("border must have outset"); let outset = broadcast(&outset, 4); let repeat_horizontal = match item["repeat-horizontal"] .as_str() - .expect("border must have repeat-horizontal") + .unwrap_or("stretch") { "stretch" => RepeatMode::Stretch, "repeat" => RepeatMode::Repeat, @@ -909,7 +912,7 @@ impl YamlFrameReader { }; let repeat_vertical = match item["repeat-vertical"] .as_str() - .expect("border must have repeat-vertical") + .unwrap_or("stretch") { "stretch" => RepeatMode::Stretch, "repeat" => RepeatMode::Repeat, @@ -917,8 +920,27 @@ impl YamlFrameReader { "space" => RepeatMode::Space, s => panic!("Unknown box border image repeat mode {}", s), }; + let source = match border_type { + "image" => { + let file = rsrc_path(&item["image-source"], &self.aux_dir); + let (image_key, _) = self + .add_or_get_image(&file, None, wrench); + NinePatchBorderSource::Image(image_key) + } + "gradient" => { + let gradient = self.to_gradient(dl, item); + NinePatchBorderSource::Gradient(gradient) + } + "radial-gradient" => { + let gradient = self.to_radial_gradient(dl, item); + NinePatchBorderSource::RadialGradient(gradient) + + } + _ => unreachable!("Unexpected border type"), + }; + Some(BorderDetails::NinePatch(NinePatchBorder { - source: NinePatchBorderSource::Image(image_key), + source, width: image_width as u32, height: image_height as u32, slice: SideOffsets2D::new(slice[0], slice[1], slice[2], slice[3]), @@ -928,28 +950,6 @@ impl YamlFrameReader { outset: SideOffsets2D::new(outset[0], outset[1], outset[2], outset[3]), })) } - "gradient" => { - let gradient = self.to_gradient(dl, item); - let outset = item["outset"] - .as_vec_f32() - .expect("borders must have outset"); - let outset = broadcast(&outset, 4); - Some(BorderDetails::Gradient(GradientBorder { - gradient, - outset: SideOffsets2D::new(outset[0], outset[1], outset[2], outset[3]), - })) - } - "radial-gradient" => { - let gradient = self.to_radial_gradient(dl, item); - let outset = item["outset"] - .as_vec_f32() - .expect("borders must have outset"); - let outset = broadcast(&outset, 4); - Some(BorderDetails::RadialGradient(RadialGradientBorder { - gradient, - outset: SideOffsets2D::new(outset[0], outset[1], outset[2], outset[3]), - })) - } _ => { println!("Unable to parse border {:?}", item); None @@ -1391,7 +1391,7 @@ impl YamlFrameReader { clip_rect, complex_clips, image_mask, - ScrollSensitivity::Script, + ScrollSensitivity::ScriptAndInputEvents, ); if let Some(numeric_id) = numeric_id { self.add_clip_id_mapping(numeric_id, real_id); diff --git a/gfx/wrench/src/yaml_frame_writer.rs b/gfx/wrench/src/yaml_frame_writer.rs index 98317e76059d..fa383b3dbff9 100644 --- a/gfx/wrench/src/yaml_frame_writer.rs +++ b/gfx/wrench/src/yaml_frame_writer.rs @@ -885,14 +885,35 @@ impl YamlFrameWriter { details.outset.left, ]; yaml_node(&mut v, "width", f32_vec_yaml(&widths, true)); - str_node(&mut v, "border-type", "image"); match details.source { NinePatchBorderSource::Image(image_key) => { + str_node(&mut v, "border-type", "image"); if let Some(path) = self.path_for_image(image_key) { path_node(&mut v, "image", &path); } } + NinePatchBorderSource::Gradient(gradient) => { + str_node(&mut v, "gradient", "image"); + point_node(&mut v, "start", &gradient.start_point); + point_node(&mut v, "end", &gradient.end_point); + let mut stops = vec![]; + for stop in display_list.get(base.gradient_stops()) { + stops.push(Yaml::Real(stop.offset.to_string())); + stops.push(Yaml::String(color_to_string(stop.color))); + } + yaml_node(&mut v, "stops", Yaml::Array(stops)); + bool_node(&mut v, "repeat", gradient.extend_mode == ExtendMode::Repeat); + } + NinePatchBorderSource::RadialGradient(gradient) => { + str_node(&mut v, "border-type", "radial-gradient"); + radial_gradient_to_yaml( + &mut v, + &gradient, + base.gradient_stops(), + display_list + ); + } } u32_node(&mut v, "image-width", details.width); @@ -924,59 +945,6 @@ impl YamlFrameWriter { RepeatMode::Space => str_node(&mut v, "repeat-vertical", "space"), }; } - BorderDetails::Gradient(ref details) => { - let widths: Vec = vec![ - item.widths.top, - item.widths.right, - item.widths.bottom, - item.widths.left, - ]; - let outset: Vec = vec![ - details.outset.top, - details.outset.right, - details.outset.bottom, - details.outset.left, - ]; - yaml_node(&mut v, "width", f32_vec_yaml(&widths, true)); - str_node(&mut v, "border-type", "gradient"); - point_node(&mut v, "start", &details.gradient.start_point); - point_node(&mut v, "end", &details.gradient.end_point); - let mut stops = vec![]; - for stop in display_list.get(base.gradient_stops()) { - stops.push(Yaml::Real(stop.offset.to_string())); - stops.push(Yaml::String(color_to_string(stop.color))); - } - yaml_node(&mut v, "stops", Yaml::Array(stops)); - bool_node( - &mut v, - "repeat", - details.gradient.extend_mode == ExtendMode::Repeat, - ); - yaml_node(&mut v, "outset", f32_vec_yaml(&outset, true)); - } - BorderDetails::RadialGradient(ref details) => { - let widths: Vec = vec![ - item.widths.top, - item.widths.right, - item.widths.bottom, - item.widths.left, - ]; - let outset: Vec = vec![ - details.outset.top, - details.outset.right, - details.outset.bottom, - details.outset.left, - ]; - yaml_node(&mut v, "width", f32_vec_yaml(&widths, true)); - str_node(&mut v, "border-type", "radial-gradient"); - yaml_node(&mut v, "outset", f32_vec_yaml(&outset, true)); - radial_gradient_to_yaml( - &mut v, - &details.gradient, - base.gradient_stops(), - display_list - ); - } } } BoxShadow(item) => {