--- a/gfx/webrender/Cargo.toml
+++ b/gfx/webrender/Cargo.toml
@@ -20,17 +20,17 @@ bincode = "0.9"
byteorder = "1.0"
euclid = "0.16"
fxhash = "0.2.1"
gleam = "0.4.20"
lazy_static = "1"
log = "0.4"
num-traits = "0.1.32"
time = "0.1"
-rayon = "0.8"
+rayon = "1"
webrender_api = {path = "../webrender_api"}
bitflags = "1.0"
thread_profiler = "0.1.1"
plane-split = "0.7"
png = { optional = true, version = "0.11" }
smallvec = "0.6"
ws = { optional = true, version = "0.7.3" }
serde_json = { optional = true, version = "1.0" }
--- a/gfx/webrender/examples/blob.rs
+++ b/gfx/webrender/examples/blob.rs
@@ -6,17 +6,17 @@ extern crate gleam;
extern crate glutin;
extern crate rayon;
extern crate webrender;
#[path = "common/boilerplate.rs"]
mod boilerplate;
use boilerplate::{Example, HandyDandyRectBuilder};
-use rayon::{Configuration as ThreadPoolConfig, ThreadPool};
+use rayon::{ThreadPool, ThreadPoolBuilder};
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::sync::Arc;
use std::sync::mpsc::{Receiver, Sender, channel};
use webrender::api::{self, DisplayListBuilder, DocumentId, PipelineId, RenderApi, ResourceUpdates};
// This example shows how to implement a very basic BlobImageRenderer that can only render
// a checkerboard pattern.
@@ -277,20 +277,21 @@ impl Example for App {
blob_img2,
);
builder.pop_stacking_context();
}
}
fn main() {
- let worker_config =
- ThreadPoolConfig::new().thread_name(|idx| format!("WebRender:Worker#{}", idx));
+ let workers =
+ ThreadPoolBuilder::new().thread_name(|idx| format!("WebRender:Worker#{}", idx))
+ .build();
- let workers = Arc::new(ThreadPool::new(worker_config).unwrap());
+ let workers = Arc::new(workers.unwrap());
let opts = webrender::RendererOptions {
workers: Some(Arc::clone(&workers)),
// Register our blob renderer, so that WebRender integrates it in the resource cache..
// Share the same pool of worker threads between WebRender and our blob renderer.
blob_image_renderer: Some(Box::new(CheckerboardRenderer::new(Arc::clone(&workers)))),
..Default::default()
};
--- a/gfx/webrender/res/brush.glsl
+++ b/gfx/webrender/res/brush.glsl
@@ -10,39 +10,43 @@ void brush_vs(
RectWithSize local_rect,
ivec3 user_data,
PictureTask pic_task
);
#define VECS_PER_BRUSH_PRIM 2
#define VECS_PER_SEGMENT 2
+#define BRUSH_FLAG_PERSPECTIVE_INTERPOLATION 1
+
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 >> 16;
bi.scroll_node_id = aData0.z & 0xffff;
bi.z = aData0.w;
bi.segment_index = aData1.x & 0xffff;
- bi.edge_mask = aData1.x >> 16;
+ bi.edge_mask = (aData1.x >> 16) & 0xff;
+ bi.flags = (aData1.x >> 24);
bi.user_data = aData1.yzw;
return bi;
}
struct BrushPrimitive {
RectWithSize local_rect;
RectWithSize local_clip_rect;
@@ -130,24 +134,27 @@ void main(void) {
// items. For now, just ensure it has no
// effect. We can tidy this up as we move
// more items to be brush shaders.
#ifdef WR_FEATURE_ALPHA_PASS
vLocalBounds = 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;
+
vi = write_transform_vertex(
local_segment_rect,
brush_prim.local_rect,
brush_prim.local_clip_rect,
mix(vec4(0.0), vec4(1.0), edge_mask),
float(brush.z),
scroll_node,
- pic_task
+ pic_task,
+ do_perspective_interpolation
);
}
// For brush instances in the alpha pass, always write
// out clip information.
// TODO(gw): It's possible that we might want alpha
// shaders that don't clip in the future,
// but it's reasonable to assume that one
--- a/gfx/webrender/res/brush_blend.glsl
+++ b/gfx/webrender/res/brush_blend.glsl
@@ -1,18 +1,18 @@
/* 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/. */
#define VECS_PER_SPECIFIC_BRUSH 5
+#define FORCE_NO_PERSPECTIVE
#include shared,prim_shared,brush
varying vec3 vUv;
-varying float vW;
flat varying float vAmount;
flat varying int vOp;
flat varying mat4 vColorMat;
flat varying vec4 vColorOffset;
#ifdef WR_VERTEX_SHADER
@@ -20,20 +20,19 @@ void brush_vs(
VertexInfo vi,
int prim_address,
RectWithSize local_rect,
ivec3 user_data,
PictureTask pic_task
) {
PictureTask src_task = fetch_picture_task(user_data.x);
vec2 texture_size = vec2(textureSize(sColor0, 0).xy);
- vec2 uv = (vi.snapped_device_pos +
- src_task.common_data.task_rect.p0 -
- src_task.content_origin) * vi.w;
- vW = vi.w;
+ vec2 uv = vi.snapped_device_pos +
+ src_task.common_data.task_rect.p0 -
+ src_task.content_origin;
vUv = vec3(uv / texture_size, src_task.common_data.texture_layer_index);
vOp = user_data.y;
float lumR = 0.2126;
float lumG = 0.7152;
float lumB = 0.0722;
float oneMinusLumR = 1.0 - lumR;
@@ -110,18 +109,17 @@ vec4 Brightness(vec4 Cs, float amount) {
return vec4(clamp(Cs.rgb * amount, vec3(0.0), vec3(1.0)), Cs.a);
}
vec4 Opacity(vec4 Cs, float amount) {
return vec4(Cs.rgb, Cs.a * amount);
}
vec4 brush_fs() {
- vec2 uv = vUv.xy / vW;
- vec4 Cs = texture(sColor0, vec3(uv, vUv.z));
+ vec4 Cs = texture(sColor0, vUv);
// Un-premultiply the input.
Cs.rgb /= Cs.a;
vec4 color;
switch (vOp) {
case 0:
--- a/gfx/webrender/res/brush_mix_blend.glsl
+++ b/gfx/webrender/res/brush_mix_blend.glsl
@@ -1,46 +1,42 @@
/* 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/. */
#define VECS_PER_SPECIFIC_BRUSH 5
#include shared,prim_shared,brush
-varying vec3 vUv;
-varying float vW;
-
varying vec3 vSrcUv;
varying vec3 vBackdropUv;
flat varying int vOp;
#ifdef WR_VERTEX_SHADER
void brush_vs(
VertexInfo vi,
int prim_address,
RectWithSize local_rect,
ivec3 user_data,
PictureTask pic_task
) {
vec2 texture_size = vec2(textureSize(sCacheRGBA8, 0));
vOp = user_data.x;
- vW = vi.w;
PictureTask src_task = fetch_picture_task(user_data.z);
- vec2 src_uv = (vi.snapped_device_pos +
- src_task.common_data.task_rect.p0 -
- src_task.content_origin) * vi.w;
+ vec2 src_uv = vi.snapped_device_pos +
+ src_task.common_data.task_rect.p0 -
+ src_task.content_origin;
vSrcUv = vec3(src_uv / texture_size, src_task.common_data.texture_layer_index);
RenderTaskCommonData backdrop_task = fetch_render_task_common_data(user_data.y);
- vec2 backdrop_uv = (vi.snapped_device_pos +
- backdrop_task.task_rect.p0 -
- src_task.content_origin) * vi.w;
+ vec2 backdrop_uv = vi.snapped_device_pos +
+ backdrop_task.task_rect.p0 -
+ src_task.content_origin;
vBackdropUv = vec3(backdrop_uv / texture_size, backdrop_task.texture_layer_index);
}
#endif
#ifdef WR_FRAGMENT_SHADER
float gauss(float x, float sigma) {
if (sigma == 0.0)
return 1.0;
@@ -200,20 +196,18 @@ const int MixBlendMode_SoftLight = 9;
const int MixBlendMode_Difference = 10;
const int MixBlendMode_Exclusion = 11;
const int MixBlendMode_Hue = 12;
const int MixBlendMode_Saturation = 13;
const int MixBlendMode_Color = 14;
const int MixBlendMode_Luminosity = 15;
vec4 brush_fs() {
- vec2 uv = vUv.xy / vW;
-
- vec4 Cb = texture(sCacheRGBA8, vBackdropUv);
- vec4 Cs = texture(sCacheRGBA8, vSrcUv);
+ vec4 Cb = textureLod(sCacheRGBA8, vBackdropUv, 0.0);
+ vec4 Cs = textureLod(sCacheRGBA8, vSrcUv, 0.0);
if (Cb.a == 0.0) {
return Cs;
}
if (Cs.a == 0.0) {
return vec4(0.0);
}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/brush_radial_gradient.glsl
@@ -0,0 +1,121 @@
+/* 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/. */
+
+#define VECS_PER_SPECIFIC_BRUSH 2
+
+#include shared,prim_shared,brush
+
+flat varying int vGradientAddress;
+flat varying float vGradientRepeat;
+
+flat varying vec2 vStartCenter;
+flat varying vec2 vEndCenter;
+flat varying float vStartRadius;
+flat varying float vEndRadius;
+
+varying vec2 vPos;
+
+#ifdef WR_FEATURE_ALPHA_PASS
+varying vec2 vLocalPos;
+#endif
+
+#ifdef WR_VERTEX_SHADER
+
+struct RadialGradient {
+ vec4 start_end_center;
+ vec4 start_end_radius_ratio_xy_extend_mode;
+};
+
+RadialGradient fetch_radial_gradient(int address) {
+ vec4 data[2] = fetch_from_resource_cache_2(address);
+ return RadialGradient(data[0], data[1]);
+}
+
+void brush_vs(
+ VertexInfo vi,
+ int prim_address,
+ RectWithSize local_rect,
+ ivec3 user_data,
+ PictureTask pic_task
+) {
+ RadialGradient gradient = fetch_radial_gradient(prim_address);
+
+ vPos = vi.local_pos - local_rect.p0;
+
+ vStartCenter = gradient.start_end_center.xy;
+ vEndCenter = gradient.start_end_center.zw;
+
+ vStartRadius = gradient.start_end_radius_ratio_xy_extend_mode.x;
+ vEndRadius = gradient.start_end_radius_ratio_xy_extend_mode.y;
+
+ // Transform all coordinates by the y scale so the
+ // fragment shader can work with circles
+ float ratio_xy = gradient.start_end_radius_ratio_xy_extend_mode.z;
+ vPos.y *= ratio_xy;
+ vStartCenter.y *= ratio_xy;
+ vEndCenter.y *= ratio_xy;
+
+ vGradientAddress = user_data.x;
+
+ // Whether to repeat the gradient instead of clamping.
+ vGradientRepeat = float(int(gradient.start_end_radius_ratio_xy_extend_mode.w) != EXTEND_MODE_CLAMP);
+
+#ifdef WR_FEATURE_ALPHA_PASS
+ vLocalPos = vi.local_pos;
+#endif
+}
+#endif
+
+#ifdef WR_FRAGMENT_SHADER
+vec4 brush_fs() {
+ vec2 cd = vEndCenter - vStartCenter;
+ vec2 pd = vPos - vStartCenter;
+ float rd = vEndRadius - vStartRadius;
+
+ // Solve for t in length(t * cd - pd) = vStartRadius + t * rd
+ // using a quadratic equation in form of At^2 - 2Bt + C = 0
+ float A = dot(cd, cd) - rd * rd;
+ float B = dot(pd, cd) + vStartRadius * rd;
+ float C = dot(pd, pd) - vStartRadius * vStartRadius;
+
+ float offset;
+ if (A == 0.0) {
+ // Since A is 0, just solve for -2Bt + C = 0
+ if (B == 0.0) {
+ discard;
+ }
+ float t = 0.5 * C / B;
+ if (vStartRadius + rd * t >= 0.0) {
+ offset = t;
+ } else {
+ discard;
+ }
+ } else {
+ float discr = B * B - A * C;
+ if (discr < 0.0) {
+ discard;
+ }
+ discr = sqrt(discr);
+ float t0 = (B + discr) / A;
+ float t1 = (B - discr) / A;
+ if (vStartRadius + rd * t0 >= 0.0) {
+ offset = t0;
+ } else if (vStartRadius + rd * t1 >= 0.0) {
+ offset = t1;
+ } else {
+ discard;
+ }
+ }
+
+ vec4 color = sample_gradient(vGradientAddress,
+ offset,
+ vGradientRepeat);
+
+#ifdef WR_FEATURE_ALPHA_PASS
+ color *= init_transform_fs(vLocalPos);
+#endif
+
+ return color;
+}
+#endif
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/brush_yuv_image.glsl
@@ -0,0 +1,166 @@
+/* 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/. */
+
+#define VECS_PER_SPECIFIC_BRUSH 0
+
+#include shared,prim_shared,brush
+
+// TODO(gw): Consider whether we should even have separate shader compilations
+// for the various YUV modes. To save on the number of shaders we
+// need to compile, it might be worth just doing this as an
+// uber-shader instead.
+// TODO(gw): Regardless of the above, we should remove the separate shader
+// compilations for the different color space matrix below. That
+// can be provided by a branch in the VS and pushed through the
+// interpolators, or even as a uniform that breaks batches, rather
+// that needing to compile to / select a different shader when
+// there is a different color space.
+
+#ifdef WR_FEATURE_ALPHA_PASS
+varying vec2 vLocalPos;
+#endif
+
+#if defined (WR_FEATURE_YUV_PLANAR)
+ varying vec3 vUv_Y;
+ flat varying vec4 vUvBounds_Y;
+
+ varying vec3 vUv_U;
+ flat varying vec4 vUvBounds_U;
+
+ varying vec3 vUv_V;
+ flat varying vec4 vUvBounds_V;
+#elif defined (WR_FEATURE_YUV_NV12)
+ varying vec3 vUv_Y;
+ flat varying vec4 vUvBounds_Y;
+
+ varying vec3 vUv_UV;
+ flat varying vec4 vUvBounds_UV;
+#elif defined (WR_FEATURE_YUV_INTERLEAVED)
+ varying vec3 vUv_YUV;
+ flat varying vec4 vUvBounds_YUV;
+#endif
+
+#ifdef WR_VERTEX_SHADER
+void write_uv_rect(
+ int resource_id,
+ vec2 f,
+ vec2 texture_size,
+ out vec3 uv,
+ out vec4 uv_bounds
+) {
+ ImageResource res = fetch_image_resource(resource_id);
+ vec2 uv0 = res.uv_rect.p0;
+ vec2 uv1 = res.uv_rect.p1;
+
+ uv.xy = mix(uv0, uv1, f);
+ uv.z = res.layer;
+
+ uv_bounds = vec4(uv0 + vec2(0.5), uv1 - vec2(0.5));
+
+ #ifndef WR_FEATURE_TEXTURE_RECT
+ uv.xy /= texture_size;
+ uv_bounds /= texture_size.xyxy;
+ #endif
+}
+
+void brush_vs(
+ VertexInfo vi,
+ int prim_address,
+ RectWithSize local_rect,
+ ivec3 user_data,
+ PictureTask pic_task
+) {
+ vec2 f = (vi.local_pos - local_rect.p0) / local_rect.size;
+
+#ifdef WR_FEATURE_ALPHA_PASS
+ vLocalPos = vi.local_pos;
+#endif
+
+#if defined (WR_FEATURE_YUV_PLANAR)
+ write_uv_rect(user_data.x, f, vec2(textureSize(sColor0, 0).xy), vUv_Y, vUvBounds_Y);
+ write_uv_rect(user_data.y, f, vec2(textureSize(sColor1, 0).xy), vUv_U, vUvBounds_U);
+ write_uv_rect(user_data.z, f, vec2(textureSize(sColor2, 0).xy), vUv_V, vUvBounds_V);
+#elif defined (WR_FEATURE_YUV_NV12)
+ write_uv_rect(user_data.x, f, vec2(textureSize(sColor0, 0).xy), vUv_Y, vUvBounds_Y);
+ write_uv_rect(user_data.y, f, vec2(textureSize(sColor1, 0).xy), vUv_UV, vUvBounds_UV);
+#elif defined (WR_FEATURE_YUV_INTERLEAVED)
+ write_uv_rect(user_data.x, f, vec2(textureSize(sColor0, 0).xy), vUv_YUV, vUvBounds_YUV);
+#endif //WR_FEATURE_YUV_*
+}
+#endif
+
+#ifdef WR_FRAGMENT_SHADER
+
+#if !defined(WR_FEATURE_YUV_REC601) && !defined(WR_FEATURE_YUV_REC709)
+#define WR_FEATURE_YUV_REC601
+#endif
+
+// The constants added to the Y, U and V components are applied in the fragment shader.
+#if defined(WR_FEATURE_YUV_REC601)
+// From Rec601:
+// [R] [1.1643835616438356, 0.0, 1.5960267857142858 ] [Y - 16]
+// [G] = [1.1643835616438358, -0.3917622900949137, -0.8129676472377708 ] x [U - 128]
+// [B] [1.1643835616438356, 2.017232142857143, 8.862867620416422e-17] [V - 128]
+//
+// For the range [0,1] instead of [0,255].
+//
+// The matrix is stored in column-major.
+const mat3 YuvColorMatrix = mat3(
+ 1.16438, 1.16438, 1.16438,
+ 0.0, -0.39176, 2.01723,
+ 1.59603, -0.81297, 0.0
+);
+#elif defined(WR_FEATURE_YUV_REC709)
+// From Rec709:
+// [R] [1.1643835616438356, 4.2781193979771426e-17, 1.7927410714285714] [Y - 16]
+// [G] = [1.1643835616438358, -0.21324861427372963, -0.532909328559444 ] x [U - 128]
+// [B] [1.1643835616438356, 2.1124017857142854, 0.0 ] [V - 128]
+//
+// For the range [0,1] instead of [0,255]:
+//
+// The matrix is stored in column-major.
+const mat3 YuvColorMatrix = mat3(
+ 1.16438, 1.16438, 1.16438,
+ 0.0 , -0.21325, 2.11240,
+ 1.79274, -0.53291, 0.0
+);
+#endif
+
+vec4 brush_fs() {
+ vec3 yuv_value;
+
+#if defined (WR_FEATURE_YUV_PLANAR)
+ // The yuv_planar format should have this third texture coordinate.
+ vec2 uv_y = clamp(vUv_Y.xy, vUvBounds_Y.xy, vUvBounds_Y.zw);
+ vec2 uv_u = clamp(vUv_U.xy, vUvBounds_U.xy, vUvBounds_U.zw);
+ vec2 uv_v = clamp(vUv_V.xy, vUvBounds_V.xy, vUvBounds_V.zw);
+ yuv_value.x = TEX_SAMPLE(sColor0, vec3(uv_y, vUv_Y.z)).r;
+ yuv_value.y = TEX_SAMPLE(sColor1, vec3(uv_u, vUv_U.z)).r;
+ yuv_value.z = TEX_SAMPLE(sColor2, vec3(uv_v, vUv_V.z)).r;
+#elif defined (WR_FEATURE_YUV_NV12)
+ vec2 uv_y = clamp(vUv_Y.xy, vUvBounds_Y.xy, vUvBounds_Y.zw);
+ vec2 uv_uv = clamp(vUv_UV.xy, vUvBounds_UV.xy, vUvBounds_UV.zw);
+ yuv_value.x = TEX_SAMPLE(sColor0, vec3(uv_y, vUv_Y.z)).r;
+ yuv_value.yz = TEX_SAMPLE(sColor1, vec3(uv_uv, vUv_UV.z)).rg;
+#elif defined (WR_FEATURE_YUV_INTERLEAVED)
+ // "The Y, Cb and Cr color channels within the 422 data are mapped into
+ // the existing green, blue and red color channels."
+ // https://www.khronos.org/registry/OpenGL/extensions/APPLE/APPLE_rgb_422.txt
+ vec2 uv_y = clamp(vUv_YUV.xy, vUvBounds_YUV.xy, vUvBounds_YUV.zw);
+ yuv_value = TEX_SAMPLE(sColor0, vec3(uv_y, vUv_YUV.z)).gbr;
+#else
+ yuv_value = vec3(0.0);
+#endif
+
+ // See the YuvColorMatrix definition for an explanation of where the constants come from.
+ vec3 rgb = YuvColorMatrix * (yuv_value - vec3(0.06275, 0.50196, 0.50196));
+ vec4 color = vec4(rgb, 1.0);
+
+#ifdef WR_FEATURE_ALPHA_PASS
+ color *= init_transform_fs(vLocalPos);
+#endif
+
+ return color;
+}
+#endif
--- a/gfx/webrender/res/ellipse.glsl
+++ b/gfx/webrender/res/ellipse.glsl
@@ -1,81 +1,39 @@
/* 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_FRAGMENT_SHADER
+// One iteration of Newton's method on the 2D equation of an ellipse:
//
-// Signed distance to an ellipse.
-// Taken from http://www.iquilezles.org/www/articles/ellipsedist/ellipsedist.htm
-// Note that this fails for exact circles.
+// E(x, y) = x^2/a^2 + y^2/b^2 - 1
+//
+// The Jacobian of this equation is:
+//
+// J(E(x, y)) = [ 2*x/a^2 2*y/b^2 ]
+//
+// We approximate the distance with:
+//
+// E(x, y) / ||J(E(x, y))||
//
-float sdEllipse( vec2 p, in vec2 ab ) {
- p = abs( p ); if( p.x > p.y ){ p=p.yx; ab=ab.yx; }
- float l = ab.y*ab.y - ab.x*ab.x;
-
- float m = ab.x*p.x/l;
- float n = ab.y*p.y/l;
- float m2 = m*m;
- float n2 = n*n;
-
- float c = (m2 + n2 - 1.0)/3.0;
- float c3 = c*c*c;
-
- float q = c3 + m2*n2*2.0;
- float d = c3 + m2*n2;
- float g = m + m*n2;
-
- float co;
-
- if( d<0.0 )
- {
- float p = acos(q/c3)/3.0;
- float s = cos(p);
- float t = sin(p)*sqrt(3.0);
- float rx = sqrt( -c*(s + t + 2.0) + m2 );
- float ry = sqrt( -c*(s - t + 2.0) + m2 );
- co = ( ry + sign(l)*rx + abs(g)/(rx*ry) - m)/2.0;
+// See G. Taubin, "Distance Approximations for Rasterizing Implicit
+// Curves", section 3.
+float distance_to_ellipse(vec2 p, vec2 radii, float aa_range) {
+ float dist;
+ if (any(lessThanEqual(radii, vec2(0.0)))) {
+ dist = length(p);
+ } else {
+ vec2 invRadiiSq = 1.0 / (radii * radii);
+ float g = dot(p * p * invRadiiSq, vec2(1.0)) - 1.0;
+ vec2 dG = 2.0 * p * invRadiiSq;
+ dist = g * inversesqrt(dot(dG, dG));
}
- else
- {
- float h = 2.0*m*n*sqrt( d );
- float s = sign(q+h)*pow( abs(q+h), 1.0/3.0 );
- float u = sign(q-h)*pow( abs(q-h), 1.0/3.0 );
- float rx = -s - u - c*4.0 + 2.0*m2;
- float ry = (s - u)*sqrt(3.0);
- float rm = sqrt( rx*rx + ry*ry );
- float p = ry/sqrt(rm-rx);
- co = (p + 2.0*g/rm - m)/2.0;
- }
-
- float si = sqrt( 1.0 - co*co );
-
- vec2 r = vec2( ab.x*co, ab.y*si );
-
- return length(r - p ) * sign(p.y-r.y);
-}
-
-float distance_to_ellipse(vec2 p, vec2 radii, float aa_range) {
- // sdEllipse fails on exact circles, so handle equal
- // radii here. The branch coherency should make this
- // a performance win for the circle case too.
- float len = length(p);
- if (radii.x == radii.y) {
- return len - radii.x;
- } else {
- if (len < min(radii.x, radii.y) - aa_range) {
- return -aa_range;
- } else if (len > max(radii.x, radii.y) + aa_range) {
- return aa_range;
- }
-
- return sdEllipse(p, radii);
- }
+ return clamp(dist, -aa_range, aa_range);
}
float clip_against_ellipse_if_needed(
vec2 pos,
float current_distance,
vec4 ellipse_center_radius,
vec2 sign_modifier,
float aa_range
--- a/gfx/webrender/res/prim_shared.glsl
+++ b/gfx/webrender/res/prim_shared.glsl
@@ -310,37 +310,16 @@ struct Gradient {
vec4 extend_mode;
};
Gradient fetch_gradient(int address) {
vec4 data[3] = fetch_from_resource_cache_3(address);
return Gradient(data[0], data[1], data[2]);
}
-struct GradientStop {
- vec4 color;
- vec4 offset;
-};
-
-GradientStop fetch_gradient_stop(int address) {
- vec4 data[2] = fetch_from_resource_cache_2(address);
- return GradientStop(data[0], data[1]);
-}
-
-struct RadialGradient {
- vec4 start_end_center;
- vec4 start_end_radius_ratio_xy_extend_mode;
- vec4 tile_size_repeat;
-};
-
-RadialGradient fetch_radial_gradient(int address) {
- vec4 data[3] = fetch_from_resource_cache_3(address);
- return RadialGradient(data[0], data[1], data[2]);
-}
-
struct Glyph {
vec2 offset;
};
Glyph fetch_glyph(int specific_prim_address,
int glyph_index,
int subpx_dir) {
// Two glyphs are packed in each texel in the GPU cache.
@@ -619,17 +598,18 @@ vec2 intersect_lines(vec2 p0, vec2 p1, v
}
VertexInfo write_transform_vertex(RectWithSize local_segment_rect,
RectWithSize local_prim_rect,
RectWithSize local_clip_rect,
vec4 clip_edge_mask,
float z,
ClipScrollNode scroll_node,
- PictureTask task) {
+ PictureTask task,
+ bool do_perspective_interpolation) {
// Calculate a clip rect from local_rect + local clip
RectWithEndpoint clip_rect = to_rect_with_endpoint(local_clip_rect);
RectWithEndpoint segment_rect = to_rect_with_endpoint(local_segment_rect);
segment_rect.p0 = clamp(segment_rect.p0, clip_rect.p0, clip_rect.p1);
segment_rect.p1 = clamp(segment_rect.p1, clip_rect.p0, clip_rect.p1);
// Calculate a clip rect from local_rect + local clip
RectWithEndpoint prim_rect = to_rect_with_endpoint(local_prim_rect);
@@ -656,23 +636,26 @@ VertexInfo write_transform_vertex(RectWi
// Transform the current vertex to the world cpace.
vec4 world_pos = scroll_node.transform * vec4(local_pos, 0.0, 1.0);
// Convert the world positions to device pixel space.
vec2 device_pos = world_pos.xy / world_pos.w * uDevicePixelRatio;
vec2 task_offset = task.common_data.task_rect.p0 - task.content_origin;
+ // Force w = 1, if we don't want perspective interpolation (for
+ // example, drawing a screen-space quad on an element with a
+ // perspective transform).
+ world_pos.w = mix(1.0, world_pos.w, do_perspective_interpolation);
+
// We want the world space coords to be perspective divided by W.
// We also want that to apply to any interpolators. However, we
// want a constant Z across the primitive, since we're using it
// for draw ordering - so scale by the W coord to ensure this.
- vec4 final_pos = vec4((device_pos + task_offset) * world_pos.w,
- z * world_pos.w,
- world_pos.w);
+ vec4 final_pos = vec4(device_pos + task_offset, z, 1.0) * world_pos.w;
gl_Position = uTransform * final_pos;
vLocalBounds = mix(
vec4(prim_rect.p0, prim_rect.p1),
vec4(segment_rect.p0, segment_rect.p1),
clip_edge_mask
);
@@ -689,17 +672,18 @@ VertexInfo write_transform_vertex(RectWi
VertexInfo write_transform_vertex_primitive(Primitive prim) {
return write_transform_vertex(
prim.local_rect,
prim.local_rect,
prim.local_clip_rect,
vec4(1.0),
prim.z,
prim.scroll_node,
- prim.task
+ prim.task,
+ true
);
}
struct GlyphResource {
vec4 uv_rect;
float layer;
vec2 offset;
float scale;
--- a/gfx/webrender/res/ps_border_corner.glsl
+++ b/gfx/webrender/res/ps_border_corner.glsl
@@ -315,17 +315,18 @@ void main(void) {
#ifdef WR_FEATURE_TRANSFORM
VertexInfo vi = write_transform_vertex(segment_rect,
prim.local_rect,
prim.local_clip_rect,
edge_mask,
prim.z,
prim.scroll_node,
- prim.task);
+ prim.task,
+ true);
#else
VertexInfo vi = write_vertex(segment_rect,
prim.local_clip_rect,
prim.z,
prim.scroll_node,
prim.task,
prim.local_rect);
#endif
--- a/gfx/webrender/res/ps_border_edge.glsl
+++ b/gfx/webrender/res/ps_border_edge.glsl
@@ -228,17 +228,18 @@ void main(void) {
#ifdef WR_FEATURE_TRANSFORM
VertexInfo vi = write_transform_vertex(segment_rect,
prim.local_rect,
prim.local_clip_rect,
edge_mask,
prim.z,
prim.scroll_node,
- prim.task);
+ prim.task,
+ true);
#else
VertexInfo vi = write_vertex(segment_rect,
prim.local_clip_rect,
prim.z,
prim.scroll_node,
prim.task,
prim.local_rect);
#endif
--- a/gfx/webrender/res/ps_gradient.glsl
+++ b/gfx/webrender/res/ps_gradient.glsl
@@ -4,16 +4,26 @@
#include shared,prim_shared
varying vec4 vColor;
varying vec2 vLocalPos;
#ifdef WR_VERTEX_SHADER
+struct GradientStop {
+ vec4 color;
+ vec4 offset;
+};
+
+GradientStop fetch_gradient_stop(int address) {
+ vec4 data[2] = fetch_from_resource_cache_2(address);
+ return GradientStop(data[0], data[1]);
+}
+
void main(void) {
Primitive prim = load_primitive();
Gradient gradient = fetch_gradient(prim.specific_prim_address);
vec4 abs_start_end_point = gradient.start_end_point + prim.local_rect.p0.xyxy;
int stop_address = prim.specific_prim_address +
VECS_PER_GRADIENT +
@@ -68,17 +78,18 @@ void main(void) {
#ifdef WR_FEATURE_TRANSFORM
VertexInfo vi = write_transform_vertex(segment_rect,
prim.local_rect,
prim.local_clip_rect,
vec4(1.0),
prim.z,
prim.scroll_node,
- prim.task);
+ prim.task,
+ true);
vLocalPos = vi.local_pos;
vec2 f = (vi.local_pos.xy - prim.local_rect.p0) / prim.local_rect.size;
#else
VertexInfo vi = write_vertex(segment_rect,
prim.local_clip_rect,
prim.z,
prim.scroll_node,
prim.task,
deleted file mode 100644
--- a/gfx/webrender/res/ps_radial_gradient.glsl
+++ /dev/null
@@ -1,115 +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/. */
-
-#include shared,prim_shared
-
-flat varying int vGradientAddress;
-flat varying float vGradientRepeat;
-
-flat varying vec2 vStartCenter;
-flat varying vec2 vEndCenter;
-flat varying float vStartRadius;
-flat varying float vEndRadius;
-
-flat varying vec2 vTileSize;
-flat varying vec2 vTileRepeat;
-
-varying vec2 vPos;
-
-#ifdef WR_VERTEX_SHADER
-void main(void) {
- Primitive prim = load_primitive();
- RadialGradient gradient = fetch_radial_gradient(prim.specific_prim_address);
-
- VertexInfo vi = write_vertex(prim.local_rect,
- prim.local_clip_rect,
- prim.z,
- prim.scroll_node,
- prim.task,
- prim.local_rect);
-
- vPos = vi.local_pos - prim.local_rect.p0;
-
- vStartCenter = gradient.start_end_center.xy;
- vEndCenter = gradient.start_end_center.zw;
-
- vStartRadius = gradient.start_end_radius_ratio_xy_extend_mode.x;
- vEndRadius = gradient.start_end_radius_ratio_xy_extend_mode.y;
-
- vTileSize = gradient.tile_size_repeat.xy;
- vTileRepeat = gradient.tile_size_repeat.zw;
-
- // Transform all coordinates by the y scale so the
- // fragment shader can work with circles
- float ratio_xy = gradient.start_end_radius_ratio_xy_extend_mode.z;
- vPos.y *= ratio_xy;
- vStartCenter.y *= ratio_xy;
- vEndCenter.y *= ratio_xy;
- vTileSize.y *= ratio_xy;
- vTileRepeat.y *= ratio_xy;
-
- vGradientAddress = prim.specific_prim_address + VECS_PER_GRADIENT;
-
- // Whether to repeat the gradient instead of clamping.
- vGradientRepeat = float(int(gradient.start_end_radius_ratio_xy_extend_mode.w) != EXTEND_MODE_CLAMP);
-
- write_clip(vi.screen_pos, prim.clip_area);
-}
-#endif
-
-#ifdef WR_FRAGMENT_SHADER
-void main(void) {
- vec2 pos = mod(vPos, vTileRepeat);
-
- if (pos.x >= vTileSize.x ||
- pos.y >= vTileSize.y) {
- discard;
- }
-
- vec2 cd = vEndCenter - vStartCenter;
- vec2 pd = pos - vStartCenter;
- float rd = vEndRadius - vStartRadius;
-
- // Solve for t in length(t * cd - pd) = vStartRadius + t * rd
- // using a quadratic equation in form of At^2 - 2Bt + C = 0
- float A = dot(cd, cd) - rd * rd;
- float B = dot(pd, cd) + vStartRadius * rd;
- float C = dot(pd, pd) - vStartRadius * vStartRadius;
-
- float offset;
- if (A == 0.0) {
- // Since A is 0, just solve for -2Bt + C = 0
- if (B == 0.0) {
- discard;
- }
- float t = 0.5 * C / B;
- if (vStartRadius + rd * t >= 0.0) {
- offset = t;
- } else {
- discard;
- }
- } else {
- float discr = B * B - A * C;
- if (discr < 0.0) {
- discard;
- }
- discr = sqrt(discr);
- float t0 = (B + discr) / A;
- float t1 = (B - discr) / A;
- if (vStartRadius + rd * t0 >= 0.0) {
- offset = t0;
- } else if (vStartRadius + rd * t1 >= 0.0) {
- offset = t1;
- } else {
- discard;
- }
- }
-
- vec4 color = sample_gradient(vGradientAddress,
- offset,
- vGradientRepeat);
-
- oFragColor = color * do_clip();
-}
-#endif
deleted file mode 100644
--- a/gfx/webrender/res/ps_yuv_image.glsl
+++ /dev/null
@@ -1,197 +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/. */
-
-#include shared,prim_shared
-
-// If this is in WR_FEATURE_TEXTURE_RECT mode, the rect and size use non-normalized
-// texture coordinates. Otherwise, it uses normalized texture coordinates. Please
-// check GL_TEXTURE_RECTANGLE.
-flat varying vec2 vTextureOffsetY; // Offset of the y plane into the texture atlas.
-flat varying vec2 vTextureOffsetU; // Offset of the u plane into the texture atlas.
-flat varying vec2 vTextureOffsetV; // Offset of the v plane into the texture atlas.
-flat varying vec2 vTextureSizeY; // Size of the y plane in the texture atlas.
-flat varying vec2 vTextureSizeUv; // Size of the u and v planes in the texture atlas.
-flat varying vec2 vStretchSize;
-flat varying vec2 vHalfTexelY; // Normalized length of the half of a Y texel.
-flat varying vec2 vHalfTexelUv; // Normalized length of the half of u and v texels.
-flat varying vec3 vLayers;
-
-#ifdef WR_FEATURE_TRANSFORM
-flat varying vec4 vLocalRect;
-#endif
-
-varying vec2 vLocalPos;
-
-#ifdef WR_VERTEX_SHADER
-struct YuvImage {
- vec2 size;
-};
-
-YuvImage fetch_yuv_image(int address) {
- vec4 data = fetch_from_resource_cache_1(address);
- return YuvImage(data.xy);
-}
-
-void main(void) {
- Primitive prim = load_primitive();
-#ifdef WR_FEATURE_TRANSFORM
- VertexInfo vi = write_transform_vertex_primitive(prim);
- vLocalPos = vi.local_pos;
- vLocalRect = vec4(prim.local_rect.p0, prim.local_rect.p0 + prim.local_rect.size);
-#else
- VertexInfo vi = write_vertex(prim.local_rect,
- prim.local_clip_rect,
- prim.z,
- prim.scroll_node,
- prim.task,
- prim.local_rect);
- vLocalPos = vi.local_pos - prim.local_rect.p0;
-#endif
-
- write_clip(vi.screen_pos, prim.clip_area);
-
- ImageResource y_rect = fetch_image_resource(prim.user_data0);
- vLayers = vec3(y_rect.layer, 0.0, 0.0);
-
-#ifndef WR_FEATURE_INTERLEAVED_Y_CB_CR // only 1 channel
- ImageResource u_rect = fetch_image_resource(prim.user_data1);
- vLayers.y = u_rect.layer;
-#ifndef WR_FEATURE_NV12 // 2 channel
- ImageResource v_rect = fetch_image_resource(prim.user_data2);
- vLayers.z = v_rect.layer;
-#endif
-#endif
-
- // If this is in WR_FEATURE_TEXTURE_RECT mode, the rect and size use
- // non-normalized texture coordinates.
-#ifdef WR_FEATURE_TEXTURE_RECT
- vec2 y_texture_size_normalization_factor = vec2(1, 1);
-#else
- vec2 y_texture_size_normalization_factor = vec2(textureSize(sColor0, 0));
-#endif
- vec2 y_st0 = y_rect.uv_rect.p0 / y_texture_size_normalization_factor;
- vec2 y_st1 = y_rect.uv_rect.p1 / y_texture_size_normalization_factor;
-
- vTextureSizeY = y_st1 - y_st0;
- vTextureOffsetY = y_st0;
-
-#ifndef WR_FEATURE_INTERLEAVED_Y_CB_CR
- // This assumes the U and V surfaces have the same size.
-#ifdef WR_FEATURE_TEXTURE_RECT
- vec2 uv_texture_size_normalization_factor = vec2(1, 1);
-#else
- vec2 uv_texture_size_normalization_factor = vec2(textureSize(sColor1, 0));
-#endif
- vec2 u_st0 = u_rect.uv_rect.p0 / uv_texture_size_normalization_factor;
- vec2 u_st1 = u_rect.uv_rect.p1 / uv_texture_size_normalization_factor;
-
-#ifndef WR_FEATURE_NV12
- vec2 v_st0 = v_rect.uv_rect.p0 / uv_texture_size_normalization_factor;
-#endif
-
- vTextureSizeUv = u_st1 - u_st0;
- vTextureOffsetU = u_st0;
-#ifndef WR_FEATURE_NV12
- vTextureOffsetV = v_st0;
-#endif
-#endif
-
- YuvImage image = fetch_yuv_image(prim.specific_prim_address);
- vStretchSize = image.size;
-
- vHalfTexelY = vec2(0.5) / y_texture_size_normalization_factor;
-#ifndef WR_FEATURE_INTERLEAVED_Y_CB_CR
- vHalfTexelUv = vec2(0.5) / uv_texture_size_normalization_factor;
-#endif
-}
-#endif
-
-#ifdef WR_FRAGMENT_SHADER
-#if !defined(WR_FEATURE_YUV_REC601) && !defined(WR_FEATURE_YUV_REC709)
-#define WR_FEATURE_YUV_REC601
-#endif
-
-// The constants added to the Y, U and V components are applied in the fragment shader.
-#if defined(WR_FEATURE_YUV_REC601)
-// From Rec601:
-// [R] [1.1643835616438356, 0.0, 1.5960267857142858 ] [Y - 16]
-// [G] = [1.1643835616438358, -0.3917622900949137, -0.8129676472377708 ] x [U - 128]
-// [B] [1.1643835616438356, 2.017232142857143, 8.862867620416422e-17] [V - 128]
-//
-// For the range [0,1] instead of [0,255].
-//
-// The matrix is stored in column-major.
-const mat3 YuvColorMatrix = mat3(
- 1.16438, 1.16438, 1.16438,
- 0.0, -0.39176, 2.01723,
- 1.59603, -0.81297, 0.0
-);
-#elif defined(WR_FEATURE_YUV_REC709)
-// From Rec709:
-// [R] [1.1643835616438356, 4.2781193979771426e-17, 1.7927410714285714] [Y - 16]
-// [G] = [1.1643835616438358, -0.21324861427372963, -0.532909328559444 ] x [U - 128]
-// [B] [1.1643835616438356, 2.1124017857142854, 0.0 ] [V - 128]
-//
-// For the range [0,1] instead of [0,255]:
-//
-// The matrix is stored in column-major.
-const mat3 YuvColorMatrix = mat3(
- 1.16438, 1.16438, 1.16438,
- 0.0 , -0.21325, 2.11240,
- 1.79274, -0.53291, 0.0
-);
-#endif
-
-void main(void) {
-#ifdef WR_FEATURE_TRANSFORM
- float alpha = init_transform_fs(vLocalPos);
-
- // We clamp the texture coordinate calculation here to the local rectangle boundaries,
- // which makes the edge of the texture stretch instead of repeat.
- vec2 relative_pos_in_rect = clamp(vLocalPos, vLocalRect.xy, vLocalRect.zw) - vLocalRect.xy;
-#else
- float alpha = 1.0;;
- vec2 relative_pos_in_rect = vLocalPos;
-#endif
-
- alpha *= do_clip();
-
- // We clamp the texture coordinates to the half-pixel offset from the borders
- // in order to avoid sampling outside of the texture area.
- vec2 st_y = vTextureOffsetY + clamp(
- relative_pos_in_rect / vStretchSize * vTextureSizeY,
- vHalfTexelY, vTextureSizeY - vHalfTexelY);
-#ifndef WR_FEATURE_INTERLEAVED_Y_CB_CR
- vec2 uv_offset = clamp(
- relative_pos_in_rect / vStretchSize * vTextureSizeUv,
- vHalfTexelUv, vTextureSizeUv - vHalfTexelUv);
- // NV12 only uses 2 textures. The sColor0 is for y and sColor1 is for uv.
- // The texture coordinates of u and v are the same. So, we could skip the
- // st_v if the format is NV12.
- vec2 st_u = vTextureOffsetU + uv_offset;
-#endif
-
- vec3 yuv_value;
-#ifdef WR_FEATURE_INTERLEAVED_Y_CB_CR
- // "The Y, Cb and Cr color channels within the 422 data are mapped into
- // the existing green, blue and red color channels."
- // https://www.khronos.org/registry/OpenGL/extensions/APPLE/APPLE_rgb_422.txt
- yuv_value = TEX_SAMPLE(sColor0, vec3(st_y, vLayers.x)).gbr;
-#elif defined(WR_FEATURE_NV12)
- yuv_value.x = TEX_SAMPLE(sColor0, vec3(st_y, vLayers.x)).r;
- yuv_value.yz = TEX_SAMPLE(sColor1, vec3(st_u, vLayers.y)).rg;
-#else
- // The yuv_planar format should have this third texture coordinate.
- vec2 st_v = vTextureOffsetV + uv_offset;
-
- yuv_value.x = TEX_SAMPLE(sColor0, vec3(st_y, vLayers.x)).r;
- yuv_value.y = TEX_SAMPLE(sColor1, vec3(st_u, vLayers.y)).r;
- yuv_value.z = TEX_SAMPLE(sColor2, vec3(st_v, vLayers.z)).r;
-#endif
-
- // See the YuvColorMatrix definition for an explanation of where the constants come from.
- vec3 rgb = YuvColorMatrix * (yuv_value - vec3(0.06275, 0.50196, 0.50196));
- oFragColor = vec4(rgb * alpha, alpha);
-}
-#endif
--- a/gfx/webrender/src/batch.rs
+++ b/gfx/webrender/src/batch.rs
@@ -2,32 +2,30 @@
* 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, DeviceIntRect, DeviceIntSize, LayerToWorldScale};
use api::{DeviceUintRect, DeviceUintPoint, DeviceUintSize, ExternalImageType, FilterOp, ImageRendering, LayerRect};
use api::{DeviceIntPoint, LayerPoint, SubpixelDirection, YuvColorSpace, YuvFormat};
use api::{LayerToWorldTransform, WorldPixel};
use border::{BorderCornerInstance, BorderCornerSide, BorderEdgeKind};
-use clip::{ClipSource, ClipStore};
+use clip::{ClipSource, ClipStore, ClipWorkItem};
use clip_scroll_tree::{CoordinateSystemId};
use euclid::{TypedTransform3D, vec3};
use glyph_rasterizer::GlyphFormat;
use gpu_cache::{GpuCache, GpuCacheAddress};
-use gpu_types::{BrushImageKind, BrushInstance, ClipChainRectIndex};
+use gpu_types::{BrushFlags, BrushImageKind, BrushInstance, ClipChainRectIndex};
use gpu_types::{ClipMaskInstance, ClipScrollNodeIndex};
use gpu_types::{CompositePrimitiveInstance, PrimitiveInstance, SimplePrimitiveInstance};
-use internal_types::{FastHashMap, SourceTexture};
+use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture};
use picture::{ContentOrigin, PictureCompositeMode, PictureKind, PicturePrimitive, PictureSurface};
use plane_split::{BspSplitter, Polygon, Splitter};
use prim_store::{ImageSource, PrimitiveIndex, PrimitiveKind, PrimitiveMetadata, PrimitiveStore};
use prim_store::{BrushPrimitive, BrushKind, DeferredResolve, EdgeAaSegmentMask, PrimitiveRun};
-use render_task::{ClipWorkItem};
-use render_task::{RenderTaskAddress, RenderTaskId};
-use render_task::{RenderTaskKind, RenderTaskTree};
+use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskKind, RenderTaskTree};
use renderer::{BlendMode, ImageBufferKind};
use renderer::BLOCKS_PER_UV_RECT;
use resource_cache::{CacheItem, GlyphFetchResult, ImageRequest, ResourceCache};
use std::{usize, f32, i32};
use tiling::{RenderTargetContext, RenderTargetKind};
use util::{MatrixHelpers, TransformedRectKind};
// Special sentinel value recognized by the shader. It is considered to be
@@ -35,20 +33,18 @@ use util::{MatrixHelpers, TransformedRec
const OPAQUE_TASK_ADDRESS: RenderTaskAddress = RenderTaskAddress(0x7fff);
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum TransformBatchKind {
TextRun(GlyphFormat),
Image(ImageBufferKind),
- YuvImage(ImageBufferKind, YuvFormat, YuvColorSpace),
AlignedGradient,
AngleGradient,
- RadialGradient,
BorderCorner,
BorderEdge,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum BrushImageSourceKind {
@@ -75,16 +71,18 @@ pub enum BrushBatchKind {
Line,
Image(ImageBufferKind),
Blend,
MixBlend {
task_id: RenderTaskId,
source_id: RenderTaskId,
backdrop_id: RenderTaskId,
},
+ YuvImage(ImageBufferKind, YuvFormat, YuvColorSpace),
+ RadialGradient,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum BatchKind {
HardwareComposite,
SplitComposite,
@@ -948,16 +946,17 @@ impl AlphaBatchBuilder {
picture_address: task_address,
prim_address: prim_cache_address,
clip_chain_rect_index,
scroll_id,
clip_task_address,
z,
segment_index: 0,
edge_flags: EdgeAaSegmentMask::empty(),
+ brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
user_data: [
cache_task_address.0 as i32,
BrushImageKind::Simple as i32,
0,
],
};
batch.push(PrimitiveInstance::from(instance));
}
@@ -1034,35 +1033,36 @@ impl AlphaBatchBuilder {
picture_address: task_address,
prim_address: prim_cache_address,
clip_chain_rect_index,
scroll_id,
clip_task_address,
z,
segment_index: 0,
edge_flags: EdgeAaSegmentMask::empty(),
+ brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
user_data: [
cache_task_address.0 as i32,
BrushImageKind::Simple as i32,
0,
],
};
{
let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
batch.push(PrimitiveInstance::from(instance));
}
let secondary_id = secondary_render_task_id.expect("no secondary!?");
- let render_task = &render_tasks[secondary_id];
+ let saved_index = render_tasks[secondary_id].saved_index.expect("no saved index!?");
+ debug_assert_ne!(saved_index, SavedTargetIndex::PENDING);
let secondary_task_address = render_tasks.get_task_address(secondary_id);
- let render_pass_index = render_task.pass_index.expect("no render_pass_index!?");
let secondary_textures = BatchTextures {
colors: [
- SourceTexture::RenderTaskCacheRGBA8(render_pass_index),
+ SourceTexture::RenderTaskCache(saved_index),
SourceTexture::Invalid,
SourceTexture::Invalid,
],
};
let key = BatchKey::new(
BatchKind::HardwareComposite,
BlendMode::PremultipliedAlpha,
secondary_textures,
@@ -1111,16 +1111,17 @@ impl AlphaBatchBuilder {
picture_address: task_address,
prim_address: prim_cache_address,
clip_chain_rect_index,
scroll_id,
clip_task_address,
z,
segment_index: 0,
edge_flags: EdgeAaSegmentMask::empty(),
+ brush_flags: BrushFlags::empty(),
user_data: [
cache_task_address.0 as i32,
filter_mode,
0,
],
};
let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
@@ -1150,16 +1151,17 @@ impl AlphaBatchBuilder {
picture_address: task_address,
prim_address: prim_cache_address,
clip_chain_rect_index,
scroll_id,
clip_task_address,
z,
segment_index: 0,
edge_flags: EdgeAaSegmentMask::empty(),
+ brush_flags: BrushFlags::empty(),
user_data: [
mode as u32 as i32,
backdrop_task_address.0 as i32,
source_task_address.0 as i32,
],
};
batch.push(PrimitiveInstance::from(instance));
@@ -1221,83 +1223,16 @@ impl AlphaBatchBuilder {
let kind = BatchKind::Transformable(
transform_kind,
TransformBatchKind::AngleGradient,
);
let key = BatchKey::new(kind, non_segmented_blend_mode, no_textures);
let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
batch.push(base_instance.build(0, 0, 0));
}
- PrimitiveKind::RadialGradient => {
- let kind = BatchKind::Transformable(
- transform_kind,
- TransformBatchKind::RadialGradient,
- );
- let key = BatchKey::new(kind, non_segmented_blend_mode, no_textures);
- let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
- batch.push(base_instance.build(0, 0, 0));
- }
- PrimitiveKind::YuvImage => {
- let mut textures = BatchTextures::no_texture();
- let mut uv_rect_addresses = [0; 3];
- let image_yuv_cpu =
- &ctx.prim_store.cpu_yuv_images[prim_metadata.cpu_prim_index.0];
-
- //yuv channel
- let channel_count = image_yuv_cpu.format.get_plane_num();
- debug_assert!(channel_count <= 3);
- for channel in 0 .. channel_count {
- let image_key = image_yuv_cpu.yuv_key[channel];
-
- let cache_item = resolve_image(
- ImageRequest {
- key: image_key,
- rendering: image_yuv_cpu.image_rendering,
- tile: None,
- },
- ctx.resource_cache,
- gpu_cache,
- deferred_resolves,
- );
-
- if cache_item.texture_id == SourceTexture::Invalid {
- warn!("Warnings: skip a PrimitiveKind::YuvImage");
- debug!("at {:?}.", task_relative_bounding_rect);
- return;
- }
-
- textures.colors[channel] = cache_item.texture_id;
- uv_rect_addresses[channel] = cache_item.uv_rect_handle.as_int(gpu_cache);
- }
-
- // All yuv textures should be the same type.
- let buffer_kind = get_buffer_kind(textures.colors[0]);
- assert!(
- textures.colors[1 .. image_yuv_cpu.format.get_plane_num()]
- .iter()
- .all(|&tid| buffer_kind == get_buffer_kind(tid))
- );
-
- let kind = BatchKind::Transformable(
- transform_kind,
- TransformBatchKind::YuvImage(
- buffer_kind,
- image_yuv_cpu.format,
- image_yuv_cpu.color_space,
- ),
- );
- let key = BatchKey::new(kind, non_segmented_blend_mode, textures);
- let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
-
- batch.push(base_instance.build(
- uv_rect_addresses[0],
- uv_rect_addresses[1],
- uv_rect_addresses[2],
- ));
- }
}
}
fn add_brush_to_batch(
&mut self,
brush: &BrushPrimitive,
prim_metadata: &PrimitiveMetadata,
batch_kind: BrushBatchKind,
@@ -1319,16 +1254,17 @@ impl AlphaBatchBuilder {
picture_address: task_address,
prim_address: prim_cache_address,
clip_chain_rect_index,
scroll_id,
clip_task_address,
z,
segment_index: 0,
edge_flags: EdgeAaSegmentMask::all(),
+ brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
user_data,
};
match brush.segment_desc {
Some(ref segment_desc) => {
let alpha_batch_key = BatchKey {
blend_mode: alpha_blend_mode,
kind: BatchKind::Brush(batch_kind),
@@ -1435,16 +1371,81 @@ impl BrushPrimitive {
}
BrushKind::Clear => {
Some((
BrushBatchKind::Solid,
BatchTextures::no_texture(),
[0; 3],
))
}
+ BrushKind::RadialGradient { ref stops_handle, .. } => {
+ Some((
+ BrushBatchKind::RadialGradient,
+ BatchTextures::no_texture(),
+ [
+ stops_handle.as_int(gpu_cache),
+ 0,
+ 0,
+ ],
+ ))
+ }
+ BrushKind::YuvImage { format, yuv_key, image_rendering, color_space } => {
+ let mut textures = BatchTextures::no_texture();
+ let mut uv_rect_addresses = [0; 3];
+
+ //yuv channel
+ let channel_count = format.get_plane_num();
+ debug_assert!(channel_count <= 3);
+ for channel in 0 .. channel_count {
+ let image_key = yuv_key[channel];
+
+ let cache_item = resolve_image(
+ ImageRequest {
+ key: image_key,
+ rendering: image_rendering,
+ tile: None,
+ },
+ resource_cache,
+ gpu_cache,
+ deferred_resolves,
+ );
+
+ if cache_item.texture_id == SourceTexture::Invalid {
+ warn!("Warnings: skip a PrimitiveKind::YuvImage");
+ return None;
+ }
+
+ textures.colors[channel] = cache_item.texture_id;
+ uv_rect_addresses[channel] = cache_item.uv_rect_handle.as_int(gpu_cache);
+ }
+
+ // All yuv textures should be the same type.
+ let buffer_kind = get_buffer_kind(textures.colors[0]);
+ assert!(
+ textures.colors[1 .. format.get_plane_num()]
+ .iter()
+ .all(|&tid| buffer_kind == get_buffer_kind(tid))
+ );
+
+ let kind = BrushBatchKind::YuvImage(
+ buffer_kind,
+ format,
+ color_space,
+ );
+
+ Some((
+ kind,
+ textures,
+ [
+ uv_rect_addresses[0],
+ uv_rect_addresses[1],
+ uv_rect_addresses[2],
+ ],
+ ))
+ }
BrushKind::Mask { .. } => {
unreachable!("bug: mask brushes not expected in normal alpha pass");
}
}
}
}
trait AlphaBatchHelpers {
@@ -1458,20 +1459,18 @@ impl AlphaBatchHelpers for PrimitiveStor
fn get_blend_mode(&self, metadata: &PrimitiveMetadata) -> BlendMode {
match metadata.prim_kind {
// Can only resolve the TextRun's blend mode once glyphs are fetched.
PrimitiveKind::TextRun => {
BlendMode::PremultipliedAlpha
}
PrimitiveKind::Border |
- PrimitiveKind::YuvImage |
PrimitiveKind::AlignedGradient |
PrimitiveKind::AngleGradient |
- PrimitiveKind::RadialGradient |
PrimitiveKind::Picture => {
BlendMode::PremultipliedAlpha
}
PrimitiveKind::Brush => {
let brush = &self.cpu_brushes[metadata.cpu_prim_index.0];
match brush.kind {
BrushKind::Clear => {
@@ -1481,16 +1480,18 @@ impl AlphaBatchHelpers for PrimitiveStor
match alpha_type {
AlphaType::PremultipliedAlpha => BlendMode::PremultipliedAlpha,
AlphaType::Alpha => BlendMode::Alpha,
}
}
BrushKind::Solid { .. } |
BrushKind::Mask { .. } |
BrushKind::Line { .. } |
+ BrushKind::YuvImage { .. } |
+ BrushKind::RadialGradient { .. } |
BrushKind::Picture => {
BlendMode::PremultipliedAlpha
}
}
}
PrimitiveKind::Image => {
let image_cpu = &self.cpu_images[metadata.cpu_prim_index.0];
match image_cpu.alpha_type {
--- a/gfx/webrender/src/border.rs
+++ b/gfx/webrender/src/border.rs
@@ -1,21 +1,20 @@
/* 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/. */
-use api::{BorderRadius, BorderSide, BorderStyle, BorderWidths, ClipAndScrollInfo, ColorF};
-use api::{LayerPoint, LayerRect, LayerPrimitiveInfo, LayerSize};
-use api::{NormalBorder, RepeatMode, TexelRect};
+use api::{BorderRadius, BorderSide, BorderStyle, BorderWidths, ColorF, LayerPoint};
+use api::{LayerPrimitiveInfo, LayerRect, LayerSize, NormalBorder, RepeatMode, TexelRect};
use clip::ClipSource;
use ellipse::Ellipse;
use frame_builder::FrameBuilder;
use gpu_cache::GpuDataRequest;
-use prim_store::{BorderPrimitiveCpu, BrushSegment, BrushSegmentDescriptor};
-use prim_store::{BrushClipMaskKind, EdgeAaSegmentMask, PrimitiveContainer};
+use prim_store::{BorderPrimitiveCpu, BrushClipMaskKind, BrushSegment, BrushSegmentDescriptor};
+use prim_store::{EdgeAaSegmentMask, PrimitiveContainer, ScrollNodeAndClipChain};
use util::{lerp, pack_as_float};
#[repr(u8)]
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum BorderCornerInstance {
None,
Single, // Single instance needed - corner styles are same or similar.
Double, // Different corner styles. Draw two instances, one per style.
@@ -281,17 +280,17 @@ pub fn ensure_no_corner_overlap(
}
impl FrameBuilder {
fn add_normal_border_primitive(
&mut self,
info: &LayerPrimitiveInfo,
border: &NormalBorder,
widths: &BorderWidths,
- clip_and_scroll: ClipAndScrollInfo,
+ clip_and_scroll: ScrollNodeAndClipChain,
corner_instances: [BorderCornerInstance; 4],
edges: [BorderEdgeKind; 4],
clip_sources: Vec<ClipSource>,
) {
let radius = &border.radius;
let left = &border.left;
let right = &border.right;
let top = &border.top;
@@ -349,17 +348,17 @@ impl FrameBuilder {
// simplified shader model one at a time. Once all borders
// are converted, this can be removed, along with the complex
// border code path.
pub fn add_normal_border(
&mut self,
info: &LayerPrimitiveInfo,
border: &NormalBorder,
widths: &BorderWidths,
- clip_and_scroll: ClipAndScrollInfo,
+ clip_and_scroll: ScrollNodeAndClipChain,
) {
// The border shader is quite expensive. For simple borders, we can just draw
// the border with a few rectangles. This generally gives better batching, and
// a GPU win in fragment shader time.
// More importantly, the software (OSMesa) implementation we run tests on is
// particularly slow at running our complex border shader, compared to the
// rectangle shader. This has the effect of making some of our tests time
// out more often on CI (the actual cause is simply too many Servo processes and
--- a/gfx/webrender/src/box_shadow.rs
+++ b/gfx/webrender/src/box_shadow.rs
@@ -1,22 +1,21 @@
/* 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/. */
-use api::{ColorF, LayerPoint, LayerRect, LayerSize, LayerVector2D};
-use api::{BorderRadius, BoxShadowClipMode, LayoutSize, LayerPrimitiveInfo};
-use api::{ClipMode, ClipAndScrollInfo, ComplexClipRegion, LocalClip};
-use api::{PipelineId};
+use api::{BorderRadius, BoxShadowClipMode, ClipMode, ColorF, ComplexClipRegion, LayerPoint};
+use api::{LayerPrimitiveInfo, LayerRect, LayerSize, LayerVector2D, LayoutSize, LocalClip};
+use api::PipelineId;
use app_units::Au;
use clip::ClipSource;
use frame_builder::FrameBuilder;
use gpu_types::BrushImageKind;
-use prim_store::{PrimitiveContainer};
-use prim_store::{BrushMaskKind, BrushKind, BrushPrimitive};
+use prim_store::{BrushKind, BrushMaskKind, BrushPrimitive, PrimitiveContainer};
+use prim_store::ScrollNodeAndClipChain;
use picture::PicturePrimitive;
use util::RectHelpers;
use render_task::MAX_BLUR_STD_DEVIATION;
// The blur shader samples BLUR_SAMPLE_SCALE * blur_radius surrounding texels.
pub const BLUR_SAMPLE_SCALE: f32 = 3.0;
// Maximum blur radius.
@@ -48,17 +47,17 @@ pub struct BoxShadowCacheKey {
pub br_bottom_right_h: Au,
pub clip_mode: BoxShadowClipMode,
}
impl FrameBuilder {
pub fn add_box_shadow(
&mut self,
pipeline_id: PipelineId,
- clip_and_scroll: ClipAndScrollInfo,
+ clip_and_scroll: ScrollNodeAndClipChain,
prim_info: &LayerPrimitiveInfo,
box_offset: &LayerVector2D,
color: &ColorF,
mut blur_radius: f32,
spread_radius: f32,
border_radius: BorderRadius,
clip_mode: BoxShadowClipMode,
) {
@@ -83,17 +82,18 @@ impl FrameBuilder {
let shadow_rect = prim_info.rect
.translate(box_offset)
.inflate(spread_amount, spread_amount);
if blur_radius == 0.0 {
if box_offset.x == 0.0 && box_offset.y == 0.0 && spread_amount == 0.0 {
return;
}
- let mut clips = Vec::new();
+ let mut clips = Vec::with_capacity(2);
+ clips.push(ClipSource::Rectangle(*prim_info.local_clip.clip_rect()));
let fast_info = match clip_mode {
BoxShadowClipMode::Outset => {
// TODO(gw): Add a fast path for ClipOut + zero border radius!
clips.push(ClipSource::new_rounded_rect(
prim_info.rect,
border_radius,
ClipMode::ClipOut
@@ -259,18 +259,20 @@ impl FrameBuilder {
clip_and_scroll
);
extra_clips.push(ClipSource::new_rounded_rect(
prim_info.rect,
border_radius,
ClipMode::ClipOut,
));
-
- let pic_info = LayerPrimitiveInfo::new(pic_rect);
+ let pic_info = LayerPrimitiveInfo::with_clip_rect(
+ pic_rect,
+ *prim_info.local_clip.clip_rect()
+ );
self.add_primitive(
clip_and_scroll,
&pic_info,
extra_clips,
PrimitiveContainer::Picture(pic_prim),
);
}
BoxShadowClipMode::Inset => {
@@ -327,24 +329,30 @@ impl FrameBuilder {
cache_key,
pipeline_id,
);
pic_prim.add_primitive(
brush_prim_index,
clip_and_scroll
);
+ let clip_rect = prim_info.local_clip.clip_rect();
+ let clip_rect = match prim_info.rect.intersection(clip_rect) {
+ Some(clip_rect) => clip_rect,
+ None => return,
+ };
+
// Draw the picture one pixel outside the original
// rect to account for the inflate above. This
// extra edge will be clipped by the local clip
// rect set below.
let pic_rect = prim_info.rect.inflate(inflate_size + box_offset.x.abs(), inflate_size + box_offset.y.abs());
let pic_info = LayerPrimitiveInfo::with_clip_rect(
pic_rect,
- prim_info.rect
+ clip_rect
);
// Add a normal clip to ensure nothing gets drawn
// outside the primitive rect.
if !border_radius.is_zero() {
extra_clips.push(ClipSource::new_rounded_rect(
prim_info.rect,
border_radius,
--- a/gfx/webrender/src/clip.rs
+++ b/gfx/webrender/src/clip.rs
@@ -1,22 +1,25 @@
/* 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/. */
use api::{BorderRadius, ClipMode, ComplexClipRegion, DeviceIntRect, DevicePixelScale, ImageMask};
use api::{ImageRendering, LayerRect, LayerToWorldTransform, LayoutPoint, LayoutVector2D};
use api::LocalClip;
use border::{BorderCornerClipSource, ensure_no_corner_overlap};
+use clip_scroll_tree::{ClipChainIndex, CoordinateSystemId};
use ellipse::Ellipse;
use freelist::{FreeList, FreeListHandle, WeakFreeListHandle};
use gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks};
+use gpu_types::ClipScrollNodeIndex;
use prim_store::{ClipData, ImageMaskData};
use resource_cache::{ImageRequest, ResourceCache};
use util::{MaxRect, MatrixHelpers, calculate_screen_bounding_rect, extract_inner_rect_safe};
+use std::rc::Rc;
pub type ClipStore = FreeList<ClipSources>;
pub type ClipSourcesHandle = FreeListHandle<ClipSources>;
pub type ClipSourcesWeakHandle = WeakFreeListHandle<ClipSources>;
#[derive(Clone, Debug)]
pub struct ClipRegion {
pub main: LayerRect,
@@ -344,8 +347,111 @@ pub fn rounded_rectangle_contains_point(
LayoutVector2D::new(radii.bottom_left.width, -radii.bottom_left.height);
if bottom_left_center.x > point.x && bottom_left_center.y < point.y &&
!Ellipse::new(radii.bottom_left).contains(*point - bottom_left_center.to_vector()) {
return false;
}
true
}
+
+pub type ClipChainNodeRef = Option<Rc<ClipChainNode>>;
+
+#[derive(Debug, Clone)]
+pub struct ClipChainNode {
+ pub work_item: ClipWorkItem,
+ pub local_clip_rect: LayerRect,
+ pub screen_outer_rect: DeviceIntRect,
+ pub screen_inner_rect: DeviceIntRect,
+ pub prev: ClipChainNodeRef,
+}
+
+#[derive(Debug, Clone)]
+pub struct ClipChain {
+ pub parent_index: Option<ClipChainIndex>,
+ pub combined_outer_screen_rect: DeviceIntRect,
+ pub combined_inner_screen_rect: DeviceIntRect,
+ pub nodes: ClipChainNodeRef,
+}
+
+impl ClipChain {
+ pub fn empty(screen_rect: &DeviceIntRect) -> ClipChain {
+ ClipChain {
+ parent_index: None,
+ combined_inner_screen_rect: *screen_rect,
+ combined_outer_screen_rect: *screen_rect,
+ nodes: None,
+ }
+ }
+
+ pub fn new_with_added_node(
+ &self,
+ work_item: ClipWorkItem,
+ local_clip_rect: LayerRect,
+ screen_outer_rect: DeviceIntRect,
+ screen_inner_rect: DeviceIntRect,
+ ) -> ClipChain {
+ // If the new node's inner rectangle completely surrounds our outer rectangle,
+ // we can discard the new node entirely since it isn't going to affect anything.
+ if screen_inner_rect.contains_rect(&self.combined_outer_screen_rect) {
+ return self.clone();
+ }
+
+ let new_node = ClipChainNode {
+ work_item,
+ local_clip_rect,
+ screen_outer_rect,
+ screen_inner_rect,
+ prev: None,
+ };
+
+ let mut new_chain = self.clone();
+ new_chain.add_node(new_node);
+ new_chain
+ }
+
+ pub fn add_node(&mut self, mut new_node: ClipChainNode) {
+ new_node.prev = self.nodes.clone();
+
+ // If this clip's outer rectangle is completely enclosed by the clip
+ // chain's inner rectangle, then the only clip that matters from this point
+ // on is this clip. We can disconnect this clip from the parent clip chain.
+ if self.combined_inner_screen_rect.contains_rect(&new_node.screen_outer_rect) {
+ new_node.prev = None;
+ }
+
+ self.combined_outer_screen_rect =
+ self.combined_outer_screen_rect.intersection(&new_node.screen_outer_rect)
+ .unwrap_or_else(DeviceIntRect::zero);
+ self.combined_inner_screen_rect =
+ self.combined_inner_screen_rect.intersection(&new_node.screen_inner_rect)
+ .unwrap_or_else(DeviceIntRect::zero);
+
+ self.nodes = Some(Rc::new(new_node));
+ }
+}
+
+pub struct ClipChainNodeIter {
+ pub current: ClipChainNodeRef,
+}
+
+impl Iterator for ClipChainNodeIter {
+ type Item = Rc<ClipChainNode>;
+
+ fn next(&mut self) -> ClipChainNodeRef {
+ let previous = self.current.clone();
+ self.current = match self.current {
+ Some(ref item) => item.prev.clone(),
+ None => return None,
+ };
+ previous
+ }
+}
+
+#[derive(Debug, Clone)]
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub struct ClipWorkItem {
+ pub scroll_node_data_index: ClipScrollNodeIndex,
+ pub clip_sources: ClipSourcesWeakHandle,
+ pub coordinate_system_id: CoordinateSystemId,
+}
+
--- a/gfx/webrender/src/clip_scroll_node.rs
+++ b/gfx/webrender/src/clip_scroll_node.rs
@@ -1,23 +1,22 @@
/* 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/. */
use api::{ClipId, DevicePixelScale, ExternalScrollId, LayerPixel, LayerPoint, LayerRect};
use api::{LayerSize, LayerToWorldTransform, LayerTransform, LayerVector2D, LayoutTransform};
use api::{LayoutVector2D, PipelineId, PropertyBinding, ScrollClamping, ScrollEventPhase};
-use api::{ScrollLocation, ScrollNodeIdType, ScrollSensitivity, StickyOffsetBounds, WorldPoint};
-use clip::{ClipSourcesHandle, ClipStore};
-use clip_scroll_tree::{CoordinateSystemId, TransformUpdateState};
+use api::{ScrollLocation, ScrollSensitivity, StickyOffsetBounds, WorldPoint};
+use clip::{ClipChain, ClipSourcesHandle, ClipStore, ClipWorkItem};
+use clip_scroll_tree::{ClipChainIndex, CoordinateSystemId, TransformUpdateState};
use euclid::SideOffsets2D;
use geometry::ray_intersects_rect;
use gpu_cache::GpuCache;
use gpu_types::{ClipScrollNodeIndex, ClipScrollNodeData};
-use render_task::{ClipChain, ClipWorkItem};
use resource_cache::ResourceCache;
use scene::SceneProperties;
use spring::{DAMPING, STIFFNESS, Spring};
use util::{MatrixHelpers, TransformOrOffset, TransformedRectKind};
#[cfg(target_os = "macos")]
const CAN_OVERSCROLL: bool = true;
@@ -51,17 +50,20 @@ impl StickyFrameInfo {
}
#[derive(Debug)]
pub enum NodeType {
/// A reference frame establishes a new coordinate space in the tree.
ReferenceFrame(ReferenceFrameInfo),
/// Other nodes just do clipping, but no transformation.
- Clip(ClipSourcesHandle),
+ Clip {
+ handle: ClipSourcesHandle,
+ clip_chain_index: ClipChainIndex
+ },
/// 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:
@@ -101,19 +103,16 @@ pub struct ClipScrollNode {
pub parent: Option<ClipId>,
/// Child layers
pub children: Vec<ClipId>,
/// The type of this node and any data associated with that node type.
pub node_type: NodeType,
- /// The ClipChain that will be used if this node is used as the 'clipping node.'
- pub clip_chain: Option<ClipChain>,
-
/// True if this node is transformed by an invertible transform. If not, display items
/// transformed by this node will not be displayed and display items not transformed by this
/// node will not be clipped by clips that are transformed by this node.
pub invertible: bool,
/// The axis-aligned coordinate system id of this node.
pub coordinate_system_id: CoordinateSystemId,
@@ -123,31 +122,30 @@ pub struct ClipScrollNode {
pub coordinate_system_relative_transform: TransformOrOffset,
/// A linear ID / index of this clip-scroll node. Used as a reference to
/// pass to shaders, to allow them to fetch a given clip-scroll node.
pub node_data_index: ClipScrollNodeIndex,
}
impl ClipScrollNode {
- fn new(
+ pub fn new(
pipeline_id: PipelineId,
parent_id: Option<ClipId>,
rect: &LayerRect,
node_type: NodeType
) -> Self {
ClipScrollNode {
local_viewport_rect: *rect,
world_viewport_transform: LayerToWorldTransform::identity(),
world_content_transform: LayerToWorldTransform::identity(),
parent: parent_id,
children: Vec::new(),
pipeline_id,
node_type: node_type,
- clip_chain: None,
invertible: true,
coordinate_system_id: CoordinateSystemId(0),
coordinate_system_relative_transform: TransformOrOffset::zero(),
node_data_index: ClipScrollNodeIndex(0),
}
}
pub fn new_scroll_frame(
@@ -165,25 +163,16 @@ impl ClipScrollNode {
(content_size.height - frame_rect.size.height).max(0.0)
),
external_id,
));
Self::new(pipeline_id, Some(parent_id), frame_rect, node_type)
}
- pub fn new_clip_node(
- pipeline_id: PipelineId,
- parent_id: ClipId,
- handle: ClipSourcesHandle,
- clip_rect: LayerRect,
- ) -> Self {
- Self::new(pipeline_id, Some(parent_id), &clip_rect, NodeType::Clip(handle))
- }
-
pub fn new_reference_frame(
parent_id: Option<ClipId>,
frame_rect: &LayerRect,
source_transform: Option<PropertyBinding<LayoutTransform>>,
source_perspective: Option<LayoutTransform>,
origin_in_parent_reference_frame: LayerVector2D,
pipeline_id: PipelineId,
) -> Self {
@@ -266,17 +255,16 @@ impl ClipScrollNode {
scrolling.started_bouncing_back = false;
true
}
pub fn mark_uninvertible(&mut self) {
self.invertible = false;
self.world_content_transform = LayerToWorldTransform::identity();
self.world_viewport_transform = LayerToWorldTransform::identity();
- self.clip_chain = None;
}
pub fn push_gpu_node_data(&mut self, node_data: &mut Vec<ClipScrollNodeData>) {
if !self.invertible {
node_data.push(ClipScrollNodeData::invalid());
return;
}
@@ -299,16 +287,17 @@ impl ClipScrollNode {
&mut self,
state: &mut TransformUpdateState,
next_coordinate_system_id: &mut CoordinateSystemId,
device_pixel_scale: DevicePixelScale,
clip_store: &mut ClipStore,
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
scene_properties: &SceneProperties,
+ clip_chains: &mut Vec<ClipChain>,
) {
// If any of our parents was not rendered, we are not rendered either and can just
// quit here.
if !state.invertible {
self.mark_uninvertible();
return;
}
@@ -326,31 +315,32 @@ impl ClipScrollNode {
}
self.update_clip_work_item(
state,
device_pixel_scale,
clip_store,
resource_cache,
gpu_cache,
+ clip_chains,
);
}
pub fn update_clip_work_item(
&mut self,
state: &mut TransformUpdateState,
device_pixel_scale: DevicePixelScale,
clip_store: &mut ClipStore,
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
+ clip_chains: &mut Vec<ClipChain>,
) {
- let clip_sources_handle = match self.node_type {
- NodeType::Clip(ref handle) => handle,
+ let (clip_sources_handle, clip_chain_index) = match self.node_type {
+ NodeType::Clip { ref handle, clip_chain_index } => (handle, clip_chain_index),
_ => {
- self.clip_chain = Some(state.parent_clip_chain.clone());
self.invertible = true;
return;
}
};
let clip_sources = clip_store.get_mut(clip_sources_handle);
clip_sources.update(gpu_cache, resource_cache);
let (screen_inner_rect, screen_outer_rect) =
@@ -359,39 +349,32 @@ impl ClipScrollNode {
// All clipping ClipScrollNodes should have outer rectangles, because they never
// use the BorderCorner clip type and they always have at last one non-ClipOut
// Rectangle ClipSource.
let screen_outer_rect = screen_outer_rect.expect("Clipping node didn't have outer rect.");
let local_outer_rect = clip_sources.local_outer_rect.expect(
"Clipping node didn't have outer rect."
);
- // If this clip's inner rectangle completely surrounds the existing clip
- // chain's outer rectangle, we can discard this clip entirely since it isn't
- // going to affect anything.
- if screen_inner_rect.contains_rect(&state.parent_clip_chain.combined_outer_screen_rect) {
- self.clip_chain = Some(state.parent_clip_chain.clone());
- return;
- }
-
let work_item = ClipWorkItem {
scroll_node_data_index: self.node_data_index,
clip_sources: clip_sources_handle.weak(),
coordinate_system_id: state.current_coordinate_system_id,
};
- let clip_chain = state.parent_clip_chain.new_with_added_node(
+ let mut clip_chain = clip_chains[state.parent_clip_chain_index.0].new_with_added_node(
work_item,
self.coordinate_system_relative_transform.apply(&local_outer_rect),
screen_outer_rect,
screen_inner_rect,
);
- self.clip_chain = Some(clip_chain.clone());
- state.parent_clip_chain = clip_chain;
+ clip_chain.parent_index = Some(state.parent_clip_chain_index);
+ clip_chains[clip_chain_index.0] = clip_chain;
+ state.parent_clip_chain_index = clip_chain_index;
}
pub fn update_transform(
&mut self,
state: &mut TransformUpdateState,
next_coordinate_system_id: &mut CoordinateSystemId,
scene_properties: &SceneProperties,
) {
@@ -616,17 +599,17 @@ impl ClipScrollNode {
state.parent_accumulated_scroll_offset = LayerVector2D::zero();
state.coordinate_system_relative_transform =
self.coordinate_system_relative_transform.clone();
let translation = -info.origin_in_parent_reference_frame;
state.nearest_scrolling_ancestor_viewport =
state.nearest_scrolling_ancestor_viewport
.translate(&translation);
}
- NodeType::Clip(..) => { }
+ 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 = self.local_viewport_rect;
}
NodeType::StickyFrame(ref info) => {
// We don't translate the combined rect by the sticky offset, because sticky
@@ -763,22 +746,17 @@ impl ClipScrollNode {
pub fn is_overscrolling(&self) -> bool {
match self.node_type {
NodeType::ScrollFrame(ref state) => state.overscroll_amount() != LayerVector2D::zero(),
_ => false,
}
}
- pub fn matches_id(&self, node_id: ClipId, id_to_match: ScrollNodeIdType) -> bool {
- let external_id = match id_to_match {
- ScrollNodeIdType::ExternalScrollId(id) => id,
- ScrollNodeIdType::ClipId(clip_id) => return node_id == clip_id,
- };
-
+ 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,
_ => false,
}
}
}
#[derive(Copy, Clone, Debug)]
--- a/gfx/webrender/src/clip_scroll_tree.rs
+++ b/gfx/webrender/src/clip_scroll_tree.rs
@@ -1,23 +1,21 @@
/* 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/. */
-use api::{ClipChainId, ClipId, DeviceIntRect, DevicePixelScale, ExternalScrollId, LayerPoint};
-use api::{LayerRect, LayerToWorldTransform, LayerVector2D, PipelineId, ScrollClamping};
-use api::{ScrollEventPhase, ScrollLocation, ScrollNodeIdType};
-use api::{ScrollNodeState, WorldPoint};
-use clip::ClipStore;
+use api::{ClipId, DeviceIntRect, DevicePixelScale, ExternalScrollId, LayerPoint, LayerRect};
+use api::{LayerToWorldTransform, LayerVector2D, PipelineId, ScrollClamping, ScrollEventPhase};
+use api::{ScrollLocation, ScrollNodeState, WorldPoint};
+use clip::{ClipChain, ClipSourcesHandle, ClipStore};
use clip_scroll_node::{ClipScrollNode, NodeType, ScrollFrameInfo, StickyFrameInfo};
use gpu_cache::GpuCache;
use gpu_types::{ClipScrollNodeIndex, ClipScrollNodeData};
use internal_types::{FastHashMap, FastHashSet};
use print_tree::{PrintTree, PrintTreePrinter};
-use render_task::ClipChain;
use resource_cache::ResourceCache;
use scene::SceneProperties;
use util::TransformOrOffset;
pub type ScrollStates = FastHashMap<ExternalScrollId, ScrollFrameInfo>;
/// An id that identifies coordinate systems in the ClipScrollTree. Each
/// coordinate system has an id and those ids will be shared when the coordinates
@@ -39,33 +37,37 @@ impl CoordinateSystemId {
}
pub fn advance(&mut self) {
self.0 += 1;
}
}
pub struct ClipChainDescriptor {
- pub id: ClipChainId,
- pub parent: Option<ClipChainId>,
+ pub index: ClipChainIndex,
+ pub parent: Option<ClipChainIndex>,
pub clips: Vec<ClipId>,
}
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub struct ClipChainIndex(pub usize);
+
pub struct ClipScrollTree {
pub nodes: FastHashMap<ClipId, ClipScrollNode>,
/// A Vec of all descriptors that describe ClipChains in the order in which they are
/// encountered during display list flattening. ClipChains are expected to never be
/// the children of ClipChains later in the list.
pub clip_chains_descriptors: Vec<ClipChainDescriptor>,
- /// A HashMap of built ClipChains that are described by `clip_chains_descriptors`.
- pub clip_chains: FastHashMap<ClipChainId, ClipChain>,
+ /// A vector of all ClipChains in this ClipScrollTree including those from
+ /// ClipChainDescriptors and also those defined by the clipping node hierarchy.
+ pub clip_chains: Vec<ClipChain>,
- pub pending_scroll_offsets: FastHashMap<ScrollNodeIdType, (LayerPoint, ScrollClamping)>,
+ pub pending_scroll_offsets: FastHashMap<ExternalScrollId, (LayerPoint, ScrollClamping)>,
/// The ClipId of the currently scrolling node. Used to allow the same
/// node to scroll even if a touch operation leaves the boundaries of that node.
pub currently_scrolling_node_id: Option<ClipId>,
/// 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.
@@ -85,17 +87,19 @@ pub struct ClipScrollTree {
}
#[derive(Clone)]
pub struct TransformUpdateState {
pub parent_reference_frame_transform: LayerToWorldTransform,
pub parent_accumulated_scroll_offset: LayerVector2D,
pub nearest_scrolling_ancestor_offset: LayerVector2D,
pub nearest_scrolling_ancestor_viewport: LayerRect,
- pub parent_clip_chain: ClipChain,
+
+ /// The index of the current parent's clip chain.
+ pub parent_clip_chain_index: ClipChainIndex,
/// An id for keeping track of the axis-aligned space of this node. This is used in
/// order to to track what kinds of clip optimizations can be done for a particular
/// display list item, since optimizations can usually only be done among
/// coordinate systems which are relatively axis aligned.
pub current_coordinate_system_id: CoordinateSystemId,
/// Transform from the coordinate system that started this compatible coordinate system.
@@ -108,17 +112,17 @@ pub struct TransformUpdateState {
}
impl ClipScrollTree {
pub fn new() -> Self {
let dummy_pipeline = PipelineId::dummy();
ClipScrollTree {
nodes: FastHashMap::default(),
clip_chains_descriptors: Vec::new(),
- clip_chains: FastHashMap::default(),
+ clip_chains: vec![ClipChain::empty(&DeviceIntRect::zero())],
pending_scroll_offsets: FastHashMap::default(),
currently_scrolling_node_id: None,
root_reference_frame_id: ClipId::root_reference_frame(dummy_pipeline),
topmost_scrolling_node_id: ClipId::root_scroll_node(dummy_pipeline),
current_new_node_item: 1,
pipelines_to_discard: FastHashSet::default(),
}
}
@@ -206,29 +210,29 @@ impl ClipScrollTree {
NodeType::ScrollFrame(info) if info.external_id.is_some() => {
scroll_states.insert(info.external_id.unwrap(), info);
}
_ => {}
}
}
self.pipelines_to_discard.clear();
- self.clip_chains.clear();
+ self.clip_chains = vec![ClipChain::empty(&DeviceIntRect::zero())];
self.clip_chains_descriptors.clear();
scroll_states
}
pub fn scroll_node(
&mut self,
origin: LayerPoint,
- id: ScrollNodeIdType,
+ id: ExternalScrollId,
clamp: ScrollClamping
) -> bool {
- for (clip_id, node) in &mut self.nodes {
- if node.matches_id(*clip_id, id) {
+ for node in &mut self.nodes.values_mut() {
+ if node.matches_external_id(id) {
return node.set_scroll_origin(&origin, clamp);
}
}
self.pending_scroll_offsets.insert(id, (origin, clamp));
false
}
@@ -316,27 +320,29 @@ impl ClipScrollTree {
pan: WorldPoint,
node_data: &mut Vec<ClipScrollNodeData>,
scene_properties: &SceneProperties,
) {
if self.nodes.is_empty() {
return;
}
+ self.clip_chains[0] = ClipChain::empty(screen_rect);
+
let root_reference_frame_id = self.root_reference_frame_id();
let mut state = TransformUpdateState {
parent_reference_frame_transform: LayerToWorldTransform::create_translation(
pan.x,
pan.y,
0.0,
),
parent_accumulated_scroll_offset: LayerVector2D::zero(),
nearest_scrolling_ancestor_offset: LayerVector2D::zero(),
nearest_scrolling_ancestor_viewport: LayerRect::zero(),
- parent_clip_chain: ClipChain::empty(screen_rect),
+ parent_clip_chain_index: ClipChainIndex(0),
current_coordinate_system_id: CoordinateSystemId::root(),
coordinate_system_relative_transform: TransformOrOffset::zero(),
invertible: true,
};
let mut next_coordinate_system_id = state.current_coordinate_system_id.next();
self.update_node(
root_reference_frame_id,
&mut state,
@@ -379,16 +385,17 @@ impl ClipScrollTree {
node.update(
&mut state,
next_coordinate_system_id,
device_pixel_scale,
clip_store,
resource_cache,
gpu_cache,
scene_properties,
+ &mut self.clip_chains,
);
node.push_gpu_node_data(gpu_node_data);
if node.children.is_empty() {
return;
}
@@ -412,65 +419,79 @@ impl ClipScrollTree {
}
pub fn build_clip_chains(&mut self, screen_rect: &DeviceIntRect) {
for descriptor in &self.clip_chains_descriptors {
// A ClipChain is an optional parent (which is another ClipChain) and a list of
// ClipScrollNode clipping nodes. Here we start the ClipChain with a clone of the
// parent's node, if necessary.
let mut chain = match descriptor.parent {
- Some(id) => self.clip_chains[&id].clone(),
+ Some(index) => self.clip_chains[index.0].clone(),
None => ClipChain::empty(screen_rect),
};
// Now we walk through each ClipScrollNode in the vector of clip nodes and
// extract their ClipChain nodes to construct the final list.
for clip_id in &descriptor.clips {
- if let Some(ref node_chain) = self.nodes[&clip_id].clip_chain {
- if let Some(ref nodes) = node_chain.nodes {
- chain.add_node((**nodes).clone());
+ let node_clip_chain_index = match self.nodes[&clip_id].node_type {
+ NodeType::Clip { clip_chain_index, .. } => clip_chain_index,
+ _ => {
+ warn!("Tried to create a clip chain with non-clipping node.");
+ continue;
}
+ };
+
+ if let Some(ref nodes) = self.clip_chains[node_clip_chain_index.0].nodes {
+ chain.add_node((**nodes).clone());
}
}
- self.clip_chains.insert(descriptor.id, chain);
+ chain.parent_index = descriptor.parent;
+ self.clip_chains[descriptor.index.0] = chain;
}
}
pub fn tick_scrolling_bounce_animations(&mut self) {
for (_, node) in &mut self.nodes {
node.tick_scrolling_bounce_animation()
}
}
pub fn finalize_and_apply_pending_scroll_offsets(&mut self, old_states: ScrollStates) {
- for (clip_id, node) in &mut self.nodes {
+ for node in self.nodes.values_mut() {
let external_id = match node.node_type {
- NodeType::ScrollFrame(info) => info.external_id,
- _ => None,
+ NodeType::ScrollFrame(ScrollFrameInfo { external_id: Some(id), ..} ) => id,
+ _ => continue,
};
- if let Some(external_id) = external_id {
- if let Some(scrolling_state) = old_states.get(&external_id) {
- node.apply_old_scrolling_state(scrolling_state);
- }
+ if let Some(scrolling_state) = old_states.get(&external_id) {
+ node.apply_old_scrolling_state(scrolling_state);
+ }
- let id = external_id.into();
- if let Some((offset, clamping)) = self.pending_scroll_offsets.remove(&id) {
- node.set_scroll_origin(&offset, clamping);
- }
- }
-
- if let Some((offset, clamping)) = self.pending_scroll_offsets.remove(&clip_id.into()) {
+ if let Some((offset, clamping)) = self.pending_scroll_offsets.remove(&external_id) {
node.set_scroll_origin(&offset, clamping);
}
}
}
+ pub fn add_clip_node(
+ &mut self,
+ id: ClipId,
+ parent_id: ClipId,
+ handle: ClipSourcesHandle,
+ clip_rect: LayerRect,
+ ) -> ClipChainIndex {
+ let clip_chain_index = self.allocate_clip_chain();
+ let node_type = NodeType::Clip { handle, clip_chain_index };
+ let node = ClipScrollNode::new(id.pipeline_id(), Some(parent_id), &clip_rect, node_type);
+ self.add_node(node, id);
+ clip_chain_index
+ }
+
pub fn add_sticky_frame(
&mut self,
id: ClipId,
parent_id: ClipId,
frame_rect: LayerRect,
sticky_frame_info: StickyFrameInfo,
) {
let node = ClipScrollNode::new_sticky_frame(
@@ -479,21 +500,22 @@ impl ClipScrollTree {
sticky_frame_info,
id.pipeline_id(),
);
self.add_node(node, id);
}
pub fn add_clip_chain_descriptor(
&mut self,
- id: ClipChainId,
- parent: Option<ClipChainId>,
+ parent: Option<ClipChainIndex>,
clips: Vec<ClipId>
- ) {
- self.clip_chains_descriptors.push(ClipChainDescriptor { id, parent, clips });
+ ) -> ClipChainIndex {
+ let index = self.allocate_clip_chain();
+ self.clip_chains_descriptors.push(ClipChainDescriptor { index, parent, clips });
+ index
}
pub fn add_node(&mut self, node: ClipScrollNode, id: ClipId) {
// When the parent node is None this means we are adding the root.
match node.parent {
Some(parent_id) => self.nodes.get_mut(&parent_id).unwrap().add_child(id),
None => self.root_reference_frame_id = id,
}
@@ -510,21 +532,21 @@ impl ClipScrollTree {
_ => {}
}
}
fn print_node<T: PrintTreePrinter>(&self, id: &ClipId, pt: &mut T, clip_store: &ClipStore) {
let node = self.nodes.get(id).unwrap();
match node.node_type {
- NodeType::Clip(ref clip_sources_handle) => {
+ NodeType::Clip { ref handle, .. } => {
pt.new_level("Clip".to_owned());
pt.add_item(format!("id: {:?}", id));
- let clips = clip_store.get(&clip_sources_handle).clips();
+ let clips = clip_store.get(&handle).clips();
pt.new_level(format!("Clip Sources [{}]", clips.len()));
for source in clips {
pt.add_item(format!("{:?}", source));
}
pt.end_level();
}
NodeType::ReferenceFrame(ref info) => {
pt.new_level(format!("ReferenceFrame {:?}", info.resolved_transform));
@@ -576,16 +598,20 @@ impl ClipScrollTree {
}
pub fn print_with<T: PrintTreePrinter>(&self, clip_store: &ClipStore, pt: &mut T) {
if !self.nodes.is_empty() {
self.print_node(&self.root_reference_frame_id, pt, clip_store);
}
}
- pub fn get_clip_chain(&self, id: &ClipId) -> Option<&ClipChain> {
- match id {
- &ClipId::ClipChain(clip_chain_id) => Some(&self.clip_chains[&clip_chain_id]),
- _ => self.nodes[id].clip_chain.as_ref(),
- }
+ pub fn allocate_clip_chain(&mut self) -> ClipChainIndex {
+ debug_assert!(!self.clip_chains.is_empty());
+ let new_clip_chain =self.clip_chains[0].clone();
+ self.clip_chains.push(new_clip_chain);
+ ClipChainIndex(self.clip_chains.len() - 1)
+ }
+
+ pub fn get_clip_chain(&self, index: ClipChainIndex) -> &ClipChain {
+ &self.clip_chains[index.0]
}
}
--- a/gfx/webrender/src/device.rs
+++ b/gfx/webrender/src/device.rs
@@ -433,16 +433,17 @@ pub struct Texture {
layer_count: i32,
format: ImageFormat,
width: u32,
height: u32,
filter: TextureFilter,
render_target: Option<RenderTargetInfo>,
fbo_ids: Vec<FBOId>,
depth_rb: Option<RBOId>,
+ last_frame_used: FrameId,
}
impl Texture {
pub fn get_dimensions(&self) -> DeviceUintSize {
DeviceUintSize::new(self.width, self.height)
}
pub fn get_render_target_layer_count(&self) -> usize {
@@ -468,16 +469,20 @@ impl Texture {
pub fn has_depth(&self) -> bool {
self.depth_rb.is_some()
}
pub fn get_rt_info(&self) -> Option<&RenderTargetInfo> {
self.render_target.as_ref()
}
+ pub fn used_in_frame(&self, frame_id: FrameId) -> bool {
+ self.last_frame_used == frame_id
+ }
+
#[cfg(feature = "replay")]
pub fn into_external(mut self) -> ExternalTexture {
let ext = ExternalTexture {
id: self.id,
target: self.target,
};
self.id = 0; // don't complain, moved out
ext
@@ -935,16 +940,17 @@ impl Device {
width: 0,
height: 0,
layer_count: 0,
format,
filter: TextureFilter::Nearest,
render_target: None,
fbo_ids: vec![],
depth_rb: None,
+ last_frame_used: self.frame_id,
}
}
fn set_texture_parameters(&mut self, target: gl::GLuint, filter: TextureFilter) {
let mag_filter = match filter {
TextureFilter::Nearest => gl::NEAREST,
TextureFilter::Linear | TextureFilter::Trilinear => gl::LINEAR,
};
@@ -1014,16 +1020,17 @@ impl Device {
let is_resized = texture.width != width || texture.height != height;
texture.width = width;
texture.height = height;
texture.filter = filter;
texture.layer_count = layer_count;
texture.render_target = render_target;
+ texture.last_frame_used = self.frame_id;
self.bind_texture(DEFAULT_TEXTURE, texture);
self.set_texture_parameters(texture.target, filter);
match render_target {
Some(info) => {
self.update_target_storage(texture, &info, is_resized, pixels);
}
--- a/gfx/webrender/src/frame.rs
+++ b/gfx/webrender/src/frame.rs
@@ -1,28 +1,29 @@
/* 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/. */
-use api::{BuiltDisplayListIter, ClipAndScrollInfo, ClipId, ColorF, ComplexClipRegion};
-use api::{DevicePixelScale, DeviceUintRect, DeviceUintSize, DisplayItemRef, DocumentLayer, Epoch};
-use api::{ExternalScrollId, FilterOp, IframeDisplayItem, ImageDisplayItem, ItemRange, LayerPoint};
+use api::{BuiltDisplayListIter, ClipId, ColorF, ComplexClipRegion, DevicePixelScale};
+use api::{DeviceUintRect, DeviceUintSize, DisplayItemRef, DocumentLayer, Epoch, ExternalScrollId};
+use api::{FilterOp, IframeDisplayItem, ImageDisplayItem, ItemRange, LayerPoint};
use api::{LayerPrimitiveInfo, LayerRect, LayerSize, LayerVector2D, LayoutSize, PipelineId};
use api::{ScrollClamping, ScrollEventPhase, ScrollFrameDisplayItem, ScrollLocation};
-use api::{ScrollNodeIdType, ScrollNodeState, ScrollPolicy, ScrollSensitivity, SpecificDisplayItem};
-use api::{StackingContext, TileOffset, TransformStyle, WorldPoint};
+use api::{ScrollNodeState, ScrollPolicy, ScrollSensitivity, SpecificDisplayItem, StackingContext};
+use api::{TileOffset, TransformStyle, WorldPoint};
use clip::ClipRegion;
use clip_scroll_node::StickyFrameInfo;
-use clip_scroll_tree::{ClipScrollTree, ScrollStates};
+use clip_scroll_tree::{ClipChainIndex, ClipScrollTree, ScrollStates};
use euclid::rect;
use frame_builder::{FrameBuilder, FrameBuilderConfig, ScrollbarInfo};
use gpu_cache::GpuCache;
use hit_test::HitTester;
use internal_types::{FastHashMap, FastHashSet, RenderedDocument};
+use prim_store::ScrollNodeAndClipChain;
use profiler::{GpuCacheProfileCounters, TextureCacheProfileCounters};
use resource_cache::{FontInstanceMap,ResourceCache, TiledImageMap};
use scene::{Scene, StackingContextHelpers, ScenePipeline, SceneProperties};
use tiling::{CompositeOps, Frame};
use renderer::PipelineInfo;
#[derive(Copy, Clone, PartialEq, PartialOrd, Debug, Eq, Ord)]
#[cfg_attr(feature = "capture", derive(Serialize))]
@@ -31,25 +32,73 @@ pub struct FrameId(pub u32);
static DEFAULT_SCROLLBAR_COLOR: ColorF = ColorF {
r: 0.3,
g: 0.3,
b: 0.3,
a: 0.6,
};
+/// A data structure that keeps track of mapping between API clip ids and the indices
+/// used internally in the ClipScrollTree to avoid having to do HashMap lookups. This
+/// also includes a small LRU cache. Currently the cache is small (1 entry), but in the
+/// future we could use uluru here to do something more involved.
+pub struct ClipIdToIndexMapper {
+ map: FastHashMap<ClipId, ClipChainIndex>,
+ cached_index: Option<(ClipId, ClipChainIndex)>,
+}
+
+impl ClipIdToIndexMapper {
+ fn new() -> ClipIdToIndexMapper {
+ ClipIdToIndexMapper {
+ map: FastHashMap::default(),
+ cached_index: None,
+ }
+ }
+
+ pub fn add(&mut self, id: ClipId, index: ClipChainIndex) {
+ debug_assert!(!self.map.contains_key(&id));
+ self.map.insert(id, index);
+ }
+
+ pub fn map_to_parent_clip_chain(&mut self, id: ClipId, parent_id: &ClipId) {
+ let parent_chain_index = self.map_clip_id(parent_id);
+ self.add(id, parent_chain_index);
+ }
+
+ pub fn map_clip_id(&mut self, id: &ClipId) -> ClipChainIndex {
+ match self.cached_index {
+ Some((cached_id, cached_index)) if cached_id == *id => return cached_index,
+ _ => {}
+ }
+
+ self.map[id]
+ }
+
+ pub fn map_clip_id_and_cache_result(&mut self, id: &ClipId) -> ClipChainIndex {
+ let index = self.map_clip_id(id);
+ self.cached_index = Some((*id, index));
+ index
+ }
+
+ pub fn simple_scroll_and_clip_chain(&mut self, id: &ClipId) -> ScrollNodeAndClipChain {
+ ScrollNodeAndClipChain::new(*id, self.map_clip_id(&id))
+ }
+}
+
struct FlattenContext<'a> {
scene: &'a Scene,
builder: FrameBuilder,
clip_scroll_tree: &'a mut ClipScrollTree,
font_instances: FontInstanceMap,
tiled_image_map: TiledImageMap,
pipeline_epochs: Vec<(PipelineId, Epoch)>,
replacements: Vec<(ClipId, ClipId)>,
output_pipelines: &'a FastHashSet<PipelineId>,
+ id_to_index_mapper: ClipIdToIndexMapper,
}
impl<'a> FlattenContext<'a> {
/// Since WebRender still handles fixed position and reference frame content internally
/// we need to apply this table of id replacements only to the id that affects the
/// position of a node. We can eventually remove this when clients start handling
/// reference frames themselves. This method applies these replacements.
fn apply_scroll_frame_id_replacement(&self, id: ClipId) -> ClipId {
@@ -99,35 +148,45 @@ impl<'a> FlattenContext<'a> {
&mut self,
traversal: &mut BuiltDisplayListIter<'a>,
pipeline_id: PipelineId,
frame_size: &LayoutSize,
) {
let root_reference_frame_id = ClipId::root_reference_frame(pipeline_id);
let root_scroll_frame_id = ClipId::root_scroll_node(pipeline_id);
+ let root_clip_chain_index =
+ self.id_to_index_mapper.map_clip_id_and_cache_result(&root_reference_frame_id);
+ let root_reference_frame_clip_and_scroll = ScrollNodeAndClipChain::new(
+ root_reference_frame_id,
+ root_clip_chain_index,
+ );
+
self.builder.push_stacking_context(
pipeline_id,
CompositeOps::default(),
TransformStyle::Flat,
true,
true,
- ClipAndScrollInfo::simple(root_scroll_frame_id),
+ ScrollNodeAndClipChain::new(
+ ClipId::root_scroll_node(pipeline_id),
+ root_clip_chain_index,
+ ),
self.output_pipelines,
);
// For the root pipeline, there's no need to add a full screen rectangle
// here, as it's handled by the framebuffer clear.
if self.scene.root_pipeline_id != Some(pipeline_id) {
if let Some(pipeline) = self.scene.pipelines.get(&pipeline_id) {
if let Some(bg_color) = pipeline.background_color {
let root_bounds = LayerRect::new(LayerPoint::zero(), *frame_size);
let info = LayerPrimitiveInfo::new(root_bounds);
self.builder.add_solid_rectangle(
- ClipAndScrollInfo::simple(root_reference_frame_id),
+ root_reference_frame_clip_and_scroll,
&info,
bg_color,
None,
);
}
}
}
@@ -137,17 +196,17 @@ impl<'a> FlattenContext<'a> {
pipeline_id,
LayerVector2D::zero(),
);
if self.builder.config.enable_scrollbars {
let scrollbar_rect = LayerRect::new(LayerPoint::zero(), LayerSize::new(10.0, 70.0));
let container_rect = LayerRect::new(LayerPoint::zero(), *frame_size);
self.builder.add_scroll_bar(
- ClipAndScrollInfo::simple(root_reference_frame_id),
+ root_reference_frame_clip_and_scroll,
&LayerPrimitiveInfo::new(scrollbar_rect),
DEFAULT_SCROLLBAR_COLOR,
ScrollbarInfo(root_scroll_frame_id, container_rect),
);
}
self.builder.pop_stacking_context();
}
@@ -179,38 +238,32 @@ impl<'a> FlattenContext<'a> {
// If flatten_item created a sub-traversal, we need `traversal` to have the
// same state as the completed subtraversal, so we reinitialize it here.
if let Some(subtraversal) = subtraversal {
*traversal = subtraversal;
}
}
}
- fn flatten_clip(
- &mut self,
- pipeline_id: PipelineId,
- parent_id: &ClipId,
- new_clip_id: &ClipId,
- clip_region: ClipRegion,
- ) {
+ fn flatten_clip(&mut self, parent_id: &ClipId, new_clip_id: &ClipId, clip_region: ClipRegion) {
self.builder.add_clip_node(
*new_clip_id,
*parent_id,
- pipeline_id,
clip_region,
self.clip_scroll_tree,
+ &mut self.id_to_index_mapper,
);
}
fn flatten_scroll_frame(
&mut self,
item: &DisplayItemRef,
info: &ScrollFrameDisplayItem,
pipeline_id: PipelineId,
- clip_and_scroll: &ClipAndScrollInfo,
+ clip_and_scroll: &ScrollNodeAndClipChain,
reference_frame_relative_offset: &LayerVector2D,
) {
let complex_clips = self.get_complex_clips(pipeline_id, item.complex_clip().0);
let clip_region = ClipRegion::create_for_clip_node(
*item.local_clip().clip_rect(),
complex_clips,
info.image_mask,
&reference_frame_relative_offset,
@@ -224,38 +277,40 @@ impl<'a> FlattenContext<'a> {
.translate(&reference_frame_relative_offset);
let content_rect = item.rect().translate(&reference_frame_relative_offset);
debug_assert!(info.clip_id != info.scroll_frame_id);
self.builder.add_clip_node(
info.clip_id,
clip_and_scroll.scroll_node_id,
- pipeline_id,
clip_region,
self.clip_scroll_tree,
+ &mut self.id_to_index_mapper,
);
self.builder.add_scroll_frame(
info.scroll_frame_id,
info.clip_id,
info.external_id,
pipeline_id,
&frame_rect,
&content_rect.size,
info.scroll_sensitivity,
self.clip_scroll_tree,
+ &mut self.id_to_index_mapper,
);
}
fn flatten_stacking_context(
&mut self,
traversal: &mut BuiltDisplayListIter<'a>,
pipeline_id: PipelineId,
- context_scroll_node_id: ClipId,
+ unreplaced_scroll_id: ClipId,
+ clip_and_scroll: ScrollNodeAndClipChain,
mut reference_frame_relative_offset: LayerVector2D,
bounds: &LayerRect,
stacking_context: &StackingContext,
filters: ItemRange<FilterOp>,
is_backface_visible: bool,
) {
// Avoid doing unnecessary work for empty stacking contexts.
if traversal.current_stacking_context_empty() {
@@ -274,57 +329,60 @@ impl<'a> FlattenContext<'a> {
CompositeOps::new(
stacking_context.filter_ops_for_compositing(display_list, filters),
stacking_context.mix_blend_mode_for_compositing(),
)
};
if stacking_context.scroll_policy == ScrollPolicy::Fixed {
self.replacements.push((
- context_scroll_node_id,
+ unreplaced_scroll_id,
self.builder.current_reference_frame_id(),
));
}
reference_frame_relative_offset += bounds.origin.to_vector();
// If we have a transformation or a perspective, we should have been assigned a new
// reference frame id. This means this stacking context establishes a new reference frame.
// Descendant fixed position content will be positioned relative to us.
if let Some(reference_frame_id) = stacking_context.reference_frame_id {
debug_assert!(
stacking_context.transform.is_some() ||
stacking_context.perspective.is_some()
);
let reference_frame_bounds = LayerRect::new(LayerPoint::zero(), bounds.size);
- let mut parent_id = self.apply_scroll_frame_id_replacement(context_scroll_node_id);
self.builder.push_reference_frame(
reference_frame_id,
- Some(parent_id),
+ Some(clip_and_scroll.scroll_node_id),
pipeline_id,
&reference_frame_bounds,
stacking_context.transform,
stacking_context.perspective,
reference_frame_relative_offset,
self.clip_scroll_tree,
+ &mut self.id_to_index_mapper,
);
- self.replacements.push((context_scroll_node_id, reference_frame_id));
+ self.replacements.push((unreplaced_scroll_id, reference_frame_id));
reference_frame_relative_offset = LayerVector2D::zero();
}
- let sc_scroll_node_id = self.apply_scroll_frame_id_replacement(context_scroll_node_id);
-
+ // We apply the replacements one more time in case we need to set it to a replacement
+ // that we just pushed above.
+ let new_scroll_node = self.apply_scroll_frame_id_replacement(unreplaced_scroll_id);
+ let stacking_context_clip_and_scroll =
+ self.id_to_index_mapper.simple_scroll_and_clip_chain(&new_scroll_node);
self.builder.push_stacking_context(
pipeline_id,
composition_operations,
stacking_context.transform_style,
is_backface_visible,
false,
- ClipAndScrollInfo::simple(sc_scroll_node_id),
+ stacking_context_clip_and_scroll,
self.output_pipelines,
);
self.flatten_items(
traversal,
pipeline_id,
reference_frame_relative_offset,
);
@@ -340,76 +398,81 @@ impl<'a> FlattenContext<'a> {
self.builder.pop_stacking_context();
}
fn flatten_iframe(
&mut self,
item: &DisplayItemRef,
info: &IframeDisplayItem,
- parent_pipeline_id: PipelineId,
- clip_and_scroll: &ClipAndScrollInfo,
+ clip_and_scroll: &ScrollNodeAndClipChain,
reference_frame_relative_offset: &LayerVector2D,
) {
let iframe_pipeline_id = info.pipeline_id;
let pipeline = match self.scene.pipelines.get(&iframe_pipeline_id) {
Some(pipeline) => pipeline,
None => return,
};
self.builder.add_clip_node(
info.clip_id,
clip_and_scroll.scroll_node_id,
- parent_pipeline_id,
ClipRegion::create_for_clip_node_with_local_clip(
&item.local_clip(),
&reference_frame_relative_offset
),
self.clip_scroll_tree,
+ &mut self.id_to_index_mapper,
);
self.pipeline_epochs.push((iframe_pipeline_id, pipeline.epoch));
let bounds = item.rect();
let iframe_rect = LayerRect::new(LayerPoint::zero(), bounds.size);
let origin = *reference_frame_relative_offset + bounds.origin.to_vector();
self.builder.push_reference_frame(
ClipId::root_reference_frame(iframe_pipeline_id),
Some(info.clip_id),
iframe_pipeline_id,
&iframe_rect,
None,
None,
origin,
self.clip_scroll_tree,
+ &mut self.id_to_index_mapper,
);
self.builder.add_scroll_frame(
ClipId::root_scroll_node(iframe_pipeline_id),
ClipId::root_reference_frame(iframe_pipeline_id),
Some(ExternalScrollId(0, iframe_pipeline_id)),
iframe_pipeline_id,
&iframe_rect,
&pipeline.content_size,
ScrollSensitivity::ScriptAndInputEvents,
self.clip_scroll_tree,
+ &mut self.id_to_index_mapper,
);
self.flatten_root(&mut pipeline.display_list.iter(), iframe_pipeline_id, &iframe_rect.size);
self.builder.pop_reference_frame();
}
fn flatten_item<'b>(
&'b mut self,
item: DisplayItemRef<'a, 'b>,
pipeline_id: PipelineId,
reference_frame_relative_offset: LayerVector2D,
) -> Option<BuiltDisplayListIter<'a>> {
- let mut clip_and_scroll = item.clip_and_scroll();
+ let clip_and_scroll = item.clip_and_scroll();
+ let mut clip_and_scroll = ScrollNodeAndClipChain::new(
+ clip_and_scroll.scroll_node_id,
+ self.id_to_index_mapper.map_clip_id_and_cache_result(&clip_and_scroll.clip_node_id()),
+ );
let unreplaced_scroll_id = clip_and_scroll.scroll_node_id;
clip_and_scroll.scroll_node_id =
self.apply_scroll_frame_id_replacement(clip_and_scroll.scroll_node_id);
let prim_info = item.get_layer_primitive_info(&reference_frame_relative_offset);
match *item.item() {
SpecificDisplayItem::Image(ref info) => {
@@ -552,51 +615,51 @@ impl<'a> FlattenContext<'a> {
);
}
SpecificDisplayItem::PushStackingContext(ref info) => {
let mut subtraversal = item.sub_iter();
self.flatten_stacking_context(
&mut subtraversal,
pipeline_id,
unreplaced_scroll_id,
+ clip_and_scroll,
reference_frame_relative_offset,
&item.rect(),
&info.stacking_context,
item.filters(),
prim_info.is_backface_visible,
);
return Some(subtraversal);
}
SpecificDisplayItem::Iframe(ref info) => {
self.flatten_iframe(
&item,
info,
- pipeline_id,
&clip_and_scroll,
&reference_frame_relative_offset
);
}
SpecificDisplayItem::Clip(ref info) => {
let complex_clips = self.get_complex_clips(pipeline_id, item.complex_clip().0);
let clip_region = ClipRegion::create_for_clip_node(
*item.local_clip().clip_rect(),
complex_clips,
info.image_mask,
&reference_frame_relative_offset,
);
- self.flatten_clip(
- pipeline_id,
- &clip_and_scroll.scroll_node_id,
- &info.id,
- clip_region,
- );
+ self.flatten_clip(&clip_and_scroll.scroll_node_id, &info.id, clip_region);
}
SpecificDisplayItem::ClipChain(ref info) => {
let items = self.get_clip_chain_items(pipeline_id, item.clip_chain_items());
- self.clip_scroll_tree.add_clip_chain_descriptor(info.id, info.parent, items);
+ let parent = info.parent.map(|id|
+ self.id_to_index_mapper.map_clip_id(&ClipId::ClipChain(id))
+ );
+ let clip_chain_index =
+ self.clip_scroll_tree.add_clip_chain_descriptor(parent, items);
+ self.id_to_index_mapper.add(ClipId::ClipChain(info.id), clip_chain_index);
},
SpecificDisplayItem::ScrollFrame(ref info) => {
self.flatten_scroll_frame(
&item,
info,
pipeline_id,
&clip_and_scroll,
&reference_frame_relative_offset
@@ -605,22 +668,24 @@ impl<'a> FlattenContext<'a> {
SpecificDisplayItem::StickyFrame(ref info) => {
let frame_rect = item.rect().translate(&reference_frame_relative_offset);
let sticky_frame_info = StickyFrameInfo::new(
info.margins,
info.vertical_offset_bounds,
info.horizontal_offset_bounds,
info.previously_applied_offset,
);
+ let parent_id = clip_and_scroll.scroll_node_id;
self.clip_scroll_tree.add_sticky_frame(
info.id,
- clip_and_scroll.scroll_node_id, /* parent id */
+ parent_id,
frame_rect,
sticky_frame_info
);
+ self.id_to_index_mapper.map_to_parent_clip_chain(info.id, &parent_id);
}
// Do nothing; these are dummy items for the display list parser
SpecificDisplayItem::SetGradientStops => {}
SpecificDisplayItem::PopStackingContext => {
unreachable!("Should have returned in parent method.")
}
@@ -645,17 +710,17 @@ impl<'a> FlattenContext<'a> {
/// decomposition. This lets us generate the minimum amount of primitives by, for example,
/// decompositing the repetition horizontally while repeating vertically in the shader (for
/// an image where the width is too bug but the height is not).
///
/// decompose_image and decompose_image_row handle image repetitions while decompose_tiled_image
/// takes care of the decomposition required by the internal tiling of the image.
fn decompose_image(
&mut self,
- clip_and_scroll: ClipAndScrollInfo,
+ clip_and_scroll: ScrollNodeAndClipChain,
prim_info: &LayerPrimitiveInfo,
info: &ImageDisplayItem,
image_size: DeviceUintSize,
tile_size: u32,
) {
let no_vertical_tiling = image_size.height <= tile_size;
let no_vertical_spacing = info.tile_spacing.height == 0.0;
let item_rect = prim_info.rect;
@@ -691,17 +756,17 @@ impl<'a> FlattenContext<'a> {
tile_size,
);
}
}
}
fn decompose_image_row(
&mut self,
- clip_and_scroll: ClipAndScrollInfo,
+ clip_and_scroll: ScrollNodeAndClipChain,
prim_info: &LayerPrimitiveInfo,
info: &ImageDisplayItem,
image_size: DeviceUintSize,
tile_size: u32,
) {
let no_horizontal_tiling = image_size.width <= tile_size;
let no_horizontal_spacing = info.tile_spacing.width == 0.0;
if no_horizontal_tiling && no_horizontal_spacing {
@@ -737,17 +802,17 @@ impl<'a> FlattenContext<'a> {
tile_size,
);
}
}
}
fn decompose_tiled_image(
&mut self,
- clip_and_scroll: ClipAndScrollInfo,
+ clip_and_scroll: ScrollNodeAndClipChain,
prim_info: &LayerPrimitiveInfo,
info: &ImageDisplayItem,
image_size: DeviceUintSize,
tile_size: u32,
) {
// The image resource is tiled. We have to generate an image primitive
// for each tile.
// We need to do this because the image is broken up into smaller tiles in the texture
@@ -873,17 +938,17 @@ impl<'a> FlattenContext<'a> {
shader_repeat_y,
);
}
}
}
fn add_tile_primitive(
&mut self,
- clip_and_scroll: ClipAndScrollInfo,
+ clip_and_scroll: ScrollNodeAndClipChain,
prim_info: &LayerPrimitiveInfo,
info: &ImageDisplayItem,
tile_offset: TileOffset,
stretched_tile_size: LayerSize,
tile_ratio_width: f32,
tile_ratio_height: f32,
shader_repeat_x: bool,
shader_repeat_y: bool,
@@ -978,17 +1043,17 @@ impl FrameContext {
pub fn get_scroll_node_state(&self) -> Vec<ScrollNodeState> {
self.clip_scroll_tree.get_scroll_node_state()
}
/// Returns true if the node actually changed position or false otherwise.
pub fn scroll_node(
&mut self,
origin: LayerPoint,
- id: ScrollNodeIdType,
+ id: ExternalScrollId,
clamp: ScrollClamping
) -> bool {
self.clip_scroll_tree.scroll_node(origin, id, clamp)
}
/// Returns true if any nodes actually changed position or false otherwise.
pub fn scroll(
&mut self,
@@ -1051,23 +1116,25 @@ impl FrameContext {
self.frame_builder_config,
),
clip_scroll_tree: &mut self.clip_scroll_tree,
font_instances: resource_cache.get_font_instances(),
tiled_image_map: resource_cache.get_tiled_image_map(),
pipeline_epochs: Vec::new(),
replacements: Vec::new(),
output_pipelines,
+ id_to_index_mapper: ClipIdToIndexMapper::new(),
};
roller.builder.push_root(
root_pipeline_id,
&root_pipeline.viewport_size,
&root_pipeline.content_size,
roller.clip_scroll_tree,
+ &mut roller.id_to_index_mapper,
);
roller.builder.setup_viewport_offset(
inner_rect,
device_pixel_scale,
roller.clip_scroll_tree,
);
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -1,41 +1,40 @@
/* 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/. */
-use api::{AlphaType, BorderDetails, BorderDisplayItem, BuiltDisplayList, ClipAndScrollInfo};
-use api::{ClipId, ColorF, DeviceIntPoint, DeviceIntRect, DeviceIntSize, DevicePixelScale};
-use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, DocumentLayer, Epoch, ExtendMode};
-use api::{ExternalScrollId, FontRenderMode, GlyphInstance, GlyphOptions, GradientStop, ImageKey};
-use api::{ImageRendering, ItemRange, LayerPoint, LayerPrimitiveInfo, LayerRect, LayerSize};
-use api::{LayerTransform, LayerVector2D, LayoutTransform, LayoutVector2D, LineOrientation};
-use api::{LineStyle, LocalClip, PipelineId, PremultipliedColorF, PropertyBinding, RepeatMode};
-use api::{ScrollSensitivity, Shadow, TexelRect, TileOffset, TransformStyle, WorldPoint};
-use api::{WorldToLayerTransform, YuvColorSpace, YuvData};
+use api::{AlphaType, BorderDetails, BorderDisplayItem, BuiltDisplayList, ClipId, ColorF};
+use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DevicePixelScale, DeviceUintPoint};
+use api::{DeviceUintRect, DeviceUintSize, DocumentLayer, Epoch, ExtendMode, ExternalScrollId};
+use api::{FontRenderMode, GlyphInstance, GlyphOptions, GradientStop, ImageKey, ImageRendering};
+use api::{ItemRange, LayerPoint, LayerPrimitiveInfo, LayerRect, LayerSize, LayerTransform};
+use api::{LayerVector2D, LayoutTransform, LayoutVector2D, LineOrientation, LineStyle, LocalClip};
+use api::{PipelineId, PremultipliedColorF, PropertyBinding, RepeatMode, ScrollSensitivity, Shadow};
+use api::{TexelRect, TileOffset, TransformStyle, WorldPoint, WorldToLayerTransform, YuvColorSpace};
+use api::YuvData;
use app_units::Au;
use border::ImageBorderSegment;
-use clip::{ClipRegion, ClipSource, ClipSources, ClipStore};
+use clip::{ClipChain, ClipRegion, ClipSource, ClipSources, ClipStore};
use clip_scroll_node::{ClipScrollNode, NodeType};
-use clip_scroll_tree::ClipScrollTree;
+use clip_scroll_tree::{ClipScrollTree, ClipChainIndex};
use euclid::{SideOffsets2D, vec2};
-use frame::FrameId;
+use frame::{FrameId, ClipIdToIndexMapper};
use glyph_rasterizer::FontInstance;
-use gpu_cache::GpuCache;
+use gpu_cache::{GpuCache, GpuCacheHandle};
use gpu_types::{ClipChainRectIndex, ClipScrollNodeData, PictureType};
use hit_test::{HitTester, HitTestingItem, HitTestingRun};
-use internal_types::{FastHashMap, FastHashSet, RenderPassIndex};
+use internal_types::{FastHashMap, FastHashSet};
use picture::{ContentOrigin, PictureCompositeMode, PictureKind, PicturePrimitive, PictureSurface};
-use prim_store::{BrushKind, BrushPrimitive, ImageCacheKey, YuvImagePrimitiveCpu};
-use prim_store::{GradientPrimitiveCpu, ImagePrimitiveCpu, ImageSource, PrimitiveKind};
-use prim_store::{PrimitiveContainer, PrimitiveIndex};
-use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu};
-use prim_store::{BrushSegmentDescriptor, PrimitiveRun, TextRunPrimitiveCpu};
+use prim_store::{BrushKind, BrushPrimitive, BrushSegmentDescriptor, GradientPrimitiveCpu};
+use prim_store::{ImageCacheKey, ImagePrimitiveCpu, ImageSource, PrimitiveContainer};
+use prim_store::{PrimitiveIndex, PrimitiveKind, PrimitiveRun, PrimitiveStore};
+use prim_store::{ScrollNodeAndClipChain, TextRunPrimitiveCpu};
use profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters};
-use render_task::{ClearMode, ClipChain, RenderTask, RenderTaskId, RenderTaskLocation, RenderTaskTree};
+use render_task::{ClearMode, RenderTask, RenderTaskId, RenderTaskLocation, RenderTaskTree};
use resource_cache::{ImageRequest, ResourceCache};
use scene::{ScenePipeline, SceneProperties};
use std::{mem, usize, f32};
use tiling::{CompositeOps, Frame, RenderPass, RenderTargetKind};
use tiling::{RenderPassKind, RenderTargetContext, ScrollbarPrimitive};
use util::{self, MaxRect, pack_as_float, RectHelpers, recycle_vec};
#[derive(Debug)]
@@ -85,20 +84,20 @@ pub struct FrameBuilder {
background_color: Option<ColorF>,
prim_store: PrimitiveStore,
pub clip_store: ClipStore,
hit_testing_runs: Vec<HitTestingRun>,
pub config: FrameBuilderConfig,
// A stack of the current shadow primitives.
// The sub-Vec stores a buffer of fast-path primitives to be appended on pop.
- shadow_prim_stack: Vec<(PrimitiveIndex, Vec<(PrimitiveIndex, ClipAndScrollInfo)>)>,
+ shadow_prim_stack: Vec<(PrimitiveIndex, Vec<(PrimitiveIndex, ScrollNodeAndClipChain)>)>,
// If we're doing any fast-path shadows, we buffer the "real"
// content here, to be appended when the shadow stack is empty.
- pending_shadow_contents: Vec<(PrimitiveIndex, ClipAndScrollInfo, LayerPrimitiveInfo)>,
+ pending_shadow_contents: Vec<(PrimitiveIndex, ScrollNodeAndClipChain, LayerPrimitiveInfo)>,
scrollbar_prims: Vec<ScrollbarPrimitive>,
/// A stack of scroll nodes used during display list processing to properly
/// parent new scroll nodes.
reference_frame_stack: Vec<ClipId>,
/// A stack of the current pictures, used during scene building.
@@ -145,24 +144,24 @@ impl PictureState {
pub fn new() -> PictureState {
PictureState {
tasks: Vec::new(),
}
}
}
pub struct PrimitiveRunContext<'a> {
- pub clip_chain: Option<&'a ClipChain>,
+ pub clip_chain: &'a ClipChain,
pub scroll_node: &'a ClipScrollNode,
pub clip_chain_rect_index: ClipChainRectIndex,
}
impl<'a> PrimitiveRunContext<'a> {
pub fn new(
- clip_chain: Option<&'a ClipChain>,
+ clip_chain: &'a ClipChain,
scroll_node: &'a ClipScrollNode,
clip_chain_rect_index: ClipChainRectIndex,
) -> Self {
PrimitiveRunContext {
clip_chain,
scroll_node,
clip_chain_rect_index,
}
@@ -247,17 +246,17 @@ impl FrameBuilder {
);
prim_index
}
pub fn add_primitive_to_hit_testing_list(
&mut self,
info: &LayerPrimitiveInfo,
- clip_and_scroll: ClipAndScrollInfo
+ clip_and_scroll: ScrollNodeAndClipChain
) {
let tag = match info.tag {
Some(tag) => tag,
None => return,
};
let new_item = HitTestingItem::new(tag, info);
match self.hit_testing_runs.last_mut() {
@@ -271,17 +270,17 @@ impl FrameBuilder {
self.hit_testing_runs.push(HitTestingRun(vec![new_item], clip_and_scroll));
}
/// Add an already created primitive to the draw lists.
pub fn add_primitive_to_draw_list(
&mut self,
prim_index: PrimitiveIndex,
- clip_and_scroll: ClipAndScrollInfo,
+ clip_and_scroll: ScrollNodeAndClipChain,
) {
// Add primitive to the top-most Picture on the stack.
// TODO(gw): Let's consider removing the extra indirection
// needed to get a specific primitive index...
let pic_prim_index = self.picture_stack.last().unwrap();
let metadata = &self.prim_store.cpu_metadata[pic_prim_index.0];
let pic = &mut self.prim_store.cpu_pictures[metadata.cpu_prim_index.0];
pic.add_primitive(
@@ -289,17 +288,17 @@ impl FrameBuilder {
clip_and_scroll
);
}
/// Convenience interface that creates a primitive entry and adds it
/// to the draw list.
pub fn add_primitive(
&mut self,
- clip_and_scroll: ClipAndScrollInfo,
+ clip_and_scroll: ScrollNodeAndClipChain,
info: &LayerPrimitiveInfo,
clip_sources: Vec<ClipSource>,
container: PrimitiveContainer,
) -> PrimitiveIndex {
self.add_primitive_to_hit_testing_list(info, clip_and_scroll);
let prim_index = self.create_primitive(info, clip_sources, container);
self.add_primitive_to_draw_list(prim_index, clip_and_scroll);
@@ -308,17 +307,17 @@ impl FrameBuilder {
pub fn push_stacking_context(
&mut self,
pipeline_id: PipelineId,
composite_ops: CompositeOps,
transform_style: TransformStyle,
is_backface_visible: bool,
is_pipeline_root: bool,
- clip_and_scroll: ClipAndScrollInfo,
+ clip_and_scroll: ScrollNodeAndClipChain,
output_pipelines: &FastHashSet<PipelineId>,
) {
// Construct the necessary set of Picture primitives
// to draw this stacking context.
let current_reference_frame_id = self.current_reference_frame_id();
// An arbitrary large clip rect. For now, we don't
// specify a clip specific to the stacking context.
@@ -635,27 +634,34 @@ impl FrameBuilder {
reference_frame_id: ClipId,
parent_id: Option<ClipId>,
pipeline_id: PipelineId,
rect: &LayerRect,
source_transform: Option<PropertyBinding<LayoutTransform>>,
source_perspective: Option<LayoutTransform>,
origin_in_parent_reference_frame: LayerVector2D,
clip_scroll_tree: &mut ClipScrollTree,
+ id_to_index_mapper: &mut ClipIdToIndexMapper,
) {
let node = ClipScrollNode::new_reference_frame(
parent_id,
rect,
source_transform,
source_perspective,
origin_in_parent_reference_frame,
pipeline_id,
);
clip_scroll_tree.add_node(node, reference_frame_id);
self.reference_frame_stack.push(reference_frame_id);
+
+ match parent_id {
+ Some(ref parent_id) =>
+ id_to_index_mapper.map_to_parent_clip_chain(reference_frame_id, parent_id),
+ _ => id_to_index_mapper.add(reference_frame_id, ClipChainIndex(0)),
+ }
}
pub fn current_reference_frame_id(&self) -> ClipId {
*self.reference_frame_stack.last().unwrap()
}
pub fn setup_viewport_offset(
&mut self,
@@ -677,95 +683,105 @@ impl FrameBuilder {
}
pub fn push_root(
&mut self,
pipeline_id: PipelineId,
viewport_size: &LayerSize,
content_size: &LayerSize,
clip_scroll_tree: &mut ClipScrollTree,
+ id_to_index_mapper: &mut ClipIdToIndexMapper,
) -> ClipId {
let viewport_rect = LayerRect::new(LayerPoint::zero(), *viewport_size);
self.push_reference_frame(
ClipId::root_reference_frame(pipeline_id),
None,
pipeline_id,
&viewport_rect,
None,
None,
LayerVector2D::zero(),
clip_scroll_tree,
+ id_to_index_mapper,
);
let topmost_scrolling_node_id = ClipId::root_scroll_node(pipeline_id);
clip_scroll_tree.topmost_scrolling_node_id = topmost_scrolling_node_id;
self.add_scroll_frame(
topmost_scrolling_node_id,
clip_scroll_tree.root_reference_frame_id,
Some(ExternalScrollId(0, pipeline_id)),
pipeline_id,
&viewport_rect,
content_size,
ScrollSensitivity::ScriptAndInputEvents,
clip_scroll_tree,
+ id_to_index_mapper,
);
topmost_scrolling_node_id
}
pub fn add_clip_node(
&mut self,
new_node_id: ClipId,
parent_id: ClipId,
- pipeline_id: PipelineId,
clip_region: ClipRegion,
clip_scroll_tree: &mut ClipScrollTree,
+ id_to_index_mapper: &mut ClipIdToIndexMapper,
) {
let clip_rect = clip_region.main;
let clip_sources = ClipSources::from(clip_region);
+
debug_assert!(clip_sources.has_clips());
-
let handle = self.clip_store.insert(clip_sources);
- let node = ClipScrollNode::new_clip_node(pipeline_id, parent_id, handle, clip_rect);
- clip_scroll_tree.add_node(node, new_node_id);
+ let clip_chain_index = clip_scroll_tree.add_clip_node(
+ new_node_id,
+ parent_id,
+ handle,
+ clip_rect
+ );
+ id_to_index_mapper.add(new_node_id, clip_chain_index);
}
pub fn add_scroll_frame(
&mut self,
new_node_id: ClipId,
parent_id: ClipId,
external_id: Option<ExternalScrollId>,
pipeline_id: PipelineId,
frame_rect: &LayerRect,
content_size: &LayerSize,
scroll_sensitivity: ScrollSensitivity,
clip_scroll_tree: &mut ClipScrollTree,
+ id_to_index_mapper: &mut ClipIdToIndexMapper,
) {
let node = ClipScrollNode::new_scroll_frame(
pipeline_id,
parent_id,
external_id,
frame_rect,
content_size,
scroll_sensitivity,
);
clip_scroll_tree.add_node(node, new_node_id);
+ id_to_index_mapper.map_to_parent_clip_chain(new_node_id, &parent_id);
}
pub fn pop_reference_frame(&mut self) {
self.reference_frame_stack.pop();
}
pub fn push_shadow(
&mut self,
shadow: Shadow,
- clip_and_scroll: ClipAndScrollInfo,
+ clip_and_scroll: ScrollNodeAndClipChain,
info: &LayerPrimitiveInfo,
) {
let pipeline_id = self.sc_stack.last().unwrap().pipeline_id;
let prim = PicturePrimitive::new_text_shadow(shadow, pipeline_id);
// Create an empty shadow primitive. Insert it into
// the draw lists immediately so that it will be drawn
// before any visual text elements that are added as
@@ -799,17 +815,17 @@ impl FrameBuilder {
}
mem::replace(&mut self.pending_shadow_contents, pending_primitives);
mem::replace(&mut self.shadow_prim_stack, shadows);
}
pub fn add_solid_rectangle(
&mut self,
- clip_and_scroll: ClipAndScrollInfo,
+ clip_and_scroll: ScrollNodeAndClipChain,
info: &LayerPrimitiveInfo,
color: ColorF,
segments: Option<BrushSegmentDescriptor>,
) {
if color.a == 0.0 {
// Don't add transparent rectangles to the draw list, but do consider them for hit
// testing. This allows specifying invisible hit testing areas.
self.add_primitive_to_hit_testing_list(info, clip_and_scroll);
@@ -828,17 +844,17 @@ impl FrameBuilder {
info,
Vec::new(),
PrimitiveContainer::Brush(prim),
);
}
pub fn add_clear_rectangle(
&mut self,
- clip_and_scroll: ClipAndScrollInfo,
+ clip_and_scroll: ScrollNodeAndClipChain,
info: &LayerPrimitiveInfo,
) {
let prim = BrushPrimitive::new(
BrushKind::Clear,
None,
);
self.add_primitive(
@@ -846,17 +862,17 @@ impl FrameBuilder {
info,
Vec::new(),
PrimitiveContainer::Brush(prim),
);
}
pub fn add_scroll_bar(
&mut self,
- clip_and_scroll: ClipAndScrollInfo,
+ clip_and_scroll: ScrollNodeAndClipChain,
info: &LayerPrimitiveInfo,
color: ColorF,
scrollbar_info: ScrollbarInfo,
) {
if color.a == 0.0 {
return;
}
@@ -878,17 +894,17 @@ impl FrameBuilder {
prim_index,
clip_id: scrollbar_info.0,
frame_rect: scrollbar_info.1,
});
}
pub fn add_line(
&mut self,
- clip_and_scroll: ClipAndScrollInfo,
+ clip_and_scroll: ScrollNodeAndClipChain,
info: &LayerPrimitiveInfo,
wavy_line_thickness: f32,
orientation: LineOrientation,
line_color: &ColorF,
style: LineStyle,
) {
let line = BrushPrimitive::new(
BrushKind::Line {
@@ -965,17 +981,17 @@ impl FrameBuilder {
}
_ => {}
}
}
}
pub fn add_border(
&mut self,
- clip_and_scroll: ClipAndScrollInfo,
+ clip_and_scroll: ScrollNodeAndClipChain,
info: &LayerPrimitiveInfo,
border_item: &BorderDisplayItem,
gradient_stops: ItemRange<GradientStop>,
gradient_stops_count: usize,
) {
let rect = info.rect;
let create_segments = |outset: SideOffsets2D<f32>| {
// Calculate the modified rect as specific by border-image-outset
@@ -1217,17 +1233,17 @@ impl FrameBuilder {
);
}
}
}
}
pub fn add_gradient(
&mut self,
- clip_and_scroll: ClipAndScrollInfo,
+ clip_and_scroll: ScrollNodeAndClipChain,
info: &LayerPrimitiveInfo,
start_point: LayerPoint,
end_point: LayerPoint,
stops: ItemRange<GradientStop>,
stops_count: usize,
extend_mode: ExtendMode,
tile_size: LayerSize,
tile_spacing: LayerSize,
@@ -1285,64 +1301,102 @@ impl FrameBuilder {
PrimitiveContainer::AlignedGradient(gradient_cpu)
} else {
PrimitiveContainer::AngleGradient(gradient_cpu)
};
self.add_primitive(clip_and_scroll, info, Vec::new(), prim);
}
+ fn add_radial_gradient_impl(
+ &mut self,
+ clip_and_scroll: ScrollNodeAndClipChain,
+ info: &LayerPrimitiveInfo,
+ start_center: LayerPoint,
+ start_radius: f32,
+ end_center: LayerPoint,
+ end_radius: f32,
+ ratio_xy: f32,
+ stops: ItemRange<GradientStop>,
+ extend_mode: ExtendMode,
+ ) {
+ let prim = BrushPrimitive::new(
+ BrushKind::RadialGradient {
+ stops_range: stops,
+ extend_mode,
+ stops_handle: GpuCacheHandle::new(),
+ start_center,
+ end_center,
+ start_radius,
+ end_radius,
+ ratio_xy,
+ },
+ None,
+ );
+
+ self.add_primitive(
+ clip_and_scroll,
+ info,
+ Vec::new(),
+ PrimitiveContainer::Brush(prim),
+ );
+ }
+
pub fn add_radial_gradient(
&mut self,
- clip_and_scroll: ClipAndScrollInfo,
+ clip_and_scroll: ScrollNodeAndClipChain,
info: &LayerPrimitiveInfo,
start_center: LayerPoint,
start_radius: f32,
end_center: LayerPoint,
end_radius: f32,
ratio_xy: f32,
stops: ItemRange<GradientStop>,
extend_mode: ExtendMode,
tile_size: LayerSize,
tile_spacing: LayerSize,
) {
- let tile_repeat = tile_size + tile_spacing;
+ let prim_infos = info.decompose(
+ tile_size,
+ tile_spacing,
+ 64 * 64,
+ );
- let radial_gradient_cpu = RadialGradientPrimitiveCpu {
- stops_range: stops,
- extend_mode,
- gpu_data_count: 0,
- gpu_blocks: [
- [start_center.x, start_center.y, end_center.x, end_center.y].into(),
- [
+ if prim_infos.is_empty() {
+ self.add_radial_gradient_impl(
+ clip_and_scroll,
+ info,
+ start_center,
+ start_radius,
+ end_center,
+ end_radius,
+ ratio_xy,
+ stops,
+ extend_mode,
+ );
+ } else {
+ for prim_info in prim_infos {
+ self.add_radial_gradient_impl(
+ clip_and_scroll,
+ &prim_info,
+ start_center,
start_radius,
+ end_center,
end_radius,
ratio_xy,
- pack_as_float(extend_mode as u32),
- ].into(),
- [
- tile_size.width,
- tile_size.height,
- tile_repeat.width,
- tile_repeat.height,
- ].into(),
- ],
- };
-
- self.add_primitive(
- clip_and_scroll,
- info,
- Vec::new(),
- PrimitiveContainer::RadialGradient(radial_gradient_cpu),
- );
+ stops,
+ extend_mode,
+ );
+ }
+ }
}
pub fn add_text(
&mut self,
- clip_and_scroll: ClipAndScrollInfo,
+ clip_and_scroll: ScrollNodeAndClipChain,
run_offset: LayoutVector2D,
info: &LayerPrimitiveInfo,
font: &FontInstance,
text_color: &ColorF,
glyph_range: ItemRange<GlyphInstance>,
glyph_count: usize,
glyph_options: Option<GlyphOptions>,
) {
@@ -1488,17 +1542,17 @@ impl FrameBuilder {
}
_ => {}
}
}
}
pub fn add_image(
&mut self,
- clip_and_scroll: ClipAndScrollInfo,
+ clip_and_scroll: ScrollNodeAndClipChain,
info: &LayerPrimitiveInfo,
stretch_size: LayerSize,
mut tile_spacing: LayerSize,
sub_rect: Option<TexelRect>,
image_key: ImageKey,
image_rendering: ImageRendering,
alpha_type: AlphaType,
tile_offset: Option<TileOffset>,
@@ -1568,42 +1622,44 @@ impl FrameBuilder {
Vec::new(),
PrimitiveContainer::Image(prim_cpu),
);
}
}
pub fn add_yuv_image(
&mut self,
- clip_and_scroll: ClipAndScrollInfo,
+ clip_and_scroll: ScrollNodeAndClipChain,
info: &LayerPrimitiveInfo,
yuv_data: YuvData,
color_space: YuvColorSpace,
image_rendering: ImageRendering,
) {
let format = yuv_data.get_format();
let yuv_key = match yuv_data {
YuvData::NV12(plane_0, plane_1) => [plane_0, plane_1, ImageKey::DUMMY],
YuvData::PlanarYCbCr(plane_0, plane_1, plane_2) => [plane_0, plane_1, plane_2],
YuvData::InterleavedYCbCr(plane_0) => [plane_0, ImageKey::DUMMY, ImageKey::DUMMY],
};
- let prim_cpu = YuvImagePrimitiveCpu {
- yuv_key,
- format,
- color_space,
- image_rendering,
- gpu_block: [info.rect.size.width, info.rect.size.height, 0.0, 0.0].into(),
- };
+ let prim = BrushPrimitive::new(
+ BrushKind::YuvImage {
+ yuv_key,
+ format,
+ color_space,
+ image_rendering,
+ },
+ None,
+ );
self.add_primitive(
clip_and_scroll,
info,
Vec::new(),
- PrimitiveContainer::YuvImage(prim_cpu),
+ PrimitiveContainer::Brush(prim),
);
}
/// Compute the contribution (bounding rectangles, and resources) of layers and their
/// primitives in screen space.
fn build_layer_screen_rects_and_cull_layers(
&mut self,
clip_scroll_tree: &ClipScrollTree,
@@ -1802,33 +1858,32 @@ impl FrameBuilder {
);
}
let mut deferred_resolves = vec![];
let mut has_texture_cache_tasks = false;
let use_dual_source_blending = self.config.dual_source_blending_is_enabled &&
self.config.dual_source_blending_is_supported;
- for (pass_index, pass) in passes.iter_mut().enumerate() {
+ for pass in &mut passes {
let ctx = RenderTargetContext {
device_pixel_scale,
prim_store: &self.prim_store,
resource_cache,
clip_scroll_tree,
use_dual_source_blending,
node_data: &node_data,
};
pass.build(
&ctx,
gpu_cache,
&mut render_tasks,
&mut deferred_resolves,
&self.clip_store,
- RenderPassIndex(pass_index),
);
if let RenderPassKind::OffScreen { ref texture_cache, .. } = pass.kind {
has_texture_cache_tasks |= !texture_cache.is_empty();
}
}
let gpu_cache_frame_id = gpu_cache.end_frame(gpu_cache_profile);
@@ -1858,8 +1913,72 @@ impl FrameBuilder {
pub fn create_hit_tester(&mut self, clip_scroll_tree: &ClipScrollTree) -> HitTester {
HitTester::new(
&self.hit_testing_runs,
&clip_scroll_tree,
&self.clip_store
)
}
}
+
+trait PrimitiveInfoTiler {
+ fn decompose(
+ &self,
+ tile_size: LayerSize,
+ tile_spacing: LayerSize,
+ max_prims: usize,
+ ) -> Vec<LayerPrimitiveInfo>;
+}
+
+impl PrimitiveInfoTiler for LayerPrimitiveInfo {
+ fn decompose(
+ &self,
+ tile_size: LayerSize,
+ tile_spacing: LayerSize,
+ max_prims: usize,
+ ) -> Vec<LayerPrimitiveInfo> {
+ let mut prims = Vec::new();
+ let tile_repeat = tile_size + tile_spacing;
+
+ if tile_repeat.width <= 0.0 ||
+ tile_repeat.height <= 0.0 {
+ return prims;
+ }
+
+ if tile_repeat.width < self.rect.size.width ||
+ tile_repeat.height < self.rect.size.height {
+ let local_clip = self.local_clip.clip_by(&self.rect);
+ let rect_p0 = self.rect.origin;
+ let rect_p1 = self.rect.bottom_right();
+
+ let mut y0 = rect_p0.y;
+ while y0 < rect_p1.y {
+ let mut x0 = rect_p0.x;
+
+ while x0 < rect_p1.x {
+ prims.push(LayerPrimitiveInfo {
+ rect: LayerRect::new(
+ LayerPoint::new(x0, y0),
+ tile_size,
+ ),
+ local_clip,
+ is_backface_visible: self.is_backface_visible,
+ tag: self.tag,
+ });
+
+ // Mostly a safety against a crazy number of primitives
+ // being generated. If we exceed that amount, just bail
+ // out and only draw the maximum amount.
+ if prims.len() > max_prims {
+ warn!("too many prims found due to repeat/tile. dropping extra prims!");
+ return prims;
+ }
+
+ x0 += tile_repeat.width;
+ }
+
+ y0 += tile_repeat.height;
+ }
+ }
+
+ prims
+ }
+}
--- a/gfx/webrender/src/glyph_rasterizer.rs
+++ b/gfx/webrender/src/glyph_rasterizer.rs
@@ -623,26 +623,27 @@ struct GlyphRasterJob {
result: Option<RasterizedGlyph>,
}
#[test]
fn rasterize_200_glyphs() {
// This test loads a font from disc, the renders 4 requests containing
// 50 glyphs each, deletes the font and waits for the result.
- use rayon::Configuration;
+ use rayon::ThreadPoolBuilder;
use std::fs::File;
use std::io::Read;
- let worker_config = Configuration::new()
+ let worker = ThreadPoolBuilder::new()
.thread_name(|idx|{ format!("WRWorker#{}", idx) })
.start_handler(move |idx| {
register_thread_with_profiler(format!("WRWorker#{}", idx));
- });
- let workers = Arc::new(ThreadPool::new(worker_config).unwrap());
+ })
+ .build();
+ let workers = Arc::new(worker.unwrap());
let mut glyph_rasterizer = GlyphRasterizer::new(workers).unwrap();
let mut glyph_cache = GlyphCache::new();
let mut gpu_cache = GpuCache::new();
let mut texture_cache = TextureCache::new(2048);
let mut font_file =
File::open("../wrench/reftests/text/VeraBd.ttf").expect("Couldn't open font file");
let mut font_data = vec![];
--- a/gfx/webrender/src/gpu_types.rs
+++ b/gfx/webrender/src/gpu_types.rs
@@ -142,16 +142,24 @@ impl From<CompositePrimitiveInstance> fo
instance.data1,
instance.data2,
instance.data3,
],
}
}
}
+bitflags! {
+ /// Flags that define how the common brush shader
+ /// code should process this instance.
+ pub struct BrushFlags: u8 {
+ const PERSPECTIVE_INTERPOLATION = 0x1;
+ }
+}
+
// TODO(gw): While we are comverting 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.
#[repr(C)]
@@ -159,28 +167,31 @@ pub struct BrushInstance {
pub picture_address: RenderTaskAddress,
pub prim_address: GpuCacheAddress,
pub clip_chain_rect_index: ClipChainRectIndex,
pub scroll_id: ClipScrollNodeIndex,
pub clip_task_address: RenderTaskAddress,
pub z: i32,
pub segment_index: i32,
pub edge_flags: EdgeAaSegmentMask,
+ pub brush_flags: BrushFlags,
pub user_data: [i32; 3],
}
impl From<BrushInstance> for PrimitiveInstance {
fn from(instance: BrushInstance) -> Self {
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) << 16) | instance.scroll_id.0 as i32,
instance.z,
- instance.segment_index | ((instance.edge_flags.bits() as i32) << 16),
+ instance.segment_index |
+ ((instance.edge_flags.bits() as i32) << 16) |
+ ((instance.brush_flags.bits() as i32) << 24),
instance.user_data[0],
instance.user_data[1],
instance.user_data[2],
]
}
}
}
--- a/gfx/webrender/src/hit_test.rs
+++ b/gfx/webrender/src/hit_test.rs
@@ -1,19 +1,20 @@
/* 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/. */
-use api::{BorderRadius, ClipAndScrollInfo, ClipId, ClipMode, HitTestFlags, HitTestItem};
-use api::{HitTestResult, ItemTag, LayerPoint, LayerPrimitiveInfo, LayerRect};
-use api::{LayerToWorldTransform, LocalClip, PipelineId, WorldPoint};
+use api::{BorderRadius, ClipId, ClipMode, HitTestFlags, HitTestItem, HitTestResult, ItemTag};
+use api::{LayerPoint, LayerPrimitiveInfo, LayerRect, LayerToWorldTransform, LocalClip, PipelineId};
+use api::WorldPoint;
use clip::{ClipSource, ClipStore, Contains, rounded_rectangle_contains_point};
use clip_scroll_node::{ClipScrollNode, NodeType};
-use clip_scroll_tree::ClipScrollTree;
+use clip_scroll_tree::{ClipChainIndex, ClipScrollTree};
use internal_types::FastHashMap;
+use prim_store::ScrollNodeAndClipChain;
/// A copy of important clip scroll node data to use during hit testing. This a copy of
/// data from the ClipScrollTree that will persist as a new frame is under construction,
/// allowing hit tests consistent with the currently rendered frame.
pub struct HitTestClipScrollNode {
/// A particular point must be inside all of these regions to be considered clipped in
/// for the purposes of a hit test.
regions: Vec<HitTestRegion>,
@@ -28,21 +29,31 @@ pub struct HitTestClipScrollNode {
node_origin: LayerPoint,
}
/// A description of a clip chain in the HitTester. This is used to describe
/// hierarchical clip scroll nodes as well as ClipChains, so that they can be
/// handled the same way during hit testing. Once we represent all ClipChains
/// using ClipChainDescriptors, we can get rid of this and just use the
/// ClipChainDescriptor here.
+#[derive(Clone)]
struct HitTestClipChainDescriptor {
- parent: Option<ClipId>,
+ parent: Option<ClipChainIndex>,
clips: Vec<ClipId>,
}
+impl HitTestClipChainDescriptor {
+ fn empty() -> HitTestClipChainDescriptor {
+ HitTestClipChainDescriptor {
+ parent: None,
+ clips: Vec::new(),
+ }
+ }
+}
+
#[derive(Clone)]
pub struct HitTestingItem {
rect: LayerRect,
clip: LocalClip,
tag: ItemTag,
}
impl HitTestingItem {
@@ -51,17 +62,17 @@ impl HitTestingItem {
rect: info.rect,
clip: info.local_clip,
tag: tag,
}
}
}
#[derive(Clone)]
-pub struct HitTestingRun(pub Vec<HitTestingItem>, pub ClipAndScrollInfo);
+pub struct HitTestingRun(pub Vec<HitTestingItem>, pub ScrollNodeAndClipChain);
enum HitTestRegion {
Rectangle(LayerRect),
RoundedRectangle(LayerRect, BorderRadius, ClipMode),
}
impl HitTestRegion {
pub fn contains(&self, point: &LayerPoint) -> bool {
@@ -73,95 +84,98 @@ impl HitTestRegion {
!rounded_rectangle_contains_point(point, &rect, &radii),
}
}
}
pub struct HitTester {
runs: Vec<HitTestingRun>,
nodes: FastHashMap<ClipId, HitTestClipScrollNode>,
- clip_chains: FastHashMap<ClipId, HitTestClipChainDescriptor>,
+ clip_chains: Vec<HitTestClipChainDescriptor>,
}
impl HitTester {
pub fn new(
runs: &Vec<HitTestingRun>,
clip_scroll_tree: &ClipScrollTree,
clip_store: &ClipStore
) -> HitTester {
let mut hit_tester = HitTester {
runs: runs.clone(),
nodes: FastHashMap::default(),
- clip_chains: FastHashMap::default(),
+ clip_chains: Vec::new(),
};
hit_tester.read_clip_scroll_tree(clip_scroll_tree, clip_store);
hit_tester
}
fn read_clip_scroll_tree(
&mut self,
clip_scroll_tree: &ClipScrollTree,
clip_store: &ClipStore
) {
self.nodes.clear();
+ self.clip_chains.clear();
+ self.clip_chains.resize(
+ clip_scroll_tree.clip_chains.len(),
+ HitTestClipChainDescriptor::empty()
+ );
for (id, node) in &clip_scroll_tree.nodes {
self.nodes.insert(*id, HitTestClipScrollNode {
regions: get_regions_for_clip_scroll_node(node, clip_store),
world_content_transform: node.world_content_transform,
world_viewport_transform: node.world_viewport_transform,
node_origin: node.local_viewport_rect.origin,
});
- self.clip_chains.insert(*id, HitTestClipChainDescriptor {
- parent: node.parent,
- clips: vec![*id],
- });
+ if let NodeType::Clip { clip_chain_index, .. } = node.node_type {
+ let clip_chain = self.clip_chains.get_mut(clip_chain_index.0).unwrap();
+ clip_chain.parent =
+ clip_scroll_tree.get_clip_chain(clip_chain_index).parent_index;
+ clip_chain.clips = vec![*id];
+ }
}
for descriptor in &clip_scroll_tree.clip_chains_descriptors {
- self.clip_chains.insert(
- ClipId::ClipChain(descriptor.id),
- HitTestClipChainDescriptor {
- parent: descriptor.parent.map(|id| ClipId::ClipChain(id)),
- clips: descriptor.clips.clone(),
- }
- );
+ let clip_chain = self.clip_chains.get_mut(descriptor.index.0).unwrap();
+ clip_chain.parent = clip_scroll_tree.get_clip_chain(descriptor.index).parent_index;
+ clip_chain.clips = descriptor.clips.clone();
}
}
fn is_point_clipped_in_for_clip_chain(
&self,
point: WorldPoint,
- chain_id: &ClipId,
+ clip_chain_index: ClipChainIndex,
test: &mut HitTest
) -> bool {
- if let Some(result) = test.clip_chain_cache.get(&chain_id) {
- return *result;
+ if let Some(result) = test.get_from_clip_chain_cache(clip_chain_index) {
+ return result;
}
- let descriptor = &self.clip_chains[&chain_id];
+ let descriptor = &self.clip_chains[clip_chain_index.0];
let parent_clipped_in = match descriptor.parent {
None => true,
- Some(ref parent) => self.is_point_clipped_in_for_clip_chain(point, parent, test),
+ Some(parent) => self.is_point_clipped_in_for_clip_chain(point, parent, test),
};
if !parent_clipped_in {
- test.clip_chain_cache.insert(*chain_id, false);
+ test.set_in_clip_chain_cache(clip_chain_index, false);
return false;
}
for clip_node in &descriptor.clips {
if !self.is_point_clipped_in_for_node(point, clip_node, test) {
- test.clip_chain_cache.insert(*chain_id, false);
+ test.set_in_clip_chain_cache(clip_chain_index, false);
return false;
}
}
- test.clip_chain_cache.insert(*chain_id, true);
+ test.set_in_clip_chain_cache(clip_chain_index, true);
true
}
fn is_point_clipped_in_for_node(
&self,
point: WorldPoint,
node_id: &ClipId,
test: &mut HitTest
@@ -192,17 +206,19 @@ impl HitTester {
true
}
pub fn hit_test(&self, mut test: HitTest) -> HitTestResult {
let point = test.get_absolute_point(self);
let mut result = HitTestResult::default();
for &HitTestingRun(ref items, ref clip_and_scroll) in self.runs.iter().rev() {
- let scroll_node = &self.nodes[&clip_and_scroll.scroll_node_id];
+ let scroll_node_id = clip_and_scroll.scroll_node_id;
+ let scroll_node = &self.nodes[&scroll_node_id];
+ let pipeline_id = scroll_node_id.pipeline_id();
match (test.pipeline_id, clip_and_scroll.scroll_node_id.pipeline_id()) {
(Some(id), node_id) if node_id != id => continue,
_ => {},
}
let transform = scroll_node.world_content_transform;
let point_in_layer = match transform.inverse() {
Some(inverted) => inverted.transform_point2d(&point),
@@ -210,40 +226,39 @@ impl HitTester {
};
let mut clipped_in = false;
for item in items.iter().rev() {
if !item.rect.contains(&point_in_layer) || !item.clip.contains(&point_in_layer) {
continue;
}
- let clip_id = &clip_and_scroll.clip_node_id();
+ let clip_chain_index = clip_and_scroll.clip_chain_index;
+ clipped_in |=
+ self.is_point_clipped_in_for_clip_chain(point, clip_chain_index, &mut test);
if !clipped_in {
- clipped_in = self.is_point_clipped_in_for_clip_chain(point, clip_id, &mut test);
- if !clipped_in {
- break;
- }
+ break;
}
// We need to trigger a lookup against the root reference frame here, because
// items that are clipped by clip chains won't test against that part of the
// hierarchy. If we don't have a valid point for this test, we are likely
// in a situation where the reference frame has an univertible transform, but the
// item's clip does not.
- let root_reference_frame = ClipId::root_reference_frame(clip_id.pipeline_id());
+ let root_reference_frame = ClipId::root_reference_frame(pipeline_id);
if !self.is_point_clipped_in_for_node(point, &root_reference_frame, &mut test) {
continue;
}
let point_in_viewport = match test.node_cache[&root_reference_frame] {
Some(point) => point,
None => continue,
};
result.items.push(HitTestItem {
- pipeline: clip_and_scroll.clip_node_id().pipeline_id(),
+ pipeline: pipeline_id,
tag: item.tag,
point_in_viewport,
point_relative_to_item: point_in_layer - item.rect.origin.to_vector(),
});
if !test.flags.contains(HitTestFlags::FIND_ALL) {
return result;
}
}
@@ -254,17 +269,17 @@ impl HitTester {
}
}
fn get_regions_for_clip_scroll_node(
node: &ClipScrollNode,
clip_store: &ClipStore
) -> Vec<HitTestRegion> {
let clips = match node.node_type {
- NodeType::Clip(ref handle) => clip_store.get(handle).clips(),
+ NodeType::Clip{ ref handle, .. } => clip_store.get(handle).clips(),
_ => return Vec::new(),
};
clips.iter().map(|ref source| {
match source.0 {
ClipSource::Rectangle(ref rect) => HitTestRegion::Rectangle(*rect),
ClipSource::RoundedRectangle(ref rect, ref radii, ref mode) =>
HitTestRegion::RoundedRectangle(*rect, *radii, *mode),
@@ -275,34 +290,49 @@ fn get_regions_for_clip_scroll_node(
}).collect()
}
pub struct HitTest {
pipeline_id: Option<PipelineId>,
point: WorldPoint,
flags: HitTestFlags,
node_cache: FastHashMap<ClipId, Option<LayerPoint>>,
- clip_chain_cache: FastHashMap<ClipId, bool>,
+ clip_chain_cache: Vec<Option<bool>>,
}
impl HitTest {
pub fn new(
pipeline_id: Option<PipelineId>,
point: WorldPoint,
flags: HitTestFlags,
) -> HitTest {
HitTest {
pipeline_id,
point,
flags,
node_cache: FastHashMap::default(),
- clip_chain_cache: FastHashMap::default(),
+ clip_chain_cache: Vec::new(),
}
}
+ pub fn get_from_clip_chain_cache(&mut self, index: ClipChainIndex) -> Option<bool> {
+ if index.0 >= self.clip_chain_cache.len() {
+ None
+ } else {
+ self.clip_chain_cache[index.0]
+ }
+ }
+
+ pub fn set_in_clip_chain_cache(&mut self, index: ClipChainIndex, value: bool) {
+ if index.0 >= self.clip_chain_cache.len() {
+ self.clip_chain_cache.resize(index.0 + 1, None);
+ }
+ self.clip_chain_cache[index.0] = Some(value);
+ }
+
pub fn get_absolute_point(&self, hit_tester: &HitTester) -> WorldPoint {
if !self.flags.contains(HitTestFlags::POINT_RELATIVE_TO_PIPELINE_VIEWPORT) {
return self.point;
}
let point = &LayerPoint::new(self.point.x, self.point.y);
self.pipeline_id.and_then(|id| hit_tester.nodes.get(&ClipId::root_reference_frame(id)))
.map(|node| node.world_viewport_transform.transform_point2d(&point))
--- a/gfx/webrender/src/internal_types.rs
+++ b/gfx/webrender/src/internal_types.rs
@@ -39,37 +39,38 @@ pub type FastHashSet<K> = HashSet<K, Bui
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct CacheTextureId(pub usize);
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
-pub struct RenderPassIndex(pub usize);
+pub struct SavedTargetIndex(pub usize);
+
+impl SavedTargetIndex {
+ pub const PENDING: Self = SavedTargetIndex(!0);
+}
// Represents the source for a texture.
// These are passed from throughout the
// pipeline until they reach the rendering
// thread, where they are resolved to a
// native texture ID.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum SourceTexture {
Invalid,
TextureCache(CacheTextureId),
External(ExternalImageData),
CacheA8,
CacheRGBA8,
- // XXX Remove this once RenderTaskCacheA8 is used.
- #[allow(dead_code)]
- RenderTaskCacheA8(RenderPassIndex),
- RenderTaskCacheRGBA8(RenderPassIndex),
+ RenderTaskCache(SavedTargetIndex),
}
pub const ORTHO_NEAR_PLANE: f32 = -1000000.0;
pub const ORTHO_FAR_PLANE: f32 = 1000000.0;
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
--- a/gfx/webrender/src/picture.rs
+++ b/gfx/webrender/src/picture.rs
@@ -1,21 +1,21 @@
/* 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/. */
-use api::{ColorF, ClipAndScrollInfo, FilterOp, MixBlendMode};
-use api::{DeviceIntPoint, DeviceIntRect, LayerToWorldScale, PipelineId};
-use api::{BoxShadowClipMode, LayerPoint, LayerRect, LayerVector2D, Shadow};
-use api::{ClipId, PremultipliedColorF};
+use api::{BoxShadowClipMode, ClipId, ColorF, DeviceIntPoint, DeviceIntRect, FilterOp, LayerPoint};
+use api::{LayerRect, LayerToWorldScale, LayerVector2D, MixBlendMode, PipelineId};
+use api::{PremultipliedColorF, Shadow};
use box_shadow::{BLUR_SAMPLE_SCALE, BoxShadowCacheKey};
use frame_builder::{FrameContext, FrameState, PictureState};
use gpu_cache::GpuDataRequest;
use gpu_types::{BrushImageKind, PictureType};
use prim_store::{BrushKind, BrushPrimitive, PrimitiveIndex, PrimitiveRun, PrimitiveRunLocalRect};
+use prim_store::ScrollNodeAndClipChain;
use render_task::{ClearMode, RenderTask, RenderTaskCacheKey};
use render_task::{RenderTaskCacheKeyKind, RenderTaskId, RenderTaskLocation};
use resource_cache::CacheItem;
use scene::{FilterOpHelpers, SceneProperties};
use tiling::RenderTargetKind;
/*
A picture represents a dynamically rendered image. It consists of:
@@ -225,17 +225,17 @@ impl PicturePrimitive {
None,
),
}
}
pub fn add_primitive(
&mut self,
prim_index: PrimitiveIndex,
- clip_and_scroll: ClipAndScrollInfo
+ clip_and_scroll: ScrollNodeAndClipChain
) {
if let Some(ref mut run) = self.runs.last_mut() {
if run.clip_and_scroll == clip_and_scroll &&
run.base_prim_index.0 + run.count == prim_index.0 {
run.count += 1;
return;
}
}
@@ -363,26 +363,27 @@ impl PicturePrimitive {
);
let render_task_id = frame_state.render_tasks.add(blur_render_task);
pic_state.tasks.push(render_task_id);
self.surface = Some(PictureSurface::RenderTask(render_task_id));
}
Some(PictureCompositeMode::Filter(FilterOp::DropShadow(offset, blur_radius, color))) => {
let rect = (prim_local_rect.translate(&-offset) * content_scale).round().to_i32();
- let picture_task = RenderTask::new_picture(
+ let mut picture_task = RenderTask::new_picture(
RenderTaskLocation::Dynamic(None, rect.size),
prim_index,
RenderTargetKind::Color,
ContentOrigin::Screen(rect.origin),
PremultipliedColorF::TRANSPARENT,
ClearMode::Transparent,
pic_state_for_children.tasks,
PictureType::Image,
);
+ picture_task.mark_for_saving();
let blur_std_deviation = blur_radius * frame_context.device_pixel_scale.0;
let picture_task_id = frame_state.render_tasks.add(picture_task);
let (blur_render_task, _) = RenderTask::new_blur(
blur_std_deviation.round(),
picture_task_id,
frame_state.render_tasks,
@@ -596,17 +597,17 @@ impl PicturePrimitive {
for _ in 0 .. 5 {
request.push([0.0; 4]);
}
}
PictureKind::Image { composite_mode, .. } => {
match composite_mode {
Some(PictureCompositeMode::Filter(FilterOp::ColorMatrix(m))) => {
for i in 0..5 {
- request.push([m[i], m[i+5], m[i+10], m[i+15]]);
+ request.push([m[i*4], m[i*4+1], m[i*4+2], m[i*4+3]]);
}
}
Some(PictureCompositeMode::Filter(filter)) => {
let amount = match filter {
FilterOp::Contrast(amount) => amount,
FilterOp::Grayscale(amount) => amount,
FilterOp::HueRotate(angle) => 0.01745329251 * angle,
FilterOp::Invert(amount) => amount,
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -1,47 +1,58 @@
/* 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/. */
-use api::{AlphaType, BorderRadius, BuiltDisplayList, ClipAndScrollInfo, ClipMode};
-use api::{ColorF, DeviceIntRect, DeviceIntSize, DevicePixelScale, Epoch};
-use api::{ComplexClipRegion, ExtendMode, FontRenderMode};
+use api::{AlphaType, BorderRadius, BuiltDisplayList, ClipId, ClipMode, ColorF, ComplexClipRegion};
+use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, Epoch, ExtendMode, FontRenderMode};
use api::{GlyphInstance, GlyphKey, GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag};
use api::{LayerPoint, LayerRect, LayerSize, LayerToWorldTransform, LayerVector2D, LineOrientation};
-use api::{LineStyle, PremultipliedColorF};
-use api::{WorldToLayerTransform, YuvColorSpace, YuvFormat};
+use api::{LineStyle, PremultipliedColorF, WorldToLayerTransform, YuvColorSpace, YuvFormat};
use border::{BorderCornerInstance, BorderEdgeKind};
-use clip_scroll_tree::{CoordinateSystemId};
+use clip_scroll_tree::{ClipChainIndex, CoordinateSystemId};
use clip_scroll_node::ClipScrollNode;
-use clip::{ClipSource, ClipSourcesHandle};
+use clip::{ClipChain, ClipChainNode, ClipChainNodeIter, ClipChainNodeRef, ClipSource};
+use clip::{ClipSourcesHandle, ClipWorkItem};
use frame_builder::{FrameContext, FrameState, PictureContext, PictureState, PrimitiveRunContext};
use glyph_rasterizer::{FontInstance, FontTransform};
use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest,
ToGpuBlocks};
use gpu_types::{ClipChainRectIndex};
use picture::{PictureKind, PicturePrimitive};
-use render_task::{BlitSource, ClipChain, ClipChainNode, ClipChainNodeIter, ClipChainNodeRef, ClipWorkItem};
-use render_task::{RenderTask, RenderTaskCacheKey, RenderTaskCacheKeyKind, RenderTaskId};
+use render_task::{BlitSource, RenderTask, RenderTaskCacheKey, RenderTaskCacheKeyKind};
+use render_task::RenderTaskId;
use renderer::{MAX_VERTEX_TEXTURE_WIDTH};
use resource_cache::{CacheItem, ImageProperties, ImageRequest, ResourceCache};
use segment::SegmentBuilder;
use std::{mem, usize};
use std::rc::Rc;
use util::{MatrixHelpers, calculate_screen_bounding_rect, pack_as_float};
use util::recycle_vec;
const MIN_BRUSH_SPLIT_AREA: f32 = 128.0 * 128.0;
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub struct ScrollNodeAndClipChain {
+ pub scroll_node_id: ClipId,
+ pub clip_chain_index: ClipChainIndex,
+}
+
+impl ScrollNodeAndClipChain {
+ pub fn new(scroll_node_id: ClipId, clip_chain_index: ClipChainIndex) -> ScrollNodeAndClipChain {
+ ScrollNodeAndClipChain { scroll_node_id, clip_chain_index }
+ }
+}
+
#[derive(Debug)]
pub struct PrimitiveRun {
pub base_prim_index: PrimitiveIndex,
pub count: usize,
- pub clip_and_scroll: ClipAndScrollInfo,
+ pub clip_and_scroll: ScrollNodeAndClipChain,
}
#[derive(Debug, Copy, Clone)]
pub struct PrimitiveOpacity {
pub is_opaque: bool,
}
impl PrimitiveOpacity {
@@ -107,21 +118,19 @@ pub struct SpecificPrimitiveIndex(pub us
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct PrimitiveIndex(pub usize);
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum PrimitiveKind {
TextRun,
Image,
- YuvImage,
Border,
AlignedGradient,
AngleGradient,
- RadialGradient,
Picture,
Brush,
}
impl GpuCacheHandle {
pub fn as_int(&self, gpu_cache: &GpuCache) -> i32 {
gpu_cache.get_address(self).as_int()
}
@@ -190,24 +199,42 @@ pub enum BrushKind {
orientation: LineOrientation,
},
Picture,
Image {
request: ImageRequest,
current_epoch: Epoch,
alpha_type: AlphaType,
},
+ YuvImage {
+ yuv_key: [ImageKey; 3],
+ format: YuvFormat,
+ color_space: YuvColorSpace,
+ image_rendering: ImageRendering,
+ },
+ RadialGradient {
+ stops_range: ItemRange<GradientStop>,
+ extend_mode: ExtendMode,
+ stops_handle: GpuCacheHandle,
+ start_center: LayerPoint,
+ end_center: LayerPoint,
+ start_radius: f32,
+ end_radius: f32,
+ ratio_xy: f32,
+ }
}
impl BrushKind {
fn supports_segments(&self) -> bool {
match *self {
BrushKind::Solid { .. } |
BrushKind::Picture |
- BrushKind::Image { .. } => true,
+ BrushKind::Image { .. } |
+ BrushKind::YuvImage { .. } |
+ BrushKind::RadialGradient { .. } => true,
BrushKind::Mask { .. } |
BrushKind::Clear |
BrushKind::Line { .. } => false,
}
}
}
@@ -279,17 +306,18 @@ impl BrushPrimitive {
segment_desc,
}
}
fn write_gpu_blocks(&self, request: &mut GpuDataRequest) {
// has to match VECS_PER_SPECIFIC_BRUSH
match self.kind {
BrushKind::Picture |
- BrushKind::Image { .. } => {
+ BrushKind::Image { .. } |
+ BrushKind::YuvImage { .. } => {
}
BrushKind::Solid { color } => {
request.push(color.premultiplied());
}
BrushKind::Clear => {
// Opaque black with operator dest out
request.push(PremultipliedColorF::BLACK);
}
@@ -326,16 +354,30 @@ impl BrushPrimitive {
request.push(color);
request.push([
wavy_line_thickness,
pack_as_float(style as u32),
pack_as_float(orientation as u32),
0.0,
]);
}
+ BrushKind::RadialGradient { start_center, end_center, start_radius, end_radius, ratio_xy, extend_mode, .. } => {
+ request.push([
+ start_center.x,
+ start_center.y,
+ end_center.x,
+ end_center.y,
+ ]);
+ request.push([
+ start_radius,
+ end_radius,
+ ratio_xy,
+ pack_as_float(extend_mode as u32),
+ ]);
+ }
}
}
}
// Key that identifies a unique (partial) image that is being
// stored in the render task cache.
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "capture", derive(Serialize))]
@@ -373,34 +415,16 @@ impl ToGpuBlocks for ImagePrimitiveCpu {
request.push([
self.stretch_size.width, self.stretch_size.height,
self.tile_spacing.width, self.tile_spacing.height,
]);
}
}
#[derive(Debug)]
-pub struct YuvImagePrimitiveCpu {
- pub yuv_key: [ImageKey; 3],
- pub format: YuvFormat,
- pub color_space: YuvColorSpace,
-
- pub image_rendering: ImageRendering,
-
- // TODO(gw): Generate on demand
- pub gpu_block: GpuBlockData,
-}
-
-impl ToGpuBlocks for YuvImagePrimitiveCpu {
- fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
- request.push(self.gpu_block);
- }
-}
-
-#[derive(Debug)]
pub struct BorderPrimitiveCpu {
pub corner_instances: [BorderCornerInstance; 4],
pub edges: [BorderEdgeKind; 4],
pub gpu_blocks: [GpuBlockData; 8],
}
impl ToGpuBlocks for BorderPrimitiveCpu {
fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
@@ -626,37 +650,16 @@ impl<'a> GradientGpuBlockBuilder<'a> {
for entry in entries.iter() {
request.push(entry.start_color);
request.push(entry.end_color);
}
}
}
-#[derive(Debug)]
-pub struct RadialGradientPrimitiveCpu {
- pub stops_range: ItemRange<GradientStop>,
- pub extend_mode: ExtendMode,
- pub gpu_data_count: i32,
- pub gpu_blocks: [GpuBlockData; 3],
-}
-
-impl RadialGradientPrimitiveCpu {
- fn build_gpu_blocks_for_angle_radial(
- &self,
- display_list: &BuiltDisplayList,
- mut request: GpuDataRequest,
- ) {
- request.extend_from_slice(&self.gpu_blocks);
-
- let gradient_builder = GradientGpuBlockBuilder::new(self.stops_range, display_list);
- gradient_builder.build(false, &mut request);
- }
-}
-
#[derive(Debug, Clone)]
pub struct TextRunPrimitiveCpu {
pub font: FontInstance,
pub offset: LayerVector2D,
pub glyph_range: ItemRange<GlyphInstance>,
pub glyph_count: usize,
pub glyph_keys: Vec<GlyphKey>,
pub glyph_gpu_blocks: Vec<GpuBlockData>,
@@ -928,63 +931,55 @@ impl ClipData {
}
}
}
#[derive(Debug)]
pub enum PrimitiveContainer {
TextRun(TextRunPrimitiveCpu),
Image(ImagePrimitiveCpu),
- YuvImage(YuvImagePrimitiveCpu),
Border(BorderPrimitiveCpu),
AlignedGradient(GradientPrimitiveCpu),
AngleGradient(GradientPrimitiveCpu),
- RadialGradient(RadialGradientPrimitiveCpu),
Picture(PicturePrimitive),
Brush(BrushPrimitive),
}
pub struct PrimitiveStore {
/// CPU side information only.
pub cpu_brushes: Vec<BrushPrimitive>,
pub cpu_text_runs: Vec<TextRunPrimitiveCpu>,
pub cpu_pictures: Vec<PicturePrimitive>,
pub cpu_images: Vec<ImagePrimitiveCpu>,
- pub cpu_yuv_images: Vec<YuvImagePrimitiveCpu>,
pub cpu_gradients: Vec<GradientPrimitiveCpu>,
- pub cpu_radial_gradients: Vec<RadialGradientPrimitiveCpu>,
pub cpu_metadata: Vec<PrimitiveMetadata>,
pub cpu_borders: Vec<BorderPrimitiveCpu>,
}
impl PrimitiveStore {
pub fn new() -> PrimitiveStore {
PrimitiveStore {
cpu_metadata: Vec::new(),
cpu_brushes: Vec::new(),
cpu_text_runs: Vec::new(),
cpu_pictures: Vec::new(),
cpu_images: Vec::new(),
- cpu_yuv_images: Vec::new(),
cpu_gradients: Vec::new(),
- cpu_radial_gradients: Vec::new(),
cpu_borders: Vec::new(),
}
}
pub fn recycle(self) -> Self {
PrimitiveStore {
cpu_metadata: recycle_vec(self.cpu_metadata),
cpu_brushes: recycle_vec(self.cpu_brushes),
cpu_text_runs: recycle_vec(self.cpu_text_runs),
cpu_pictures: recycle_vec(self.cpu_pictures),
cpu_images: recycle_vec(self.cpu_images),
- cpu_yuv_images: recycle_vec(self.cpu_yuv_images),
cpu_gradients: recycle_vec(self.cpu_gradients),
- cpu_radial_gradients: recycle_vec(self.cpu_radial_gradients),
cpu_borders: recycle_vec(self.cpu_borders),
}
}
pub fn add_primitive(
&mut self,
local_rect: &LayerRect,
local_clip_rect: &LayerRect,
@@ -1013,16 +1008,18 @@ impl PrimitiveStore {
let metadata = match container {
PrimitiveContainer::Brush(brush) => {
let opacity = match brush.kind {
BrushKind::Clear => PrimitiveOpacity::translucent(),
BrushKind::Solid { ref color } => PrimitiveOpacity::from_alpha(color.a),
BrushKind::Mask { .. } => PrimitiveOpacity::translucent(),
BrushKind::Line { .. } => PrimitiveOpacity::translucent(),
BrushKind::Image { .. } => PrimitiveOpacity::translucent(),
+ BrushKind::YuvImage { .. } => PrimitiveOpacity::opaque(),
+ BrushKind::RadialGradient { .. } => PrimitiveOpacity::translucent(),
BrushKind::Picture => {
// TODO(gw): This is not currently used. In the future
// we should detect opaque pictures.
unreachable!();
}
};
let metadata = PrimitiveMetadata {
@@ -1064,27 +1061,16 @@ impl PrimitiveStore {
prim_kind: PrimitiveKind::Image,
cpu_prim_index: SpecificPrimitiveIndex(self.cpu_images.len()),
..base_metadata
};
self.cpu_images.push(image_cpu);
metadata
}
- PrimitiveContainer::YuvImage(image_cpu) => {
- let metadata = PrimitiveMetadata {
- opacity: PrimitiveOpacity::opaque(),
- prim_kind: PrimitiveKind::YuvImage,
- cpu_prim_index: SpecificPrimitiveIndex(self.cpu_yuv_images.len()),
- ..base_metadata
- };
-
- self.cpu_yuv_images.push(image_cpu);
- metadata
- }
PrimitiveContainer::Border(border_cpu) => {
let metadata = PrimitiveMetadata {
opacity: PrimitiveOpacity::translucent(),
prim_kind: PrimitiveKind::Border,
cpu_prim_index: SpecificPrimitiveIndex(self.cpu_borders.len()),
..base_metadata
};
@@ -1109,28 +1095,16 @@ impl PrimitiveStore {
prim_kind: PrimitiveKind::AngleGradient,
cpu_prim_index: SpecificPrimitiveIndex(self.cpu_gradients.len()),
..base_metadata
};
self.cpu_gradients.push(gradient_cpu);
metadata
}
- PrimitiveContainer::RadialGradient(radial_gradient_cpu) => {
- let metadata = PrimitiveMetadata {
- // TODO: calculate if the gradient is actually opaque
- opacity: PrimitiveOpacity::translucent(),
- prim_kind: PrimitiveKind::RadialGradient,
- cpu_prim_index: SpecificPrimitiveIndex(self.cpu_radial_gradients.len()),
- ..base_metadata
- };
-
- self.cpu_radial_gradients.push(radial_gradient_cpu);
- metadata
- }
};
self.cpu_metadata.push(metadata);
PrimitiveIndex(prim_index)
}
pub fn get_metadata(&self, index: PrimitiveIndex) -> &PrimitiveMetadata {
@@ -1288,32 +1262,16 @@ impl PrimitiveStore {
if request_source_image {
frame_state.resource_cache.request_image(
image_cpu.key.request,
frame_state.gpu_cache,
);
}
}
}
- PrimitiveKind::YuvImage => {
- let image_cpu = &mut self.cpu_yuv_images[metadata.cpu_prim_index.0];
-
- let channel_num = image_cpu.format.get_plane_num();
- debug_assert!(channel_num <= 3);
- for channel in 0 .. channel_num {
- frame_state.resource_cache.request_image(
- ImageRequest {
- key: image_cpu.yuv_key[channel],
- rendering: image_cpu.image_rendering,
- tile: None,
- },
- frame_state.gpu_cache,
- );
- }
- }
PrimitiveKind::Brush => {
let brush = &mut self.cpu_brushes[metadata.cpu_prim_index.0];
match brush.kind {
BrushKind::Image { request, ref mut current_epoch, .. } => {
let image_properties = frame_state
.resource_cache
.get_image_properties(request.key);
@@ -1327,26 +1285,51 @@ impl PrimitiveStore {
}
}
frame_state.resource_cache.request_image(
request,
frame_state.gpu_cache,
);
}
+ BrushKind::YuvImage { format, yuv_key, image_rendering, .. } => {
+ let channel_num = format.get_plane_num();
+ debug_assert!(channel_num <= 3);
+ for channel in 0 .. channel_num {
+ frame_state.resource_cache.request_image(
+ ImageRequest {
+ key: yuv_key[channel],
+ rendering: image_rendering,
+ tile: None,
+ },
+ frame_state.gpu_cache,
+ );
+ }
+ }
+ BrushKind::RadialGradient { ref mut stops_handle, stops_range, .. } => {
+ if let Some(mut request) = frame_state.gpu_cache.request(stops_handle) {
+ let gradient_builder = GradientGpuBlockBuilder::new(
+ stops_range,
+ pic_context.display_list,
+ );
+ gradient_builder.build(
+ false,
+ &mut request,
+ );
+ }
+ }
BrushKind::Mask { .. } |
BrushKind::Solid { .. } |
BrushKind::Clear |
BrushKind::Line { .. } |
BrushKind::Picture { .. } => {}
}
}
PrimitiveKind::AlignedGradient |
- PrimitiveKind::AngleGradient |
- PrimitiveKind::RadialGradient => {}
+ PrimitiveKind::AngleGradient => {}
}
// 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);
@@ -1354,41 +1337,30 @@ impl PrimitiveStore {
PrimitiveKind::Border => {
let border = &self.cpu_borders[metadata.cpu_prim_index.0];
border.write_gpu_blocks(request);
}
PrimitiveKind::Image => {
let image = &self.cpu_images[metadata.cpu_prim_index.0];
image.write_gpu_blocks(request);
}
- PrimitiveKind::YuvImage => {
- let yuv_image = &self.cpu_yuv_images[metadata.cpu_prim_index.0];
- yuv_image.write_gpu_blocks(request);
- }
PrimitiveKind::AlignedGradient => {
let gradient = &self.cpu_gradients[metadata.cpu_prim_index.0];
metadata.opacity = gradient.build_gpu_blocks_for_aligned(
pic_context.display_list,
request,
);
}
PrimitiveKind::AngleGradient => {
let gradient = &self.cpu_gradients[metadata.cpu_prim_index.0];
gradient.build_gpu_blocks_for_angle_radial(
pic_context.display_list,
request,
);
}
- PrimitiveKind::RadialGradient => {
- let gradient = &self.cpu_radial_gradients[metadata.cpu_prim_index.0];
- gradient.build_gpu_blocks_for_angle_radial(
- pic_context.display_list,
- request,
- );
- }
PrimitiveKind::TextRun => {
let text = &self.cpu_text_runs[metadata.cpu_prim_index.0];
text.write_gpu_blocks(&mut request);
}
PrimitiveKind::Picture => {
let pic = &self.cpu_pictures[metadata.cpu_prim_index.0];
pic.write_gpu_blocks(&mut request);
@@ -1629,22 +1601,19 @@ impl PrimitiveStore {
let prim_screen_rect = match prim_screen_rect.intersection(&frame_context.screen_rect) {
Some(rect) => rect,
None => {
self.cpu_metadata[prim_index.0].screen_rect = None;
return false;
}
};
- let mut combined_outer_rect = match prim_run_context.clip_chain {
- Some(ref chain) => prim_screen_rect.intersection(&chain.combined_outer_screen_rect),
- None => Some(prim_screen_rect),
- };
-
- let clip_chain = prim_run_context.clip_chain.map_or(None, |x| x.nodes.clone());
+ 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();
let prim_coordinate_system_id = prim_run_context.scroll_node.coordinate_system_id;
let transform = &prim_run_context.scroll_node.world_content_transform;
let extra_clip = {
let metadata = &self.cpu_metadata[prim_index.0];
let prim_clips = frame_state.clip_store.get_mut(&metadata.clip_sources);
if prim_clips.has_clips() {
prim_clips.update(
@@ -1855,22 +1824,18 @@ impl PrimitiveStore {
};
let screen_bounding_rect = calculate_screen_bounding_rect(
&prim_run_context.scroll_node.world_content_transform,
&local_rect,
frame_context.device_pixel_scale,
);
- let clip_bounds = match prim_run_context.clip_chain {
- Some(ref node) => node.combined_outer_screen_rect,
- None => frame_context.screen_rect,
- };
metadata.screen_rect = screen_bounding_rect
- .intersection(&clip_bounds)
+ .intersection(&prim_run_context.clip_chain.combined_outer_screen_rect)
.map(|clipped| {
ScreenRect {
clipped,
unclipped: screen_bounding_rect,
}
});
if metadata.screen_rect.is_none() && pic_context.perform_culling {
@@ -1930,34 +1895,30 @@ impl PrimitiveStore {
// 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).
let scroll_node = &frame_context
.clip_scroll_tree
.nodes[&run.clip_and_scroll.scroll_node_id];
let clip_chain = frame_context
.clip_scroll_tree
- .get_clip_chain(&run.clip_and_scroll.clip_node_id());
+ .get_clip_chain(run.clip_and_scroll.clip_chain_index);
if pic_context.perform_culling {
if !scroll_node.invertible {
debug!("{:?} {:?}: position not invertible", run.base_prim_index, pic_context.pipeline_id);
continue;
}
- match clip_chain {
- Some(ref chain) if chain.combined_outer_screen_rect.is_empty() => {
- debug!("{:?} {:?}: clipped out", run.base_prim_index, pic_context.pipeline_id);
- continue;
- }
- _ => {},
+ if clip_chain.combined_outer_screen_rect.is_empty() {
+ debug!("{:?} {:?}: clipped out", run.base_prim_index, pic_context.pipeline_id);
+ continue;
}
}
-
let parent_relative_transform = pic_context
.inv_world_transform
.map(|inv_parent| {
inv_parent.pre_mul(&scroll_node.world_content_transform)
});
let original_relative_transform = pic_context.original_reference_frame_id
.and_then(|original_reference_frame_id| {
@@ -2077,24 +2038,19 @@ fn convert_clip_chain_to_clip_vector(
Some(node.work_item.clone())
})
.collect()
}
fn get_local_clip_rect_for_nodes(
scroll_node: &ClipScrollNode,
- clip_chain: Option<&ClipChain>,
+ clip_chain: &ClipChain,
) -> Option<LayerRect> {
- let clip_chain_nodes = match clip_chain {
- Some(ref clip_chain) => clip_chain.nodes.clone(),
- None => return None,
- };
-
- let local_rect = ClipChainNodeIter { current: clip_chain_nodes }.fold(
+ let local_rect = ClipChainNodeIter { current: clip_chain.nodes.clone() }.fold(
None,
|combined_local_clip_rect: Option<LayerRect>, node| {
if node.work_item.coordinate_system_id != scroll_node.coordinate_system_id {
return combined_local_clip_rect;
}
Some(match combined_local_clip_rect {
Some(combined_rect) =>
--- a/gfx/webrender/src/profiler.rs
+++ b/gfx/webrender/src/profiler.rs
@@ -295,25 +295,36 @@ impl ProfileCounter for AverageTimeProfi
}
}
#[derive(Clone)]
pub struct FrameProfileCounters {
pub total_primitives: IntProfileCounter,
pub visible_primitives: IntProfileCounter,
+ pub targets_used: IntProfileCounter,
+ pub targets_changed: IntProfileCounter,
+ pub targets_created: IntProfileCounter,
}
impl FrameProfileCounters {
pub fn new() -> Self {
FrameProfileCounters {
total_primitives: IntProfileCounter::new("Total Primitives"),
visible_primitives: IntProfileCounter::new("Visible Primitives"),
+ targets_used: IntProfileCounter::new("Used targets"),
+ targets_changed: IntProfileCounter::new("Changed targets"),
+ targets_created: IntProfileCounter::new("Created targets"),
}
}
+ pub fn reset_targets(&mut self) {
+ self.targets_used.reset();
+ self.targets_changed.reset();
+ self.targets_created.reset();
+ }
}
#[derive(Clone)]
pub struct TextureCacheProfileCounters {
pub pages_a8_linear: ResourceProfileCounter,
pub pages_rgba8_linear: ResourceProfileCounter,
pub pages_rgba8_nearest: ResourceProfileCounter,
}
@@ -835,17 +846,17 @@ impl Profiler {
let new_y = total_rect.origin.y + total_rect.size.height + 30.0;
if left {
draw_state.y_left = new_y;
} else {
draw_state.y_right = new_y;
}
}
- fn draw_gpu_cache_bar(
+ fn draw_bar(
&mut self,
label: &str,
label_color: ColorU,
counters: &[(ColorU, &IntProfileCounter)],
debug_renderer: &mut DebugRenderer,
) -> Rect<f32> {
let mut rect = debug_renderer.add_text(
self.draw_state.x_left,
@@ -893,34 +904,73 @@ impl Profiler {
description: "",
value: counters.updated_blocks.value + counters.saved_blocks.value,
};
let total_blocks = IntProfileCounter {
description: "",
value: counters.allocated_rows.value * MAX_VERTEX_TEXTURE_WIDTH,
};
- let rect0 = self.draw_gpu_cache_bar(
+ let rect0 = self.draw_bar(
&format!("GPU cache rows ({}):", counters.allocated_rows.value),
- ColorU::new(255, 255, 255, 255),
+ ColorU::new(0xFF, 0xFF, 0xFF, 0xFF),
&[
(color_updated, &counters.updated_rows),
(color_free, &counters.allocated_rows),
],
debug_renderer,
);
- let rect1 = self.draw_gpu_cache_bar(
+ let rect1 = self.draw_bar(
"GPU cache blocks",
- ColorU::new(255, 255, 0, 255),
+ ColorU::new(0xFF, 0xFF, 0, 0xFF),
&[
(color_updated, &counters.updated_blocks),
(color_saved, &requested_blocks),
(color_free, &counters.allocated_blocks),
- (ColorU::new(0, 0, 0, 255), &total_blocks),
+ (ColorU::new(0, 0, 0, 0xFF), &total_blocks),
+ ],
+ debug_renderer,
+ );
+
+ let total_rect = rect0.union(&rect1).inflate(10.0, 10.0);
+ debug_renderer.add_quad(
+ total_rect.origin.x,
+ total_rect.origin.y,
+ total_rect.origin.x + total_rect.size.width,
+ total_rect.origin.y + total_rect.size.height,
+ ColorF::new(0.1, 0.1, 0.1, 0.8).into(),
+ ColorF::new(0.2, 0.2, 0.2, 0.8).into(),
+ );
+
+ self.draw_state.y_left = total_rect.origin.y + total_rect.size.height + 30.0;
+ }
+
+ fn draw_frame_bars(
+ &mut self,
+ counters: &FrameProfileCounters,
+ debug_renderer: &mut DebugRenderer,
+ ) {
+ let rect0 = self.draw_bar(
+ &format!("primitives ({}):", counters.total_primitives.value),
+ ColorU::new(0xFF, 0xFF, 0xFF, 0xFF),
+ &[
+ (ColorU::new(0, 0, 0xFF, 0xFF), &counters.visible_primitives),
+ (ColorU::new(0, 0, 0, 0xFF), &counters.total_primitives),
+ ],
+ debug_renderer,
+ );
+
+ let rect1 = self.draw_bar(
+ &format!("GPU targets ({}):", &counters.targets_used.value),
+ ColorU::new(0xFF, 0xFF, 0, 0xFF),
+ &[
+ (ColorU::new(0, 0, 0xFF, 0xFF), &counters.targets_created),
+ (ColorU::new(0xFF, 0, 0, 0xFF), &counters.targets_changed),
+ (ColorU::new(0, 0xFF, 0, 0xFF), &counters.targets_used),
],
debug_renderer,
);
let total_rect = rect0.union(&rect1).inflate(10.0, 10.0);
debug_renderer.add_quad(
total_rect.origin.x,
total_rect.origin.y,
@@ -1012,25 +1062,17 @@ impl Profiler {
&backend_profile.ipc.total_time,
],
debug_renderer,
true,
&mut self.draw_state
);
for frame_profile in frame_profiles {
- Profiler::draw_counters(
- &[
- &frame_profile.total_primitives,
- &frame_profile.visible_primitives,
- ],
- debug_renderer,
- true,
- &mut self.draw_state
- );
+ self.draw_frame_bars(frame_profile, debug_renderer);
}
Profiler::draw_counters(
&[&renderer_profile.draw_calls, &renderer_profile.vertices],
debug_renderer,
true,
&mut self.draw_state
);
--- a/gfx/webrender/src/render_task.rs
+++ b/gfx/webrender/src/render_task.rs
@@ -1,28 +1,27 @@
/* 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/. */
-use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize};
-use api::{ImageDescriptor, ImageFormat, LayerRect, PremultipliedColorF};
+use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, ImageDescriptor, ImageFormat};
+use api::PremultipliedColorF;
use box_shadow::BoxShadowCacheKey;
-use clip::{ClipSourcesWeakHandle};
+use clip::ClipWorkItem;
use clip_scroll_tree::CoordinateSystemId;
use device::TextureFilter;
use gpu_cache::GpuCache;
-use gpu_types::{ClipScrollNodeIndex, PictureType};
-use internal_types::{FastHashMap, RenderPassIndex, SourceTexture};
+use gpu_types::PictureType;
+use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture};
use picture::ContentOrigin;
use prim_store::{PrimitiveIndex, ImageCacheKey};
#[cfg(feature = "debugger")]
use print_tree::{PrintTreePrinter};
use resource_cache::CacheItem;
use std::{cmp, ops, usize, f32, i32};
-use std::rc::Rc;
use texture_cache::{TextureCache, TextureCacheHandle};
use tiling::{RenderPass, RenderTargetIndex};
use tiling::{RenderTargetKind};
const FLOATS_PER_RENDER_TASK_INFO: usize = 12;
pub const MAX_BLUR_STD_DEVIATION: f32 = 4.0;
pub const MIN_DOWNSCALING_RT_SIZE: i32 = 128;
@@ -38,108 +37,25 @@ pub struct RenderTaskId(pub u32); // TOD
pub struct RenderTaskAddress(pub u32);
#[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct RenderTaskTree {
pub tasks: Vec<RenderTask>,
pub task_data: Vec<RenderTaskData>,
-}
-
-pub type ClipChainNodeRef = Option<Rc<ClipChainNode>>;
-
-#[derive(Debug, Clone)]
-pub struct ClipChainNode {
- pub work_item: ClipWorkItem,
- pub local_clip_rect: LayerRect,
- pub screen_outer_rect: DeviceIntRect,
- pub screen_inner_rect: DeviceIntRect,
- pub prev: ClipChainNodeRef,
-}
-
-#[derive(Debug, Clone)]
-pub struct ClipChain {
- pub combined_outer_screen_rect: DeviceIntRect,
- pub combined_inner_screen_rect: DeviceIntRect,
- pub nodes: ClipChainNodeRef,
-}
-
-impl ClipChain {
- pub fn empty(screen_rect: &DeviceIntRect) -> ClipChain {
- ClipChain {
- combined_inner_screen_rect: *screen_rect,
- combined_outer_screen_rect: *screen_rect,
- nodes: None,
- }
- }
-
- pub fn new_with_added_node(
- &self,
- work_item: ClipWorkItem,
- local_clip_rect: LayerRect,
- screen_outer_rect: DeviceIntRect,
- screen_inner_rect: DeviceIntRect,
- ) -> ClipChain {
- let new_node = ClipChainNode {
- work_item,
- local_clip_rect,
- screen_outer_rect,
- screen_inner_rect,
- prev: None,
- };
-
- let mut new_chain = self.clone();
- new_chain.add_node(new_node);
- new_chain
- }
-
- pub fn add_node(&mut self, mut new_node: ClipChainNode) {
- new_node.prev = self.nodes.clone();
-
- // If this clip's outer rectangle is completely enclosed by the clip
- // chain's inner rectangle, then the only clip that matters from this point
- // on is this clip. We can disconnect this clip from the parent clip chain.
- if self.combined_inner_screen_rect.contains_rect(&new_node.screen_outer_rect) {
- new_node.prev = None;
- }
-
- self.combined_outer_screen_rect =
- self.combined_outer_screen_rect.intersection(&new_node.screen_outer_rect)
- .unwrap_or_else(DeviceIntRect::zero);
- self.combined_inner_screen_rect =
- self.combined_inner_screen_rect.intersection(&new_node.screen_inner_rect)
- .unwrap_or_else(DeviceIntRect::zero);
-
- self.nodes = Some(Rc::new(new_node));
- }
-}
-
-pub struct ClipChainNodeIter {
- pub current: ClipChainNodeRef,
-}
-
-impl Iterator for ClipChainNodeIter {
- type Item = Rc<ClipChainNode>;
-
- fn next(&mut self) -> ClipChainNodeRef {
- let previous = self.current.clone();
- self.current = match self.current {
- Some(ref item) => item.prev.clone(),
- None => return None,
- };
- previous
- }
+ next_saved: SavedTargetIndex,
}
impl RenderTaskTree {
pub fn new() -> Self {
RenderTaskTree {
tasks: Vec::new(),
task_data: Vec::new(),
+ next_saved: SavedTargetIndex(0),
}
}
pub fn add(&mut self, task: RenderTask) -> RenderTaskId {
let id = RenderTaskId(self.tasks.len() as u32);
self.tasks.push(task);
id
}
@@ -190,20 +106,26 @@ impl RenderTaskTree {
pass.add_render_task(id, task.get_dynamic_size(), task.target_kind());
}
pub fn get_task_address(&self, id: RenderTaskId) -> RenderTaskAddress {
RenderTaskAddress(id.0)
}
pub fn build(&mut self) {
- for task in &mut self.tasks {
+ for task in &self.tasks {
self.task_data.push(task.write_task_data());
}
}
+
+ pub fn save_target(&mut self) -> SavedTargetIndex {
+ let id = self.next_saved;
+ self.next_saved.0 += 1;
+ id
+ }
}
impl ops::Index<RenderTaskId> for RenderTaskTree {
type Output = RenderTask;
fn index(&self, id: RenderTaskId) -> &RenderTask {
&self.tasks[id.0 as usize]
}
}
@@ -218,25 +140,16 @@ impl ops::IndexMut<RenderTaskId> for Ren
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum RenderTaskLocation {
Fixed(DeviceIntRect),
Dynamic(Option<(DeviceIntPoint, RenderTargetIndex)>, DeviceIntSize),
TextureCache(SourceTexture, i32, DeviceIntRect),
}
-#[derive(Debug, Clone)]
-#[cfg_attr(feature = "capture", derive(Serialize))]
-#[cfg_attr(feature = "replay", derive(Deserialize))]
-pub struct ClipWorkItem {
- pub scroll_node_data_index: ClipScrollNodeIndex,
- pub clip_sources: ClipSourcesWeakHandle,
- pub coordinate_system_id: CoordinateSystemId,
-}
-
#[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct CacheMaskTask {
actual_rect: DeviceIntRect,
pub clips: Vec<ClipWorkItem>,
pub coordinate_system_id: CoordinateSystemId,
}
@@ -326,17 +239,17 @@ pub enum ClearMode {
#[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct RenderTask {
pub location: RenderTaskLocation,
pub children: Vec<RenderTaskId>,
pub kind: RenderTaskKind,
pub clear_mode: ClearMode,
- pub pass_index: Option<RenderPassIndex>,
+ pub saved_index: Option<SavedTargetIndex>,
}
impl RenderTask {
pub fn new_picture(
location: RenderTaskLocation,
prim_index: PrimitiveIndex,
target_kind: RenderTargetKind,
content_origin: ContentOrigin,
@@ -351,27 +264,27 @@ impl RenderTask {
kind: RenderTaskKind::Picture(PictureTask {
prim_index,
target_kind,
content_origin,
color,
pic_type,
}),
clear_mode,
- pass_index: None,
+ saved_index: None,
}
}
pub fn new_readback(screen_rect: DeviceIntRect) -> Self {
RenderTask {
children: Vec::new(),
location: RenderTaskLocation::Dynamic(None, screen_rect.size),
kind: RenderTaskKind::Readback(screen_rect),
clear_mode: ClearMode::Transparent,
- pass_index: None,
+ saved_index: None,
}
}
pub fn new_blit(
size: DeviceIntSize,
source: BlitSource,
) -> Self {
let mut children = Vec::new();
@@ -387,35 +300,35 @@ impl RenderTask {
RenderTask {
children,
location: RenderTaskLocation::Dynamic(None, size),
kind: RenderTaskKind::Blit(BlitTask {
source,
}),
clear_mode: ClearMode::Transparent,
- pass_index: None,
+ saved_index: None,
}
}
pub fn new_mask(
outer_rect: DeviceIntRect,
clips: Vec<ClipWorkItem>,
prim_coordinate_system_id: CoordinateSystemId,
- ) -> RenderTask {
+ ) -> Self {
RenderTask {
children: Vec::new(),
location: RenderTaskLocation::Dynamic(None, outer_rect.size),
kind: RenderTaskKind::CacheMask(CacheMaskTask {
actual_rect: outer_rect,
clips,
coordinate_system_id: prim_coordinate_system_id,
}),
clear_mode: ClearMode::One,
- pass_index: None,
+ saved_index: None,
}
}
// Construct a render task to apply a blur to a primitive.
// The render task chain that is constructed looks like:
//
// PrimitiveCacheTask: Draw the primitives.
// ^
@@ -468,32 +381,32 @@ impl RenderTask {
location: RenderTaskLocation::Dynamic(None, adjusted_blur_target_size),
kind: RenderTaskKind::VerticalBlur(BlurTask {
blur_std_deviation: adjusted_blur_std_deviation,
target_kind,
color,
scale_factor,
}),
clear_mode,
- pass_index: None,
+ saved_index: None,
};
let blur_task_v_id = render_tasks.add(blur_task_v);
let blur_task_h = RenderTask {
children: vec![blur_task_v_id],
location: RenderTaskLocation::Dynamic(None, adjusted_blur_target_size),
kind: RenderTaskKind::HorizontalBlur(BlurTask {
blur_std_deviation: adjusted_blur_std_deviation,
target_kind,
color,
scale_factor,
}),
clear_mode,
- pass_index: None,
+ saved_index: None,
};
(blur_task_h, scale_factor)
}
pub fn new_scaling(
target_kind: RenderTargetKind,
src_task_id: RenderTaskId,
@@ -502,17 +415,17 @@ impl RenderTask {
RenderTask {
children: vec![src_task_id],
location: RenderTaskLocation::Dynamic(None, target_size),
kind: RenderTaskKind::Scaling(target_kind),
clear_mode: match target_kind {
RenderTargetKind::Color => ClearMode::Transparent,
RenderTargetKind::Alpha => ClearMode::One,
},
- pass_index: None,
+ saved_index: None,
}
}
// Write (up to) 8 floats of data specific to the type
// of render task that is provided to the GPU shaders
// via a vertex texture.
pub fn write_task_data(&self) -> RenderTaskData {
// NOTE: The ordering and layout of these structures are
@@ -669,17 +582,16 @@ impl RenderTask {
pub fn is_shared(&self) -> bool {
match self.kind {
RenderTaskKind::Picture(..) |
RenderTaskKind::VerticalBlur(..) |
RenderTaskKind::Readback(..) |
RenderTaskKind::HorizontalBlur(..) |
RenderTaskKind::Scaling(..) |
RenderTaskKind::Blit(..) => false,
-
RenderTaskKind::CacheMask(..) => true,
}
}
#[cfg(feature = "debugger")]
pub fn print_with<T: PrintTreePrinter>(&self, pt: &mut T, tree: &RenderTaskTree) -> bool {
match self.kind {
RenderTaskKind::Picture(ref task) => {
@@ -718,16 +630,29 @@ impl RenderTask {
if tree[child_id].print_with(pt, tree) {
pt.add_item(format!("self: {:?}", child_id))
}
}
pt.end_level();
true
}
+
+ /// Mark this render task for keeping the results alive up until the end of the frame.
+ pub fn mark_for_saving(&mut self) {
+ match self.location {
+ RenderTaskLocation::Fixed(..) |
+ RenderTaskLocation::Dynamic(..) => {
+ self.saved_index = Some(SavedTargetIndex::PENDING);
+ }
+ RenderTaskLocation::TextureCache(..) => {
+ panic!("Unable to mark a permanently cached task for saving!");
+ }
+ }
+ }
}
#[derive(Debug, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum RenderTaskCacheKeyKind {
BoxShadow(BoxShadowCacheKey),
Image(ImageCacheKey),
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -35,25 +35,25 @@ use device::{FileWatcherHandler, ShaderE
use device::{ProgramCache, ReadPixelsFormat};
use euclid::{rect, Transform3D};
use frame_builder::FrameBuilderConfig;
use gleam::gl;
use glyph_rasterizer::GlyphFormat;
use gpu_cache::{GpuBlockData, GpuCacheUpdate, GpuCacheUpdateList};
use gpu_types::PrimitiveInstance;
use internal_types::{SourceTexture, ORTHO_FAR_PLANE, ORTHO_NEAR_PLANE, ResourceCacheError};
-use internal_types::{CacheTextureId, FastHashMap, RenderedDocument, ResultMsg, TextureUpdateOp};
-use internal_types::{DebugOutput, RenderPassIndex, RenderTargetInfo, TextureUpdateList, TextureUpdateSource};
+use internal_types::{CacheTextureId, DebugOutput, FastHashMap, RenderedDocument, ResultMsg};
+use internal_types::{TextureUpdateList, TextureUpdateOp, TextureUpdateSource};
+use internal_types::{RenderTargetInfo, SavedTargetIndex};
use picture::ContentOrigin;
use prim_store::DeferredResolve;
-use profiler::{BackendProfileCounters, Profiler};
+use profiler::{BackendProfileCounters, FrameProfileCounters, Profiler};
use profiler::{GpuProfileTag, RendererProfileCounters, RendererProfileTimers};
use query::{GpuProfiler, GpuTimer};
-use rayon::Configuration as ThreadPoolConfig;
-use rayon::ThreadPool;
+use rayon::{ThreadPool, ThreadPoolBuilder};
use record::ApiRecordingReceiver;
use render_backend::RenderBackend;
use render_task::{RenderTaskKind, RenderTaskTree};
use resource_cache::ResourceCache;
#[cfg(feature = "debugger")]
use serde_json;
use std;
use std::cmp;
@@ -78,16 +78,24 @@ pub const MAX_VERTEX_TEXTURE_WIDTH: usiz
/// Enabling this toggle would force the GPU cache scattered texture to
/// be resized every frame, which enables GPU debuggers to see if this
/// is performed correctly.
const GPU_CACHE_RESIZE_TEST: bool = false;
/// Number of GPU blocks per UV rectangle provided for an image.
pub const BLOCKS_PER_UV_RECT: usize = 2;
+const GPU_TAG_BRUSH_RADIAL_GRADIENT: GpuProfileTag = GpuProfileTag {
+ label: "B_RadialGradient",
+ color: debug_colors::LIGHTPINK,
+};
+const GPU_TAG_BRUSH_YUV_IMAGE: GpuProfileTag = GpuProfileTag {
+ label: "B_YuvImage",
+ color: debug_colors::DARKGREEN,
+};
const GPU_TAG_BRUSH_MIXBLEND: GpuProfileTag = GpuProfileTag {
label: "B_MixBlend",
color: debug_colors::MAGENTA,
};
const GPU_TAG_BRUSH_BLEND: GpuProfileTag = GpuProfileTag {
label: "B_Blend",
color: debug_colors::LIGHTBLUE,
};
@@ -126,20 +134,16 @@ const GPU_TAG_SETUP_TARGET: GpuProfileTa
const GPU_TAG_SETUP_DATA: GpuProfileTag = GpuProfileTag {
label: "data init",
color: debug_colors::LIGHTGREY,
};
const GPU_TAG_PRIM_IMAGE: GpuProfileTag = GpuProfileTag {
label: "Image",
color: debug_colors::GREEN,
};
-const GPU_TAG_PRIM_YUV_IMAGE: GpuProfileTag = GpuProfileTag {
- label: "YuvImage",
- color: debug_colors::DARKGREEN,
-};
const GPU_TAG_PRIM_HW_COMPOSITE: GpuProfileTag = GpuProfileTag {
label: "HwComposite",
color: debug_colors::DODGERBLUE,
};
const GPU_TAG_PRIM_SPLIT_COMPOSITE: GpuProfileTag = GpuProfileTag {
label: "SplitComposite",
color: debug_colors::DARKBLUE,
};
@@ -150,20 +154,16 @@ const GPU_TAG_PRIM_TEXT_RUN: GpuProfileT
const GPU_TAG_PRIM_GRADIENT: GpuProfileTag = GpuProfileTag {
label: "Gradient",
color: debug_colors::YELLOW,
};
const GPU_TAG_PRIM_ANGLE_GRADIENT: GpuProfileTag = GpuProfileTag {
label: "AngleGradient",
color: debug_colors::POWDERBLUE,
};
-const GPU_TAG_PRIM_RADIAL_GRADIENT: GpuProfileTag = GpuProfileTag {
- label: "RadialGradient",
- color: debug_colors::LIGHTPINK,
-};
const GPU_TAG_PRIM_BORDER_CORNER: GpuProfileTag = GpuProfileTag {
label: "BorderCorner",
color: debug_colors::DARKSLATEGREY,
};
const GPU_TAG_PRIM_BORDER_EDGE: GpuProfileTag = GpuProfileTag {
label: "BorderEdge",
color: debug_colors::LAVENDER,
};
@@ -195,35 +195,31 @@ impl TransformBatchKind {
match *self {
TransformBatchKind::TextRun(..) => "TextRun",
TransformBatchKind::Image(image_buffer_kind, ..) => match image_buffer_kind {
ImageBufferKind::Texture2D => "Image (2D)",
ImageBufferKind::TextureRect => "Image (Rect)",
ImageBufferKind::TextureExternal => "Image (External)",
ImageBufferKind::Texture2DArray => "Image (Array)",
},
- TransformBatchKind::YuvImage(..) => "YuvImage",
TransformBatchKind::AlignedGradient => "AlignedGradient",
TransformBatchKind::AngleGradient => "AngleGradient",
- TransformBatchKind::RadialGradient => "RadialGradient",
TransformBatchKind::BorderCorner => "BorderCorner",
TransformBatchKind::BorderEdge => "BorderEdge",
}
}
fn gpu_sampler_tag(&self) -> GpuProfileTag {
match *self {
TransformBatchKind::TextRun(..) => GPU_TAG_PRIM_TEXT_RUN,
TransformBatchKind::Image(..) => GPU_TAG_PRIM_IMAGE,
- TransformBatchKind::YuvImage(..) => GPU_TAG_PRIM_YUV_IMAGE,
TransformBatchKind::BorderCorner => GPU_TAG_PRIM_BORDER_CORNER,
TransformBatchKind::BorderEdge => GPU_TAG_PRIM_BORDER_EDGE,
TransformBatchKind::AlignedGradient => GPU_TAG_PRIM_GRADIENT,
TransformBatchKind::AngleGradient => GPU_TAG_PRIM_ANGLE_GRADIENT,
- TransformBatchKind::RadialGradient => GPU_TAG_PRIM_RADIAL_GRADIENT,
}
}
}
impl BatchKind {
#[cfg(feature = "debugger")]
fn debug_name(&self) -> &'static str {
match *self {
@@ -232,16 +228,18 @@ impl BatchKind {
BatchKind::Brush(kind) => {
match kind {
BrushBatchKind::Picture(..) => "Brush (Picture)",
BrushBatchKind::Solid => "Brush (Solid)",
BrushBatchKind::Line => "Brush (Line)",
BrushBatchKind::Image(..) => "Brush (Image)",
BrushBatchKind::Blend => "Brush (Blend)",
BrushBatchKind::MixBlend { .. } => "Brush (Composite)",
+ BrushBatchKind::YuvImage(..) => "Brush (YuvImage)",
+ BrushBatchKind::RadialGradient => "Brush (RadialGradient)",
}
}
BatchKind::Transformable(_, batch_kind) => batch_kind.debug_name(),
}
}
fn gpu_sampler_tag(&self) -> GpuProfileTag {
match *self {
@@ -250,16 +248,18 @@ impl BatchKind {
BatchKind::Brush(kind) => {
match kind {
BrushBatchKind::Picture(..) => GPU_TAG_BRUSH_PICTURE,
BrushBatchKind::Solid => GPU_TAG_BRUSH_SOLID,
BrushBatchKind::Line => GPU_TAG_BRUSH_LINE,
BrushBatchKind::Image(..) => GPU_TAG_BRUSH_IMAGE,
BrushBatchKind::Blend => GPU_TAG_BRUSH_BLEND,
BrushBatchKind::MixBlend { .. } => GPU_TAG_BRUSH_MIXBLEND,
+ BrushBatchKind::YuvImage(..) => GPU_TAG_BRUSH_YUV_IMAGE,
+ BrushBatchKind::RadialGradient => GPU_TAG_BRUSH_RADIAL_GRADIENT,
}
}
BatchKind::Transformable(_, batch_kind) => batch_kind.gpu_sampler_tag(),
}
}
}
bitflags! {
@@ -593,17 +593,21 @@ impl CpuProfile {
frame_id,
backend_time_ns,
composite_time_ns,
draw_calls,
}
}
}
-struct RenderTargetPoolId(usize);
+struct ActiveTexture {
+ texture: Texture,
+ saved_index: Option<SavedTargetIndex>,
+ is_shared: bool,
+}
struct SourceTextureResolver {
/// A vector for fast resolves of texture cache IDs to
/// native texture IDs. This maps to a free-list managed
/// by the backend thread / texture cache. We free the
/// texture memory associated with a TextureId when its
/// texture cache ID is freed by the texture cache, but
/// reuse the TextureId when the texture caches's free
@@ -615,22 +619,27 @@ struct SourceTextureResolver {
external_images: FastHashMap<(ExternalImageId, u8), ExternalTexture>,
/// A special 1x1 dummy cache texture used for shaders that expect to work
/// with the cache but are actually running in the first pass
/// when no target is yet provided as a cache texture input.
dummy_cache_texture: Texture,
/// The current cache textures.
- cache_rgba8_texture: Option<Texture>,
- cache_a8_texture: Option<Texture>,
-
- pass_rgba8_textures: FastHashMap<RenderPassIndex, RenderTargetPoolId>,
- pass_a8_textures: FastHashMap<RenderPassIndex, RenderTargetPoolId>,
-
+ cache_rgba8_texture: Option<ActiveTexture>,
+ cache_a8_texture: Option<ActiveTexture>,
+
+ /// An alpha texture shared between all passes.
+ //TODO: just use the standard texture saving logic instead.
+ shared_alpha_texture: Option<Texture>,
+
+ /// Saved cache textures that are to be re-used.
+ saved_textures: Vec<Texture>,
+
+ /// General pool of render targets.
render_target_pool: Vec<Texture>,
}
impl SourceTextureResolver {
fn new(device: &mut Device) -> SourceTextureResolver {
let mut dummy_cache_texture = device
.create_texture(TextureTarget::Array, ImageFormat::BGRA8);
device.init_texture(
@@ -644,18 +653,18 @@ impl SourceTextureResolver {
);
SourceTextureResolver {
cache_texture_map: Vec::new(),
external_images: FastHashMap::default(),
dummy_cache_texture,
cache_a8_texture: None,
cache_rgba8_texture: None,
- pass_rgba8_textures: FastHashMap::default(),
- pass_a8_textures: FastHashMap::default(),
+ shared_alpha_texture: None,
+ saved_textures: Vec::default(),
render_target_pool: Vec::new(),
}
}
fn deinit(self, device: &mut Device) {
device.delete_texture(self.dummy_cache_texture);
for texture in self.cache_texture_map {
@@ -665,125 +674,127 @@ impl SourceTextureResolver {
for texture in self.render_target_pool {
device.delete_texture(texture);
}
}
fn begin_frame(&mut self) {
assert!(self.cache_rgba8_texture.is_none());
assert!(self.cache_a8_texture.is_none());
-
- self.pass_rgba8_textures.clear();
- self.pass_a8_textures.clear();
+ assert!(self.saved_textures.is_empty());
}
- fn end_frame(&mut self, pass_index: RenderPassIndex) {
+ fn end_frame(&mut self) {
// return the cached targets to the pool
- self.end_pass(None, None, pass_index)
+ self.end_pass(None, None);
+ // return the global alpha texture
+ self.render_target_pool.extend(self.shared_alpha_texture.take());
+ // return the saved targets as well
+ self.render_target_pool.extend(self.saved_textures.drain(..));
}
fn end_pass(
&mut self,
- a8_texture: Option<Texture>,
- rgba8_texture: Option<Texture>,
- pass_index: RenderPassIndex,
+ a8_texture: Option<ActiveTexture>,
+ rgba8_texture: Option<ActiveTexture>,
) {
// If we have cache textures from previous pass, return them to the pool.
// Also assign the pool index of those cache textures to last pass's index because this is
// the result of last pass.
- if let Some(texture) = self.cache_rgba8_texture.take() {
- self.pass_rgba8_textures.insert(
- RenderPassIndex(pass_index.0 - 1), RenderTargetPoolId(self.render_target_pool.len()));
- self.render_target_pool.push(texture);
+ // Note: the order here is important, needs to match the logic in `RenderPass::build()`.
+ if let Some(at) = self.cache_rgba8_texture.take() {
+ assert!(!at.is_shared);
+ if let Some(index) = at.saved_index {
+ assert_eq!(self.saved_textures.len(), index.0);
+ self.saved_textures.push(at.texture);
+ } else {
+ self.render_target_pool.push(at.texture);
+ }
}
- if let Some(texture) = self.cache_a8_texture.take() {
- self.pass_a8_textures.insert(
- RenderPassIndex(pass_index.0 - 1), RenderTargetPoolId(self.render_target_pool.len()));
- self.render_target_pool.push(texture);
+ if let Some(at) = self.cache_a8_texture.take() {
+ if let Some(index) = at.saved_index {
+ assert!(!at.is_shared);
+ assert_eq!(self.saved_textures.len(), index.0);
+ self.saved_textures.push(at.texture);
+ } else if at.is_shared {
+ assert!(self.shared_alpha_texture.is_none());
+ self.shared_alpha_texture = Some(at.texture);
+ } else {
+ self.render_target_pool.push(at.texture);
+ }
}
// We have another pass to process, make these textures available
// as inputs to the next pass.
self.cache_rgba8_texture = rgba8_texture;
self.cache_a8_texture = a8_texture;
}
// Bind a source texture to the device.
fn bind(&self, texture_id: &SourceTexture, sampler: TextureSampler, device: &mut Device) {
match *texture_id {
SourceTexture::Invalid => {}
SourceTexture::CacheA8 => {
- let texture = self.cache_a8_texture
- .as_ref()
- .unwrap_or(&self.dummy_cache_texture);
+ let texture = match self.cache_a8_texture {
+ Some(ref at) => &at.texture,
+ None => &self.dummy_cache_texture,
+ };
device.bind_texture(sampler, texture);
}
SourceTexture::CacheRGBA8 => {
- let texture = self.cache_rgba8_texture
- .as_ref()
- .unwrap_or(&self.dummy_cache_texture);
+ let texture = match self.cache_rgba8_texture {
+ Some(ref at) => &at.texture,
+ None => &self.dummy_cache_texture,
+ };
device.bind_texture(sampler, texture);
}
SourceTexture::External(external_image) => {
let texture = self.external_images
.get(&(external_image.id, external_image.channel_index))
.expect(&format!("BUG: External image should be resolved by now"));
device.bind_external_texture(sampler, texture);
}
SourceTexture::TextureCache(index) => {
let texture = &self.cache_texture_map[index.0];
device.bind_texture(sampler, texture);
}
- SourceTexture::RenderTaskCacheRGBA8(pass_index) => {
- let pool_index = self.pass_rgba8_textures
- .get(&pass_index)
- .expect("BUG: pass_index doesn't map to pool_index");
- device.bind_texture(sampler, &self.render_target_pool[pool_index.0])
- }
- SourceTexture::RenderTaskCacheA8(pass_index) => {
- let pool_index = self.pass_a8_textures
- .get(&pass_index)
- .expect("BUG: pass_index doesn't map to pool_index");
- device.bind_texture(sampler, &self.render_target_pool[pool_index.0])
+ SourceTexture::RenderTaskCache(saved_index) => {
+ let texture = &self.saved_textures[saved_index.0];
+ device.bind_texture(sampler, texture)
}
}
}
// Get the real (OpenGL) texture ID for a given source texture.
// For a texture cache texture, the IDs are stored in a vector
// map for fast access.
fn resolve(&self, texture_id: &SourceTexture) -> Option<&Texture> {
match *texture_id {
SourceTexture::Invalid => None,
SourceTexture::CacheA8 => Some(
- self.cache_a8_texture
- .as_ref()
- .unwrap_or(&self.dummy_cache_texture),
+ match self.cache_a8_texture {
+ Some(ref at) => &at.texture,
+ None => &self.dummy_cache_texture,
+ }
),
SourceTexture::CacheRGBA8 => Some(
- self.cache_rgba8_texture
- .as_ref()
- .unwrap_or(&self.dummy_cache_texture),
+ match self.cache_rgba8_texture {
+ Some(ref at) => &at.texture,
+ None => &self.dummy_cache_texture,
+ }
),
SourceTexture::External(..) => {
panic!("BUG: External textures cannot be resolved, they can only be bound.");
}
- SourceTexture::TextureCache(index) => Some(&self.cache_texture_map[index.0]),
- SourceTexture::RenderTaskCacheRGBA8(pass_index) => {
- let pool_index = self.pass_rgba8_textures
- .get(&pass_index)
- .expect("BUG: pass_index doesn't map to pool_index");
- Some(&self.render_target_pool[pool_index.0])
- },
- SourceTexture::RenderTaskCacheA8(pass_index) => {
- let pool_index = self.pass_a8_textures
- .get(&pass_index)
- .expect("BUG: pass_index doesn't map to pool_index");
- Some(&self.render_target_pool[pool_index.0])
- },
+ SourceTexture::TextureCache(index) => {
+ Some(&self.cache_texture_map[index.0])
+ }
+ SourceTexture::RenderTaskCache(saved_index) => {
+ Some(&self.saved_textures[saved_index.0])
+ }
}
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[allow(dead_code)] // SubpixelVariableTextColor is not used at the moment.
@@ -1600,16 +1611,18 @@ pub struct Renderer {
brush_picture_rgba8: BrushShader,
brush_picture_rgba8_alpha_mask: BrushShader,
brush_picture_a8: BrushShader,
brush_solid: BrushShader,
brush_line: BrushShader,
brush_image: Vec<Option<BrushShader>>,
brush_blend: BrushShader,
brush_mix_blend: BrushShader,
+ brush_yuv_image: Vec<Option<BrushShader>>,
+ brush_radial_gradient: BrushShader,
/// These are "cache clip shaders". These shaders are used to
/// draw clip instances into the cached clip mask. The results
/// of these shaders are also used by the primitive shaders.
cs_clip_rectangle: LazilyCompiledShader,
cs_clip_image: LazilyCompiledShader,
cs_clip_border: LazilyCompiledShader,
@@ -1618,22 +1631,20 @@ pub struct Renderer {
// Most draw directly to the framebuffer, but some use inputs
// from the cache shaders to draw. Specifically, the box
// shadow primitive shader stretches the box shadow cache
// output, and the cache_image shader blits the results of
// a cache shader (e.g. blur) to the screen.
ps_text_run: TextShader,
ps_text_run_dual_source: TextShader,
ps_image: Vec<Option<PrimitiveShader>>,
- ps_yuv_image: Vec<Option<PrimitiveShader>>,
ps_border_corner: PrimitiveShader,
ps_border_edge: PrimitiveShader,
ps_gradient: PrimitiveShader,
ps_angle_gradient: PrimitiveShader,
- ps_radial_gradient: PrimitiveShader,
ps_hw_composite: LazilyCompiledShader,
ps_split_composite: LazilyCompiledShader,
max_texture_size: u32,
max_recorded_profiles: usize,
clear_color: Option<ColorF>,
@@ -1740,16 +1751,17 @@ impl Renderer {
gl: Rc<gl::Gl>,
notifier: Box<RenderNotifier>,
mut options: RendererOptions,
) -> Result<(Renderer, RenderApiSender), RendererError> {
let (api_tx, api_rx) = try!{ channel::msg_channel() };
let (payload_tx, payload_rx) = try!{ channel::payload_channel() };
let (result_tx, result_rx) = channel();
let gl_type = gl.get_type();
+ let dithering_feature = ["DITHERING"];
let debug_server = DebugServer::new(api_tx.clone());
let file_watch_handler = FileWatcher {
result_tx: result_tx.clone(),
notifier: notifier.clone(),
};
@@ -1856,16 +1868,27 @@ impl Renderer {
let brush_picture_rgba8_alpha_mask = try!{
BrushShader::new("brush_picture",
&mut device,
&["COLOR_TARGET_ALPHA_MASK"],
options.precache_shaders)
};
+ let brush_radial_gradient = try!{
+ BrushShader::new("brush_radial_gradient",
+ &mut device,
+ if options.enable_dithering {
+ &dithering_feature
+ } else {
+ &[]
+ },
+ options.precache_shaders)
+ };
+
let cs_blur_a8 = try!{
LazilyCompiledShader::new(ShaderKind::Cache(VertexArrayKind::Blur),
"cs_blur",
&["ALPHA_TARGET"],
&mut device,
options.precache_shaders)
};
@@ -1947,20 +1970,20 @@ impl Renderer {
brush_image[buffer_kind] = Some(shader);
}
image_features.clear();
}
// All yuv_image configuration.
let mut yuv_features = Vec::new();
let yuv_shader_num = IMAGE_BUFFER_KINDS.len() * YUV_FORMATS.len() * YUV_COLOR_SPACES.len();
- let mut ps_yuv_image: Vec<Option<PrimitiveShader>> = Vec::new();
+ let mut brush_yuv_image = Vec::new();
// PrimitiveShader is not clonable. Use push() to initialize the vec.
for _ in 0 .. yuv_shader_num {
- ps_yuv_image.push(None);
+ brush_yuv_image.push(None);
}
for buffer_kind in 0 .. IMAGE_BUFFER_KINDS.len() {
if IMAGE_BUFFER_KINDS[buffer_kind].has_platform_support(&gl_type) {
for format_kind in 0 .. YUV_FORMATS.len() {
for color_space_kind in 0 .. YUV_COLOR_SPACES.len() {
let feature_string = IMAGE_BUFFER_KINDS[buffer_kind].get_feature_string();
if feature_string != "" {
yuv_features.push(feature_string);
@@ -1971,27 +1994,27 @@ impl Renderer {
}
let feature_string =
YUV_COLOR_SPACES[color_space_kind].get_feature_string();
if feature_string != "" {
yuv_features.push(feature_string);
}
let shader = try!{
- PrimitiveShader::new("ps_yuv_image",
- &mut device,
- &yuv_features,
- options.precache_shaders)
+ BrushShader::new("brush_yuv_image",
+ &mut device,
+ &yuv_features,
+ options.precache_shaders)
};
let index = Renderer::get_yuv_shader_index(
IMAGE_BUFFER_KINDS[buffer_kind],
YUV_FORMATS[format_kind],
YUV_COLOR_SPACES[color_space_kind],
);
- ps_yuv_image[index] = Some(shader);
+ brush_yuv_image[index] = Some(shader);
yuv_features.clear();
}
}
}
}
let ps_border_corner = try!{
PrimitiveShader::new("ps_border_corner",
@@ -2002,18 +2025,16 @@ impl Renderer {
let ps_border_edge = try!{
PrimitiveShader::new("ps_border_edge",
&mut device,
&[],
options.precache_shaders)
};
- let dithering_feature = ["DITHERING"];
-
let ps_gradient = try!{
PrimitiveShader::new("ps_gradient",
&mut device,
if options.enable_dithering {
&dithering_feature
} else {
&[]
},
@@ -2026,27 +2047,16 @@ impl Renderer {
if options.enable_dithering {
&dithering_feature
} else {
&[]
},
options.precache_shaders)
};
- let ps_radial_gradient = try!{
- PrimitiveShader::new("ps_radial_gradient",
- &mut device,
- if options.enable_dithering {
- &dithering_feature
- } else {
- &[]
- },
- options.precache_shaders)
- };
-
let ps_hw_composite = try!{
LazilyCompiledShader::new(ShaderKind::Primitive,
"ps_hardware_composite",
&[],
&mut device,
options.precache_shaders)
};
@@ -2210,30 +2220,31 @@ impl Renderer {
let recorder = options.recorder;
let thread_listener = Arc::new(options.thread_listener);
let thread_listener_for_rayon_start = thread_listener.clone();
let thread_listener_for_rayon_end = thread_listener.clone();
let workers = options
.workers
.take()
.unwrap_or_else(|| {
- let worker_config = ThreadPoolConfig::new()
+ let worker = ThreadPoolBuilder::new()
.thread_name(|idx|{ format!("WRWorker#{}", idx) })
.start_handler(move |idx| {
register_thread_with_profiler(format!("WRWorker#{}", idx));
if let Some(ref thread_listener) = *thread_listener_for_rayon_start {
thread_listener.thread_started(&format!("WRWorker#{}", idx));
}
})
.exit_handler(move |idx| {
if let Some(ref thread_listener) = *thread_listener_for_rayon_end {
thread_listener.thread_stopped(&format!("WRWorker#{}", idx));
}
- });
- Arc::new(ThreadPool::new(worker_config).unwrap())
+ })
+ .build();
+ Arc::new(worker.unwrap())
});
let enable_render_on_scroll = options.enable_render_on_scroll;
let blob_image_renderer = options.blob_image_renderer.take();
let thread_listener_for_render_backend = thread_listener.clone();
let thread_name = format!("WRRenderBackend#{}", options.renderer_id.unwrap_or(0));
let resource_cache = ResourceCache::new(
texture_cache,
@@ -2285,28 +2296,28 @@ impl Renderer {
brush_picture_rgba8,
brush_picture_rgba8_alpha_mask,
brush_picture_a8,
brush_solid,
brush_line,
brush_image,
brush_blend,
brush_mix_blend,
+ brush_yuv_image,
+ brush_radial_gradient,
cs_clip_rectangle,
cs_clip_border,
cs_clip_image,
ps_text_run,
ps_text_run_dual_source,
ps_image,
- ps_yuv_image,
ps_border_corner,
ps_border_edge,
ps_gradient,
ps_angle_gradient,
- ps_radial_gradient,
ps_hw_composite,
ps_split_composite,
debug: debug_renderer,
debug_flags,
backend_profile_counters: BackendProfileCounters::new(),
profile_counters: RendererProfileCounters::new(),
profiler: Profiler::new(),
max_texture_size: max_texture_size,
@@ -2868,28 +2879,23 @@ impl Renderer {
};
self.device.bind_draw_target(None, None);
self.device.enable_depth_write();
self.device.clear_target(clear_color, clear_depth_value, None);
self.device.disable_depth_write();
}
}
- // Re-use whatever targets possible from the pool, before
- // they get changed/re-allocated by the rendered frames.
- for doc_with_id in &mut active_documents {
- self.prepare_tile_frame(&mut doc_with_id.1.frame);
- }
-
#[cfg(feature = "replay")]
self.texture_resolver.external_images.extend(
self.owned_external_images.iter().map(|(key, value)| (*key, value.clone()))
);
for &mut (_, RenderedDocument { ref mut frame, .. }) in &mut active_documents {
+ frame.profile_counters.reset_targets();
self.prepare_gpu_cache(frame);
assert!(frame.gpu_cache_frame_id <= self.gpu_cache_frame_id);
self.draw_tile_frame(
frame,
framebuffer_size,
clear_depth_value.is_some(),
cpu_frame_id,
@@ -3252,16 +3258,39 @@ impl Renderer {
self.brush_mix_blend.bind(
&mut self.device,
key.blend_mode,
projection,
0,
&mut self.renderer_errors,
);
}
+ BrushBatchKind::RadialGradient => {
+ self.brush_radial_gradient.bind(
+ &mut self.device,
+ key.blend_mode,
+ projection,
+ 0,
+ &mut self.renderer_errors,
+ );
+ }
+ BrushBatchKind::YuvImage(image_buffer_kind, format, color_space) => {
+ let shader_index =
+ Renderer::get_yuv_shader_index(image_buffer_kind, format, color_space);
+ self.brush_yuv_image[shader_index]
+ .as_mut()
+ .expect("Unsupported YUV shader kind")
+ .bind(
+ &mut self.device,
+ key.blend_mode,
+ projection,
+ 0,
+ &mut self.renderer_errors,
+ );
+ }
}
}
BatchKind::Transformable(transform_kind, batch_kind) => match batch_kind {
TransformBatchKind::TextRun(..) => {
unreachable!("bug: text batches are special cased");
}
TransformBatchKind::Image(image_buffer_kind) => {
self.ps_image[image_buffer_kind as usize]
@@ -3270,30 +3299,16 @@ impl Renderer {
.bind(
&mut self.device,
transform_kind,
projection,
0,
&mut self.renderer_errors,
);
}
- TransformBatchKind::YuvImage(image_buffer_kind, format, color_space) => {
- let shader_index =
- Renderer::get_yuv_shader_index(image_buffer_kind, format, color_space);
- self.ps_yuv_image[shader_index]
- .as_mut()
- .expect("Unsupported YUV shader kind")
- .bind(
- &mut self.device,
- transform_kind,
- projection,
- 0,
- &mut self.renderer_errors,
- );
- }
TransformBatchKind::BorderCorner => {
self.ps_border_corner.bind(
&mut self.device,
transform_kind,
projection,
0,
&mut self.renderer_errors,
);
@@ -3320,25 +3335,16 @@ impl Renderer {
self.ps_angle_gradient.bind(
&mut self.device,
transform_kind,
projection,
0,
&mut self.renderer_errors,
);
}
- TransformBatchKind::RadialGradient => {
- self.ps_radial_gradient.bind(
- &mut self.device,
- transform_kind,
- projection,
- 0,
- &mut self.renderer_errors,
- );
- }
},
};
// Handle special case readback for composites.
if let BatchKind::Brush(BrushBatchKind::MixBlend { task_id, source_id, backdrop_id }) = key.kind {
if scissor_rect.is_some() {
self.device.disable_scissor();
}
@@ -4245,99 +4251,89 @@ impl Renderer {
.expect("Found external image, but no handler set!");
for (ext_data, _) in self.texture_resolver.external_images.drain() {
handler.unlock(ext_data.0, ext_data.1);
}
}
}
- fn prepare_target_list<T: RenderTarget>(
+ fn allocate_target_texture<T: RenderTarget>(
&mut self,
list: &mut RenderTargetList<T>,
- perfect_only: bool,
- ) {
+ counters: &mut FrameProfileCounters,
+ frame_id: FrameId,
+ ) -> Option<ActiveTexture> {
debug_assert_ne!(list.max_size, DeviceUintSize::zero());
if list.targets.is_empty() {
- return;
+ return None
}
- let mut texture = if perfect_only {
- debug_assert!(list.texture.is_none());
-
- let selector = TargetSelector {
- size: list.max_size,
- num_layers: list.targets.len() as _,
- format: list.format,
- };
- let index = self.texture_resolver.render_target_pool
+
+ counters.targets_used.inc();
+
+ // First, try finding a perfect match
+ let selector = TargetSelector {
+ size: list.max_size,
+ num_layers: list.targets.len() as _,
+ format: list.format,
+ };
+ let mut index = self.texture_resolver.render_target_pool
+ .iter()
+ .position(|texture| {
+ //TODO: re-use a part of a larger target, if available
+ selector == TargetSelector {
+ size: texture.get_dimensions(),
+ num_layers: texture.get_render_target_layer_count(),
+ format: texture.get_format(),
+ }
+ });
+
+ // Next, try at least finding a matching format
+ if index.is_none() {
+ counters.targets_changed.inc();
+ index = self.texture_resolver.render_target_pool
.iter()
- .position(|texture| {
- selector == TargetSelector {
- size: texture.get_dimensions(),
- num_layers: texture.get_render_target_layer_count(),
- format: texture.get_format(),
- }
- });
- match index {
- Some(pos) => self.texture_resolver.render_target_pool.swap_remove(pos),
- None => return,
+ .position(|texture| texture.get_format() == list.format && !texture.used_in_frame(frame_id));
+ }
+
+ let mut texture = match index {
+ Some(pos) => {
+ self.texture_resolver.render_target_pool.swap_remove(pos)
}
- } else {
- if list.texture.is_some() {
- list.check_ready();
- return
- }
- let index = self.texture_resolver.render_target_pool
- .iter()
- .position(|texture| texture.get_format() == list.format);
- match index {
- Some(pos) => self.texture_resolver.render_target_pool.swap_remove(pos),
- None => self.device.create_texture(TextureTarget::Array, list.format),
+ None => {
+ counters.targets_created.inc();
+ // finally, give up and create a new one
+ self.device.create_texture(TextureTarget::Array, list.format)
}
};
self.device.init_texture(
&mut texture,
list.max_size.width,
list.max_size.height,
TextureFilter::Linear,
Some(RenderTargetInfo {
has_depth: list.needs_depth(),
}),
list.targets.len() as _,
None,
);
- list.texture = Some(texture);
- list.check_ready();
- }
-
- fn prepare_tile_frame(&mut self, frame: &mut Frame) {
- // Init textures and render targets to match this scene.
- // First pass grabs all the perfectly matching targets from the pool.
- for pass in &mut frame.passes {
- if let RenderPassKind::OffScreen { ref mut alpha, ref mut color, .. } = pass.kind {
- self.prepare_target_list(alpha, true);
- self.prepare_target_list(color, true);
- }
- }
+
+ list.check_ready(&texture);
+ Some(ActiveTexture {
+ texture,
+ saved_index: list.saved_index.clone(),
+ is_shared: list.is_shared,
+ })
}
fn bind_frame_data(&mut self, frame: &mut Frame) {
let _timer = self.gpu_profile.start_timer(GPU_TAG_SETUP_DATA);
self.device.set_device_pixel_ratio(frame.device_pixel_ratio);
- // Some of the textures are already assigned by `prepare_frame`.
- // Now re-allocate the space for the rest of the target textures.
- for pass in &mut frame.passes {
- if let RenderPassKind::OffScreen { ref mut alpha, ref mut color, .. } = pass.kind {
- self.prepare_target_list(alpha, false);
- self.prepare_target_list(color, false);
- }
- }
-
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(
&mut self.device,
&mut frame.clip_chain_local_clip_rects
);
self.device.bind_texture(
@@ -4419,18 +4415,18 @@ impl Renderer {
frame_id,
stats,
);
}
(None, None)
}
RenderPassKind::OffScreen { ref mut alpha, ref mut color, ref mut texture_cache } => {
- alpha.check_ready();
- color.check_ready();
+ let alpha_tex = self.allocate_target_texture(alpha, &mut frame.profile_counters, frame_id);
+ let color_tex = self.allocate_target_texture(color, &mut frame.profile_counters, frame_id);
// If this frame has already been drawn, then any texture
// cache targets have already been updated and can be
// skipped this time.
if !frame.has_been_rendered {
for (&(texture_id, target_index), target) in texture_cache {
self.draw_texture_cache_target(
&texture_id,
@@ -4450,17 +4446,17 @@ impl Renderer {
alpha.max_size.width as f32,
0.0,
alpha.max_size.height as f32,
ORTHO_NEAR_PLANE,
ORTHO_FAR_PLANE,
);
self.draw_alpha_target(
- (alpha.texture.as_ref().unwrap(), target_index as i32),
+ (&alpha_tex.as_ref().unwrap().texture, target_index as i32),
target,
alpha.max_size,
&projection,
&frame.render_tasks,
stats,
);
}
@@ -4472,52 +4468,46 @@ impl Renderer {
color.max_size.width as f32,
0.0,
color.max_size.height as f32,
ORTHO_NEAR_PLANE,
ORTHO_FAR_PLANE,
);
self.draw_color_target(
- Some((color.texture.as_ref().unwrap(), target_index as i32)),
+ Some((&color_tex.as_ref().unwrap().texture, target_index as i32)),
target,
frame.inner_rect,
color.max_size,
false,
Some([0.0, 0.0, 0.0, 0.0]),
&frame.render_tasks,
&projection,
frame_id,
stats,
);
}
- (alpha.texture.take(), color.texture.take())
+ (alpha_tex, color_tex)
}
};
+ //Note: the `end_pass` will make sure this texture is not recycled this frame
+ if let Some(ActiveTexture { ref texture, is_shared: true, .. }) = cur_alpha {
+ self.device
+ .bind_texture(TextureSampler::SharedCacheA8, texture);
+ }
+
self.texture_resolver.end_pass(
cur_alpha,
cur_color,
- RenderPassIndex(pass_index),
);
-
- // After completing the first pass, make the A8 target available as an
- // input to any subsequent passes.
- if pass_index == 0 {
- if let Some(shared_alpha_texture) =
- self.texture_resolver.resolve(&SourceTexture::CacheA8)
- {
- self.device
- .bind_texture(TextureSampler::SharedCacheA8, shared_alpha_texture);
- }
- }
}
- self.texture_resolver.end_frame(RenderPassIndex(frame.passes.len()));
+ self.texture_resolver.end_frame();
if let Some(framebuffer_size) = framebuffer_size {
self.draw_render_target_debug(framebuffer_size);
self.draw_texture_cache_debug(framebuffer_size);
}
self.draw_epoch_debug();
// Garbage collect any frame outputs that weren't used this frame.
let device = &mut self.device;
@@ -4745,44 +4735,44 @@ impl Renderer {
self.brush_mask_corner.deinit(&mut self.device);
self.brush_picture_rgba8.deinit(&mut self.device);
self.brush_picture_rgba8_alpha_mask.deinit(&mut self.device);
self.brush_picture_a8.deinit(&mut self.device);
self.brush_solid.deinit(&mut self.device);
self.brush_line.deinit(&mut self.device);
self.brush_blend.deinit(&mut self.device);
self.brush_mix_blend.deinit(&mut self.device);
+ self.brush_radial_gradient.deinit(&mut self.device);
self.cs_clip_rectangle.deinit(&mut self.device);
self.cs_clip_image.deinit(&mut self.device);
self.cs_clip_border.deinit(&mut self.device);
self.ps_text_run.deinit(&mut self.device);
self.ps_text_run_dual_source.deinit(&mut self.device);
for shader in self.brush_image {
if let Some(shader) = shader {
shader.deinit(&mut self.device);
}
}
for shader in self.ps_image {
if let Some(shader) = shader {
shader.deinit(&mut self.device);
}
}
- for shader in self.ps_yuv_image {
+ for shader in self.brush_yuv_image {
if let Some(shader) = shader {
shader.deinit(&mut self.device);
}
}
for (_, target) in self.output_targets {
self.device.delete_fbo(target.fbo_id);
}
self.ps_border_corner.deinit(&mut self.device);
self.ps_border_edge.deinit(&mut self.device);
self.ps_gradient.deinit(&mut self.device);
self.ps_angle_gradient.deinit(&mut self.device);
- self.ps_radial_gradient.deinit(&mut self.device);
self.ps_hw_composite.deinit(&mut self.device);
self.ps_split_composite.deinit(&mut self.device);
#[cfg(feature = "capture")]
self.device.delete_fbo(self.read_fbo);
#[cfg(feature = "replay")]
for (_, ext) in self.owned_external_images {
self.device.delete_external_texture(ext);
}
--- a/gfx/webrender/src/scene.rs
+++ b/gfx/webrender/src/scene.rs
@@ -192,20 +192,21 @@ impl FilterOpHelpers for FilterOp {
FilterOp::Invert(amount) => amount == 0.0,
FilterOp::Opacity(_, amount) => amount >= 1.0,
FilterOp::Saturate(amount) => amount == 1.0,
FilterOp::Sepia(amount) => amount == 0.0,
FilterOp::DropShadow(offset, blur, _) => {
offset.x == 0.0 && offset.y == 0.0 && blur == 0.0
},
FilterOp::ColorMatrix(matrix) => {
- matrix == [1.0, 0.0, 0.0, 0.0, 0.0,
- 0.0, 1.0, 0.0, 0.0, 0.0,
- 0.0, 0.0, 1.0, 0.0, 0.0,
- 0.0, 0.0, 0.0, 1.0, 0.0]
+ matrix == [1.0, 0.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0, 0.0,
+ 0.0, 0.0, 1.0, 0.0,
+ 0.0, 0.0, 0.0, 1.0,
+ 0.0, 0.0, 0.0, 0.0]
}
}
}
}
pub trait StackingContextHelpers {
fn mix_blend_mode_for_compositing(&self) -> Option<MixBlendMode>;
fn filter_ops_for_compositing(
--- a/gfx/webrender/src/tiling.rs
+++ b/gfx/webrender/src/tiling.rs
@@ -6,20 +6,20 @@ use api::{ClipId, ColorF, DeviceIntPoint
use api::{DevicePixelScale, DeviceUintPoint, DeviceUintRect, DeviceUintSize};
use api::{DocumentLayer, FilterOp, ImageFormat};
use api::{LayerRect, MixBlendMode, PipelineId};
use batch::{AlphaBatchBuilder, AlphaBatchContainer, ClipBatcher, resolve_image};
use clip::{ClipStore};
use clip_scroll_tree::{ClipScrollTree};
use device::{FrameId, Texture};
use gpu_cache::{GpuCache};
-use gpu_types::{BlurDirection, BlurInstance, BrushInstance, ClipChainRectIndex};
+use gpu_types::{BlurDirection, BlurInstance, BrushFlags, BrushInstance, ClipChainRectIndex};
use gpu_types::{ClipScrollNodeData, ClipScrollNodeIndex};
use gpu_types::{PrimitiveInstance};
-use internal_types::{FastHashMap, RenderPassIndex, SourceTexture};
+use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture};
use picture::{PictureKind};
use prim_store::{PrimitiveIndex, PrimitiveKind, PrimitiveStore};
use prim_store::{BrushMaskKind, BrushKind, DeferredResolve, EdgeAaSegmentMask};
use profiler::FrameProfileCounters;
use render_task::{BlitSource, RenderTaskAddress, RenderTaskId, RenderTaskKind};
use render_task::{BlurTask, ClearMode, RenderTaskLocation, RenderTaskTree};
use resource_cache::ResourceCache;
use std::{cmp, usize, f32, i32};
@@ -132,41 +132,46 @@ pub enum RenderTargetKind {
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct RenderTargetList<T> {
screen_size: DeviceIntSize,
pub format: ImageFormat,
pub max_size: DeviceUintSize,
pub targets: Vec<T>,
- #[cfg_attr(feature = "serde", serde(skip))]
- pub texture: Option<Texture>,
+ pub saved_index: Option<SavedTargetIndex>,
+ pub is_shared: bool,
}
impl<T: RenderTarget> RenderTargetList<T> {
fn new(
screen_size: DeviceIntSize,
format: ImageFormat,
) -> Self {
RenderTargetList {
screen_size,
format,
max_size: DeviceUintSize::new(MIN_TARGET_SIZE, MIN_TARGET_SIZE),
targets: Vec::new(),
- texture: None,
+ saved_index: None,
+ is_shared: false,
}
}
fn build(
&mut self,
ctx: &RenderTargetContext,
gpu_cache: &mut GpuCache,
render_tasks: &mut RenderTaskTree,
deferred_resolves: &mut Vec<DeferredResolve>,
+ saved_index: Option<SavedTargetIndex>,
) {
+ 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);
}
}
fn add_task(
&mut self,
task_id: RenderTaskId,
@@ -209,30 +214,23 @@ impl<T: RenderTarget> RenderTargetList<T
(origin, RenderTargetIndex(self.targets.len() - 1))
}
pub fn needs_depth(&self) -> bool {
self.targets.iter().any(|target| target.needs_depth())
}
- pub fn check_ready(&self) {
- match self.texture {
- Some(ref t) => {
- assert_eq!(t.get_dimensions(), self.max_size);
- assert_eq!(t.get_format(), self.format);
- assert_eq!(t.get_render_target_layer_count(), self.targets.len());
- assert_eq!(t.get_layer_count() as usize, self.targets.len());
- assert_eq!(t.has_depth(), t.get_rt_info().unwrap().has_depth);
- assert_eq!(t.has_depth(), self.needs_depth());
- }
- None => {
- assert!(self.targets.is_empty())
- }
- }
+ pub fn check_ready(&self, t: &Texture) {
+ assert_eq!(t.get_dimensions(), self.max_size);
+ assert_eq!(t.get_format(), self.format);
+ assert_eq!(t.get_render_target_layer_count(), self.targets.len());
+ assert_eq!(t.get_layer_count() as usize, self.targets.len());
+ assert_eq!(t.has_depth(), t.get_rt_info().unwrap().has_depth);
+ assert_eq!(t.has_depth(), self.needs_depth());
}
}
/// Frame output information for a given pipeline ID.
/// Storing the task ID allows the renderer to find
/// the target rect within the render target that this
/// pipeline exists at.
#[cfg_attr(feature = "capture", derive(Serialize))]
@@ -584,25 +582,28 @@ impl RenderTarget for AlphaRenderTarget
// tasks support clip masks and
// transform primitives, these
// will need to be filled out!
clip_chain_rect_index: ClipChainRectIndex(0),
scroll_id: ClipScrollNodeIndex(0),
clip_task_address: RenderTaskAddress(0),
z: 0,
segment_index: 0,
+ brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
edge_flags: EdgeAaSegmentMask::empty(),
user_data: [0; 3],
};
let brush = &ctx.prim_store.cpu_brushes[sub_metadata.cpu_prim_index.0];
let batch = match brush.kind {
BrushKind::Solid { .. } |
BrushKind::Clear |
BrushKind::Picture |
BrushKind::Line { .. } |
+ BrushKind::YuvImage { .. } |
+ BrushKind::RadialGradient { .. } |
BrushKind::Image { .. } => {
unreachable!("bug: unexpected brush here");
}
BrushKind::Mask { ref kind, .. } => {
match *kind {
BrushMaskKind::Corner(..) => &mut self.brush_mask_corners,
BrushMaskKind::RoundedRect(..) => &mut self.brush_mask_rounded_rects,
}
@@ -779,47 +780,67 @@ impl RenderPass {
pub fn build(
&mut self,
ctx: &RenderTargetContext,
gpu_cache: &mut GpuCache,
render_tasks: &mut RenderTaskTree,
deferred_resolves: &mut Vec<DeferredResolve>,
clip_store: &ClipStore,
- pass_index: RenderPassIndex,
) {
profile_scope!("RenderPass::build");
match self.kind {
RenderPassKind::MainFramebuffer(ref mut target) => {
for &task_id in &self.tasks {
assert_eq!(render_tasks[task_id].target_kind(), RenderTargetKind::Color);
- render_tasks[task_id].pass_index = Some(pass_index);
target.add_task(
task_id,
ctx,
gpu_cache,
render_tasks,
clip_store,
deferred_resolves,
);
}
target.build(ctx, gpu_cache, render_tasks, deferred_resolves);
}
RenderPassKind::OffScreen { ref mut color, ref mut alpha, ref mut texture_cache } => {
+ let is_shared_alpha = self.tasks.iter().any(|&task_id| {
+ match render_tasks[task_id].kind {
+ RenderTaskKind::CacheMask(..) => true,
+ _ => false,
+ }
+ });
+ let saved_color = if self.tasks.iter().any(|&task_id| {
+ let t = &render_tasks[task_id];
+ t.target_kind() == RenderTargetKind::Color && t.saved_index.is_some()
+ }) {
+ Some(render_tasks.save_target())
+ } else {
+ None
+ };
+ let saved_alpha = if self.tasks.iter().any(|&task_id| {
+ let t = &render_tasks[task_id];
+ t.target_kind() == RenderTargetKind::Alpha && t.saved_index.is_some()
+ }) {
+ Some(render_tasks.save_target())
+ } else {
+ None
+ };
+
// Step through each task, adding to batches as appropriate.
for &task_id in &self.tasks {
let (target_kind, texture_target) = {
let task = &mut render_tasks[task_id];
- task.pass_index = Some(pass_index);
let target_kind = task.target_kind();
// Find a target to assign this task to, or create a new
// one if required.
- match task.location {
+ let (target_kind, texture_target) = match task.location {
RenderTaskLocation::TextureCache(texture_id, layer, _) => {
// TODO(gw): When we support caching color items, we will
// need to calculate that here to get the
// correct target kind.
(RenderTargetKind::Alpha, Some((texture_id, layer)))
}
RenderTaskLocation::Fixed(..) => {
(RenderTargetKind::Color, None)
@@ -829,17 +850,28 @@ impl RenderPass {
let (alloc_origin, target_index) = match target_kind {
RenderTargetKind::Color => color.allocate(alloc_size),
RenderTargetKind::Alpha => alpha.allocate(alloc_size),
};
*origin = Some((alloc_origin.to_i32(), target_index));
(target_kind, None)
}
+ };
+
+ // Replace the pending saved index with a real one
+ if let Some(index) = task.saved_index {
+ assert_eq!(index, SavedTargetIndex::PENDING);
+ task.saved_index = match target_kind {
+ RenderTargetKind::Color => saved_color,
+ RenderTargetKind::Alpha => saved_alpha,
+ };
}
+
+ (target_kind, texture_target)
};
match texture_target {
Some(texture_target) => {
let texture = texture_cache
.entry(texture_target)
.or_insert(
TextureCacheRenderTarget::new(None, DeviceIntSize::zero())
@@ -864,18 +896,19 @@ impl RenderPass {
clip_store,
deferred_resolves,
),
}
}
}
}
- color.build(ctx, gpu_cache, render_tasks, deferred_resolves);
- alpha.build(ctx, gpu_cache, render_tasks, deferred_resolves);
+ color.build(ctx, gpu_cache, render_tasks, deferred_resolves, saved_color);
+ alpha.build(ctx, gpu_cache, render_tasks, deferred_resolves, saved_alpha);
+ alpha.is_shared = is_shared_alpha;
}
}
}
}
#[derive(Debug, Clone, Default)]
pub struct CompositeOps {
// Requires only a single texture as input (e.g. most filters)
--- a/gfx/webrender/tests/angle_shader_validation.rs
+++ b/gfx/webrender/tests/angle_shader_validation.rs
@@ -58,41 +58,37 @@ const SHADERS: &[Shader] = &[
name: "ps_gradient",
features: PRIM_FEATURES,
},
Shader {
name: "ps_angle_gradient",
features: PRIM_FEATURES,
},
Shader {
- name: "ps_radial_gradient",
- features: PRIM_FEATURES,
- },
- Shader {
name: "ps_hardware_composite",
features: PRIM_FEATURES,
},
Shader {
name: "ps_split_composite",
features: PRIM_FEATURES,
},
Shader {
name: "ps_image",
features: PRIM_FEATURES,
},
Shader {
- name: "ps_yuv_image",
- features: PRIM_FEATURES,
- },
- Shader {
name: "ps_text_run",
features: PRIM_FEATURES,
},
// Brush shaders
Shader {
+ name: "brush_yuv_image",
+ features: &["", "YUV_NV12", "YUV_PLANAR", "YUV_INTERLEAVED"],
+ },
+ Shader {
name: "brush_mask",
features: &[],
},
Shader {
name: "brush_solid",
features: &[],
},
Shader {
@@ -106,16 +102,20 @@ const SHADERS: &[Shader] = &[
Shader {
name: "brush_composite",
features: &[],
},
Shader {
name: "brush_line",
features: &[],
},
+ Shader {
+ name: "brush_radial_gradient",
+ features: &[],
+ },
];
const VERSION_STRING: &str = "#version 300 es\n";
#[test]
fn validate_shaders() {
angle::hl::initialize().unwrap();
--- a/gfx/webrender_api/src/api.rs
+++ b/gfx/webrender_api/src/api.rs
@@ -4,17 +4,17 @@
use app_units::Au;
use channel::{self, MsgSender, Payload, PayloadSender, PayloadSenderHelperMethods};
use std::cell::Cell;
use std::fmt;
use std::marker::PhantomData;
use std::path::PathBuf;
use std::u32;
-use {BuiltDisplayList, BuiltDisplayListDescriptor, ClipId, ColorF, DeviceIntPoint, DeviceUintRect};
+use {BuiltDisplayList, BuiltDisplayListDescriptor, ColorF, DeviceIntPoint, DeviceUintRect};
use {DeviceUintSize, ExternalScrollId, FontInstanceKey, FontInstanceOptions};
use {FontInstancePlatformOptions, FontKey, FontVariation, GlyphDimensions, GlyphKey, ImageData};
use {ImageDescriptor, ImageKey, ItemTag, LayoutPoint, LayoutSize, LayoutTransform, LayoutVector2D};
use {NativeFontHandle, WorldPoint};
pub type TileSize = u16;
/// Documents are rendered in the ascending order of their associated layer values.
pub type DocumentLayer = i8;
@@ -249,17 +249,17 @@ impl Transaction {
phase: ScrollEventPhase,
) {
self.ops.push(DocumentMsg::Scroll(scroll_location, cursor, phase));
}
pub fn scroll_node_with_id(
&mut self,
origin: LayoutPoint,
- id: ScrollNodeIdType,
+ id: ExternalScrollId,
clamp: ScrollClamping,
) {
self.ops.push(DocumentMsg::ScrollNodeWithId(origin, id, clamp));
}
pub fn set_page_zoom(&mut self, page_zoom: ZoomFactor) {
self.ops.push(DocumentMsg::SetPageZoom(page_zoom));
}
@@ -379,47 +379,23 @@ pub enum DocumentMsg {
RemovePipeline(PipelineId),
EnableFrameOutput(PipelineId, bool),
SetWindowParameters {
window_size: DeviceUintSize,
inner_rect: DeviceUintRect,
device_pixel_ratio: f32,
},
Scroll(ScrollLocation, WorldPoint, ScrollEventPhase),
- ScrollNodeWithId(LayoutPoint, ScrollNodeIdType, ScrollClamping),
+ ScrollNodeWithId(LayoutPoint, ExternalScrollId, ScrollClamping),
TickScrollingBounce,
GetScrollNodeState(MsgSender<Vec<ScrollNodeState>>),
GenerateFrame,
UpdateDynamicProperties(DynamicProperties),
}
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
-pub enum ScrollNodeIdType {
- ExternalScrollId(ExternalScrollId),
- ClipId(ClipId),
-}
-
-impl From<ExternalScrollId> for ScrollNodeIdType {
- fn from(id: ExternalScrollId) -> Self {
- ScrollNodeIdType::ExternalScrollId(id)
- }
-}
-
-impl From<ClipId> for ScrollNodeIdType {
- fn from(id: ClipId) -> Self {
- ScrollNodeIdType::ClipId(id)
- }
-}
-
-impl<'a> From<&'a ClipId> for ScrollNodeIdType {
- fn from(id: &'a ClipId) -> Self {
- ScrollNodeIdType::ClipId(*id)
- }
-}
-
impl fmt::Debug for DocumentMsg {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match *self {
DocumentMsg::SetDisplayList { .. } => "DocumentMsg::SetDisplayList",
DocumentMsg::HitTest(..) => "DocumentMsg::HitTest",
DocumentMsg::SetPageZoom(..) => "DocumentMsg::SetPageZoom",
DocumentMsg::SetPinchZoom(..) => "DocumentMsg::SetPinchZoom",
DocumentMsg::SetPan(..) => "DocumentMsg::SetPan",
--- a/gfx/webrender_api/src/display_item.rs
+++ b/gfx/webrender_api/src/display_item.rs
@@ -596,19 +596,19 @@ impl YuvFormat {
YuvFormat::NV12 => 2,
YuvFormat::PlanarYCbCr => 3,
YuvFormat::InterleavedYCbCr => 1,
}
}
pub fn get_feature_string(&self) -> &'static str {
match *self {
- YuvFormat::NV12 => "NV12",
- YuvFormat::PlanarYCbCr => "",
- YuvFormat::InterleavedYCbCr => "INTERLEAVED_Y_CB_CR",
+ YuvFormat::NV12 => "YUV_NV12",
+ YuvFormat::PlanarYCbCr => "YUV_PLANAR",
+ YuvFormat::InterleavedYCbCr => "YUV_INTERLEAVED",
}
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub struct ImageMask {
pub image: ImageKey,
@@ -644,16 +644,32 @@ impl LocalClip {
ComplexClipRegion {
rect: complex.rect.translate(offset),
radii: complex.radii,
mode: complex.mode,
},
),
}
}
+
+ pub fn clip_by(&self, rect: &LayoutRect) -> LocalClip {
+ match *self {
+ LocalClip::Rect(clip_rect) => {
+ LocalClip::Rect(
+ clip_rect.intersection(rect).unwrap_or(LayoutRect::zero())
+ )
+ }
+ LocalClip::RoundedRect(clip_rect, complex) => {
+ LocalClip::RoundedRect(
+ clip_rect.intersection(rect).unwrap_or(LayoutRect::zero()),
+ complex,
+ )
+ }
+ }
+ }
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum ClipMode {
Clip, // Pixels inside the region are visible.
ClipOut, // Pixels outside the region are visible.
}
--- a/gfx/webrender_bindings/Cargo.toml
+++ b/gfx/webrender_bindings/Cargo.toml
@@ -1,16 +1,16 @@
[package]
name = "webrender_bindings"
version = "0.1.0"
authors = ["The Mozilla Project Developers"]
license = "MPL-2.0"
[dependencies]
-rayon = "0.8"
+rayon = "1"
thread_profiler = "0.1.1"
euclid = "0.16"
app_units = "0.6"
gleam = "0.4.20"
log = "0.4"
[dependencies.webrender]
path = "../webrender"
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-4af31b8aa79d5a1f3c0242dea6be4876c71075c5
+e8d2ffb404a85651fe08a6d09abbece9bd2b9182
--- a/gfx/wrench/src/wrench.rs
+++ b/gfx/wrench/src/wrench.rs
@@ -497,17 +497,17 @@ impl Wrench {
pub fn begin_frame(&mut self) {
self.frame_start_sender.push(time::SteadyTime::now());
}
pub fn send_lists(
&mut self,
frame_number: u32,
display_lists: Vec<(PipelineId, LayerSize, BuiltDisplayList)>,
- scroll_offsets: &HashMap<ClipId, LayerPoint>,
+ scroll_offsets: &HashMap<ExternalScrollId, LayerPoint>,
) {
let root_background_color = Some(ColorF::new(1.0, 1.0, 1.0, 1.0));
let mut txn = Transaction::new();
for display_list in display_lists {
txn.set_display_list(
Epoch(frame_number),
root_background_color,
@@ -518,17 +518,17 @@ impl Wrench {
}
// TODO(nical) - Need to separate the set_display_list from the scrolling
// operations into separate transactions for mysterious -but probably related
// to the other comment below- reasons.
self.api.send_transaction(self.document_id, txn);
let mut txn = Transaction::new();
for (id, offset) in scroll_offsets {
- txn.scroll_node_with_id(*offset, id.into(), ScrollClamping::NoClamping);
+ txn.scroll_node_with_id(*offset, *id, ScrollClamping::NoClamping);
}
// TODO(nical) - Wrench does not notify frames when there was scrolling
// in the transaction (See RenderNotifier implementations). If we don't
// generate a frame after scrolling, wrench just stops and some tests
// will time out.
// I suppose this was to avoid taking the snapshot after scrolling if
// there was other updates coming in a subsequent messages but it's very
// error-prone with transactions.
--- a/gfx/wrench/src/yaml_frame_reader.rs
+++ b/gfx/wrench/src/yaml_frame_reader.rs
@@ -188,17 +188,17 @@ pub struct YamlFrameReader {
include_only: Vec<String>,
watch_source: bool,
list_resources: bool,
/// A HashMap of offsets which specify what scroll offsets particular
/// scroll layers should be initialized with.
- scroll_offsets: HashMap<ClipId, LayerPoint>,
+ scroll_offsets: HashMap<ExternalScrollId, LayerPoint>,
image_map: HashMap<(PathBuf, Option<i64>), (ImageKey, LayoutSize)>,
fonts: HashMap<FontDescriptor, FontKey>,
font_instances: HashMap<(FontKey, Au, FontInstanceFlags), FontInstanceKey>,
font_render_mode: Option<FontRenderMode>,
/// A HashMap that allows specifying a numeric id for clip and clip chains in YAML
@@ -1268,17 +1268,22 @@ impl YamlFrameReader {
}
pub fn add_display_list_items_from_yaml(
&mut self,
dl: &mut DisplayListBuilder,
wrench: &mut Wrench,
yaml: &Yaml,
) {
- let full_clip = LayoutRect::new(LayoutPoint::zero(), wrench.window_size_f32());
+ // A very large number (but safely far away from finite limits of f32)
+ let big_number = 1.0e30;
+ // A rect that should in practical terms serve as a no-op for clipping
+ let full_clip = LayoutRect::new(
+ LayoutPoint::new(-big_number / 2.0, -big_number / 2.0),
+ LayoutSize::new(big_number, big_number));
for item in yaml.as_vec().unwrap() {
// an explicit type can be skipped with some shorthand
let item_type = if !item["rect"].is_badvalue() {
"rect"
} else if !item["image"].is_badvalue() {
"image"
} else if !item["text"].is_badvalue() {
@@ -1357,33 +1362,34 @@ impl YamlFrameReader {
let content_size = yaml["content-size"].as_size().unwrap_or(clip_rect.size);
let content_rect = LayerRect::new(clip_rect.origin, content_size);
let numeric_id = yaml["id"].as_i64().map(|id| id as u64);
let complex_clips = self.to_complex_clip_regions(&yaml["complex"]);
let image_mask = self.to_image_mask(&yaml["image-mask"], wrench);
- let external_id = numeric_id.map(|id| ExternalScrollId(id as u64, dl.pipeline_id));
+ let external_id = yaml["scroll-offset"].as_point().map(|size| {
+ let id = ExternalScrollId((self.scroll_offsets.len() + 1) as u64, dl.pipeline_id);
+ self.scroll_offsets.insert(id, LayerPoint::new(size.x, size.y));
+ id
+ });
+
let real_id = dl.define_scroll_frame(
external_id,
content_rect,
clip_rect,
complex_clips,
image_mask,
ScrollSensitivity::Script,
);
if let Some(numeric_id) = numeric_id {
self.clip_id_map.insert(numeric_id, real_id);
}
- if let Some(size) = yaml["scroll-offset"].as_point() {
- self.scroll_offsets.insert(real_id, LayerPoint::new(size.x, size.y));
- }
-
if !yaml["items"].is_badvalue() {
dl.push_clip_id(real_id);
self.add_display_list_items_from_yaml(dl, wrench, &yaml["items"]);
dl.pop_clip_id();
}
}
pub fn handle_sticky_frame(
@@ -1537,19 +1543,18 @@ impl YamlFrameReader {
.as_mix_blend_mode()
.unwrap_or(MixBlendMode::Normal);
let scroll_policy = yaml["scroll-policy"]
.as_scroll_policy()
.unwrap_or(ScrollPolicy::Scrollable);
if is_root {
if let Some(size) = yaml["scroll-offset"].as_point() {
- let id = ClipId::root_scroll_node(dl.pipeline_id);
- self.scroll_offsets
- .insert(id, LayerPoint::new(size.x, size.y));
+ let external_id = ExternalScrollId(0, dl.pipeline_id);
+ self.scroll_offsets.insert(external_id, LayerPoint::new(size.x, size.y));
}
}
let filters = yaml["filters"].as_vec_filter_op().unwrap_or(vec![]);
info.rect = bounds;
info.local_clip = LocalClip::from(bounds);
dl.push_stacking_context(