--- a/gfx/webrender/Cargo.toml
+++ b/gfx/webrender/Cargo.toml
@@ -1,57 +1,78 @@
[package]
name = "webrender"
-version = "0.57.0"
+version = "0.57.2"
authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
license = "MPL-2.0"
repository = "https://github.com/servo/webrender"
build = "build.rs"
[features]
default = ["freetype-lib"]
freetype-lib = ["freetype/servo-freetype-sys"]
-profiler = ["thread_profiler/thread_profiler"]
-debugger = ["ws", "serde_json", "serde", "image", "base64"]
-capture = ["webrender_api/serialize", "ron", "serde"]
+profiler = ["thread_profiler/thread_profiler", "debug_renderer"]
+debugger = ["ws", "serde_json", "serde", "image", "base64", "debug_renderer"]
+capture = ["webrender_api/serialize", "ron", "serde", "debug_renderer"]
replay = ["webrender_api/deserialize", "ron", "serde"]
+debug_renderer = []
+pathfinder = ["pathfinder_font_renderer", "pathfinder_gfx_utils", "pathfinder_partitioner", "pathfinder_path_utils"]
[dependencies]
app_units = "0.6"
byteorder = "1.0"
bincode = "1.0"
euclid = "0.17"
fxhash = "0.2.1"
gleam = "0.4.20"
lazy_static = "1"
log = "0.4"
-num-traits = "0.1.32"
+num-traits = "0.1.43"
time = "0.1"
rayon = "1"
webrender_api = {path = "../webrender_api"}
bitflags = "1.0"
thread_profiler = "0.1.1"
plane-split = "0.8"
png = { optional = true, version = "0.11" }
smallvec = "0.6"
ws = { optional = true, version = "0.7.3" }
serde_json = { optional = true, version = "1.0" }
serde = { optional = true, version = "1.0", features = ["serde_derive"] }
image = { optional = true, version = "0.18" }
base64 = { optional = true, version = "0.6" }
ron = { optional = true, version = "0.1.7" }
+cfg-if = "0.1.2"
+
+[dependencies.pathfinder_font_renderer]
+git = "https://github.com/pcwalton/pathfinder"
+optional = true
+# Uncomment to test FreeType on macOS:
+# features = ["freetype"]
+
+[dependencies.pathfinder_gfx_utils]
+git = "https://github.com/pcwalton/pathfinder"
+optional = true
+
+[dependencies.pathfinder_partitioner]
+git = "https://github.com/pcwalton/pathfinder"
+optional = true
+
+[dependencies.pathfinder_path_utils]
+git = "https://github.com/pcwalton/pathfinder"
+optional = true
[dev-dependencies]
mozangle = "0.1"
env_logger = "0.5"
rand = "0.3" # for the benchmarks
-glutin = "0.12" # for the example apps
+glutin = "0.13" # for the example apps
[target.'cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))'.dependencies]
-freetype = { version = "0.3", default-features = false }
+freetype = { version = "0.4", default-features = false }
[target.'cfg(target_os = "windows")'.dependencies]
dwrote = "0.4.1"
[target.'cfg(target_os = "macos")'.dependencies]
core-foundation = "0.5"
core-graphics = "0.13"
core-text = { version = "9.2.0", default-features = false }
--- a/gfx/webrender/doc/text-rendering.md
+++ b/gfx/webrender/doc/text-rendering.md
@@ -133,17 +133,17 @@ for subpixel text, the text mask contain
Regular painting uses four values per pixel: three color values, and one alpha value. The alpha value applies to all components of the pixel equally.
Imagine for a second a world in which you have *three alpha values per pixel*, one for each color component.
- Old world: Each pixel has four values: `color.r`, `color.g`, `color.b`, and `color.a`.
- New world: Each pixel has *six* values: `color.r`, `color.a_r`, `color.g`, `color.a_g`, `color.b`, and `color.a_b`.
-In such a world we can define a component-alpha-aware opererator "over":
+In such a world we can define a component-alpha-aware operator "over":
```glsl
vec6 over_comp(vec6 src, vec6 dest) {
vec6 result;
result.r = src.r + (1.0 - src.a_r) * dest.r;
result.g = src.g + (1.0 - src.a_g) * dest.g;
result.b = src.b + (1.0 - src.a_b) * dest.b;
result.a_r = src.a_r + (1.0 - src.a_r) * dest.a_r;
--- a/gfx/webrender/examples/blob.rs
+++ b/gfx/webrender/examples/blob.rs
@@ -19,18 +19,18 @@ use std::sync::mpsc::{Receiver, Sender,
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.
// The deserialized command list internally used by this example is just a color.
type ImageRenderingCommands = api::ColorU;
-// Serialize/deserialze the blob.
-// Ror real usecases you should probably use serde rather than doing it by hand.
+// Serialize/deserialize the blob.
+// For real usecases you should probably use serde rather than doing it by hand.
fn serialize_blob(color: api::ColorU) -> Vec<u8> {
vec![color.r, color.g, color.b, color.a]
}
fn deserialize_blob(blob: &[u8]) -> Result<ImageRenderingCommands, ()> {
let mut iter = blob.iter();
return match (iter.next(), iter.next(), iter.next(), iter.next()) {
@@ -68,32 +68,32 @@ fn render_blob(
let y2 = y + descriptor.offset.y as u32;
// Render a simple checkerboard pattern
let checker = if (x2 % 20 >= 10) != (y2 % 20 >= 10) {
1
} else {
0
};
- // ..nested in the per-tile cherkerboard pattern
+ // ..nested in the per-tile checkerboard pattern
let tc = if tile_checker { 0 } else { (1 - checker) * 40 };
match descriptor.format {
api::ImageFormat::BGRA8 => {
texels.push(color.b * checker + tc);
texels.push(color.g * checker + tc);
texels.push(color.r * checker + tc);
texels.push(color.a * checker + tc);
}
api::ImageFormat::R8 => {
texels.push(color.a * checker + tc);
}
_ => {
return Err(api::BlobImageError::Other(
- format!("Usupported image format"),
+ format!("Unsupported image format"),
));
}
}
}
}
Ok(api::RasterizedBlobImage {
data: texels,
@@ -173,17 +173,17 @@ impl api::BlobImageRenderer for Checkerb
self.workers.spawn(move || {
let result = render_blob(cmds, &descriptor, request.tile);
tx.send((request, result)).unwrap();
});
// Add None in the map of rendered images. This makes it possible to differentiate
// between commands that aren't finished yet (entry in the map is equal to None) and
// keys that have never been requested (entry not in the map), which would cause deadlocks
- // if we were to block upon receing their result in resolve!
+ // if we were to block upon receiving their result in resolve!
self.rendered_images.insert(request, None);
}
fn resolve(&mut self, request: api::BlobImageRequest) -> api::BlobImageResult {
// In this method we wait until the work is complete on the worker threads and
// gather the results.
// First look at whether we have already received the rendered image
new file mode 100644
index 0000000000000000000000000000000000000000..5edcddc3d16058b22265792febf739529044b5a8
GIT binary patch
literal 65580
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
Msjbg!Rkzyz0eL$NuK)l5
--- a/gfx/webrender/res/brush.glsl
+++ b/gfx/webrender/res/brush.glsl
@@ -4,16 +4,17 @@
#ifdef WR_VERTEX_SHADER
void brush_vs(
VertexInfo vi,
int prim_address,
RectWithSize local_rect,
ivec3 user_data,
+ mat4 transform,
PictureTask pic_task
);
#define VECS_PER_BRUSH_PRIM 2
#define VECS_PER_SEGMENT 2
#define BRUSH_FLAG_PERSPECTIVE_INTERPOLATION 1
@@ -141,16 +142,17 @@ void main(void) {
#endif
// Run the specific brush VS code to write interpolators.
brush_vs(
vi,
brush.prim_address + VECS_PER_BRUSH_PRIM,
brush_prim.local_rect,
brush.user_data,
+ scroll_node.transform,
pic_task
);
}
#endif
#ifdef WR_FRAGMENT_SHADER
vec4 brush_fs();
--- a/gfx/webrender/res/brush_blend.glsl
+++ b/gfx/webrender/res/brush_blend.glsl
@@ -1,13 +1,13 @@
/* 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
+#define VECS_PER_SPECIFIC_BRUSH 0
#define FORCE_NO_PERSPECTIVE
#include shared,prim_shared,brush
varying vec3 vUv;
flat varying float vAmount;
flat varying int vOp;
@@ -17,16 +17,17 @@ flat varying vec4 vUvClipBounds;
#ifdef WR_VERTEX_SHADER
void brush_vs(
VertexInfo vi,
int prim_address,
RectWithSize local_rect,
ivec3 user_data,
+ mat4 transform,
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;
vUv = vec3(uv / texture_size, src_task.common_data.texture_layer_index);
--- a/gfx/webrender/res/brush_image.glsl
+++ b/gfx/webrender/res/brush_image.glsl
@@ -1,13 +1,13 @@
/* 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
+#define VECS_PER_SPECIFIC_BRUSH 0
#include shared,prim_shared,brush
#ifdef WR_FEATURE_ALPHA_PASS
varying vec2 vLocalPos;
#endif
varying vec3 vUv;
@@ -24,31 +24,47 @@ flat varying vec4 vColor;
#ifdef WR_FEATURE_ALPHA_PASS
#define IMAGE_SOURCE_COLOR 0
#define IMAGE_SOURCE_ALPHA 1
#define IMAGE_SOURCE_MASK_FROM_COLOR 2
#endif
struct ImageBrush {
RectWithSize rendered_task_rect;
+ vec2 offset;
vec4 color;
};
ImageBrush fetch_image_primitive(int address) {
- vec4[2] data = fetch_from_resource_cache_2(address);
+ vec4[3] data = fetch_from_resource_cache_3(address);
RectWithSize rendered_task_rect = RectWithSize(data[0].xy, data[0].zw);
- ImageBrush brush = ImageBrush(rendered_task_rect, data[1]);
+ ImageBrush brush = ImageBrush(rendered_task_rect, data[1].xy, data[2]);
return brush;
}
+#ifdef WR_FEATURE_ALPHA_PASS
+vec2 transform_point_snapped(
+ vec2 local_pos,
+ RectWithSize local_rect,
+ mat4 transform
+) {
+ vec2 snap_offset = compute_snap_offset(local_pos, transform, local_rect);
+ vec4 world_pos = transform * vec4(local_pos, 0.0, 1.0);
+ vec2 device_pos = world_pos.xy / world_pos.w * uDevicePixelRatio;
+
+ return device_pos + snap_offset;
+}
+#endif
+
void brush_vs(
VertexInfo vi,
int prim_address,
RectWithSize local_rect,
ivec3 user_data,
+ mat4 transform,
PictureTask pic_task
) {
// If this is in WR_FEATURE_TEXTURE_RECT mode, the rect and size use
// non-normalized texture coordinates.
#ifdef WR_FEATURE_TEXTURE_RECT
vec2 texture_size = vec2(1, 1);
#else
vec2 texture_size = vec2(textureSize(sColor0, 0));
@@ -68,33 +84,61 @@ void brush_vs(
vUvBounds = vec4(
min_uv + vec2(0.5),
max_uv - vec2(0.5)
) / texture_size.xyxy;
vec2 f;
#ifdef WR_FEATURE_ALPHA_PASS
- ImageBrush image = fetch_image_primitive(prim_address);
- vColor = image.color;
+ int image_source = user_data.y >> 16;
+ int raster_space = user_data.y & 0xffff;
// Derive the texture coordinates for this image, based on
// whether the source image is a local-space or screen-space
// image.
- switch (user_data.z) {
- case RASTER_SCREEN:
- f = (vi.snapped_device_pos - image.rendered_task_rect.p0) / image.rendered_task_rect.size;
+ switch (raster_space) {
+ case RASTER_SCREEN: {
+ ImageBrush image = fetch_image_primitive(user_data.z);
+ vColor = image.color;
+
+ vec2 snapped_device_pos;
+
+ // For drop-shadows, we need to apply a local offset
+ // in order to generate the correct screen-space UV.
+ // For other effects, we can use the 1:1 mapping of
+ // the vertex device position for the UV generation.
+ switch (image_source) {
+ case IMAGE_SOURCE_MASK_FROM_COLOR: {
+ vec2 local_pos = vi.local_pos - image.offset;
+ snapped_device_pos = transform_point_snapped(
+ local_pos,
+ local_rect,
+ transform
+ );
+ break;
+ }
+ case IMAGE_SOURCE_COLOR:
+ case IMAGE_SOURCE_ALPHA:
+ default:
+ snapped_device_pos = vi.snapped_device_pos;
+ break;
+ }
+
+ f = (snapped_device_pos - image.rendered_task_rect.p0) / image.rendered_task_rect.size;
vUvClipBounds = vec4(
min_uv,
max_uv
) / texture_size.xyxy;
break;
+ }
case RASTER_LOCAL:
default: {
+ vColor = vec4(1.0);
f = (vi.local_pos - local_rect.p0) / local_rect.size;
// Set the clip bounds to a value that won't have any
// effect for local space images.
#ifdef WR_FEATURE_TEXTURE_RECT
vUvClipBounds = vec4(0.0, 0.0, vec2(textureSize(sColor0)));
#else
vUvClipBounds = vec4(0.0, 0.0, 1.0, 1.0);
@@ -105,26 +149,27 @@ void brush_vs(
#else
f = (vi.local_pos - local_rect.p0) / local_rect.size;
#endif
vUv.xy = mix(uv0, uv1, f);
vUv.xy /= texture_size;
#ifdef WR_FEATURE_ALPHA_PASS
- switch (user_data.y) {
- case IMAGE_SOURCE_COLOR:
- vSelect = vec2(0.0, 0.0);
- break;
+ switch (image_source) {
case IMAGE_SOURCE_ALPHA:
vSelect = vec2(0.0, 1.0);
break;
case IMAGE_SOURCE_MASK_FROM_COLOR:
vSelect = vec2(1.0, 1.0);
break;
+ case IMAGE_SOURCE_COLOR:
+ default:
+ vSelect = vec2(0.0, 0.0);
+ break;
}
vLocalPos = vi.local_pos;
#endif
}
#endif
#ifdef WR_FRAGMENT_SHADER
--- a/gfx/webrender/res/brush_linear_gradient.glsl
+++ b/gfx/webrender/res/brush_linear_gradient.glsl
@@ -30,16 +30,17 @@ Gradient fetch_gradient(int address) {
return Gradient(data[0], data[1]);
}
void brush_vs(
VertexInfo vi,
int prim_address,
RectWithSize local_rect,
ivec3 user_data,
+ mat4 transform,
PictureTask pic_task
) {
Gradient gradient = fetch_gradient(prim_address);
vPos = vi.local_pos - local_rect.p0;
vec2 start_point = gradient.start_end_point.xy;
vec2 end_point = gradient.start_end_point.zw;
deleted file mode 100644
--- a/gfx/webrender/res/brush_mask_rounded_rect.glsl
+++ /dev/null
@@ -1,98 +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/. */
-
-#define VECS_PER_SPECIFIC_BRUSH 4
-
-#include shared,prim_shared,ellipse,brush
-
-flat varying float vClipMode;
-flat varying vec4 vClipCenter_Radius_TL;
-flat varying vec4 vClipCenter_Radius_TR;
-flat varying vec4 vClipCenter_Radius_BR;
-flat varying vec4 vClipCenter_Radius_BL;
-flat varying vec4 vLocalRect;
-varying vec2 vLocalPos;
-
-#ifdef WR_VERTEX_SHADER
-
-struct RoundedRectPrimitive {
- float clip_mode;
- vec4 rect;
- vec2 radius_tl;
- vec2 radius_tr;
- vec2 radius_br;
- vec2 radius_bl;
-};
-
-RoundedRectPrimitive fetch_rounded_rect_primitive(int address) {
- vec4 data[4] = fetch_from_resource_cache_4(address);
- return RoundedRectPrimitive(
- data[0].x,
- data[1],
- data[2].xy,
- data[2].zw,
- data[3].xy,
- data[3].zw
- );
-}
-
-void brush_vs(
- VertexInfo vi,
- int prim_address,
- RectWithSize local_rect,
- ivec3 user_data,
- PictureTask pic_task
-) {
- // Load the specific primitive.
- RoundedRectPrimitive prim = fetch_rounded_rect_primitive(prim_address);
-
- // Write clip parameters
- vClipMode = prim.clip_mode;
-
- // TODO(gw): In the future, when brush primitives may be segment rects
- // we need to account for that here, and differentiate between
- // the segment rect (geometry) amd the primitive rect (which
- // defines where the clip radii are relative to).
- vec4 clip_rect = vec4(prim.rect.xy, prim.rect.xy + prim.rect.zw);
-
- vClipCenter_Radius_TL = vec4(clip_rect.xy + prim.radius_tl, prim.radius_tl);
- vClipCenter_Radius_TR = vec4(clip_rect.zy + vec2(-prim.radius_tr.x, prim.radius_tr.y), prim.radius_tr);
- vClipCenter_Radius_BR = vec4(clip_rect.zw - prim.radius_br, prim.radius_br);
- vClipCenter_Radius_BL = vec4(clip_rect.xw + vec2(prim.radius_bl.x, -prim.radius_bl.y), prim.radius_bl);
-
- vLocalRect = clip_rect;
- vLocalPos = vi.local_pos;
-}
-#endif
-
-#ifdef WR_FRAGMENT_SHADER
-vec4 brush_fs() {
- // TODO(gw): The mask code below is super-inefficient. Once we
- // start using primitive segments in brush shaders, this can
- // be made much faster.
-
- // NOTE: The AA range must be computed outside the if statement,
- // since otherwise the results can be undefined if the
- // input function is not continuous. I have observed this
- // as flickering behaviour on Intel GPUs.
- float aa_range = compute_aa_range(vLocalPos);
-
- // Apply ellipse clip on each corner.
- float d = 0.0;
-
- if (vLocalPos.x > vLocalRect.x &&
- vLocalPos.y > vLocalRect.y &&
- vLocalPos.x <= vLocalRect.z &&
- vLocalPos.y <= vLocalRect.w) {
- d = rounded_rect(vLocalPos,
- vClipCenter_Radius_TL,
- vClipCenter_Radius_TR,
- vClipCenter_Radius_BR,
- vClipCenter_Radius_BL,
- aa_range);
- }
-
- return vec4(mix(d, 1.0 - d, vClipMode));
-}
-#endif
--- a/gfx/webrender/res/brush_mix_blend.glsl
+++ b/gfx/webrender/res/brush_mix_blend.glsl
@@ -1,27 +1,28 @@
/* 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
+#define VECS_PER_SPECIFIC_BRUSH 0
#include shared,prim_shared,brush
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,
+ mat4 transform,
PictureTask pic_task
) {
vec2 texture_size = vec2(textureSize(sCacheRGBA8, 0));
vOp = user_data.x;
PictureTask src_task = fetch_picture_task(user_data.z);
vec2 src_uv = vi.snapped_device_pos +
src_task.common_data.task_rect.p0 -
--- a/gfx/webrender/res/brush_radial_gradient.glsl
+++ b/gfx/webrender/res/brush_radial_gradient.glsl
@@ -31,16 +31,17 @@ RadialGradient fetch_radial_gradient(int
return RadialGradient(data[0], data[1]);
}
void brush_vs(
VertexInfo vi,
int prim_address,
RectWithSize local_rect,
ivec3 user_data,
+ mat4 transform,
PictureTask pic_task
) {
RadialGradient gradient = fetch_radial_gradient(prim_address);
vPos = vi.local_pos - local_rect.p0;
vCenter = gradient.center_start_end_radius.xy;
vStartRadius = gradient.center_start_end_radius.z;
--- a/gfx/webrender/res/brush_solid.glsl
+++ b/gfx/webrender/res/brush_solid.glsl
@@ -23,16 +23,17 @@ SolidBrush fetch_solid_primitive(int add
return SolidBrush(data);
}
void brush_vs(
VertexInfo vi,
int prim_address,
RectWithSize local_rect,
ivec3 user_data,
+ mat4 transform,
PictureTask pic_task
) {
SolidBrush prim = fetch_solid_primitive(prim_address);
vColor = prim.color;
#ifdef WR_FEATURE_ALPHA_PASS
vLocalPos = vi.local_pos;
#endif
--- a/gfx/webrender/res/brush_yuv_image.glsl
+++ b/gfx/webrender/res/brush_yuv_image.glsl
@@ -70,16 +70,17 @@ void write_uv_rect(
#endif
}
void brush_vs(
VertexInfo vi,
int prim_address,
RectWithSize local_rect,
ivec3 user_data,
+ mat4 transform,
PictureTask pic_task
) {
vec2 f = (vi.local_pos - local_rect.p0) / local_rect.size;
#ifdef WR_FEATURE_ALPHA_PASS
vLocalPos = vi.local_pos;
#endif
--- a/gfx/webrender/res/ellipse.glsl
+++ b/gfx/webrender/res/ellipse.glsl
@@ -53,17 +53,17 @@ float rounded_rect(vec2 pos,
vec4 clip_center_radius_tl,
vec4 clip_center_radius_tr,
vec4 clip_center_radius_br,
vec4 clip_center_radius_bl,
float aa_range) {
// Start with a negative value (means "inside") for all fragments that are not
// in a corner. If the fragment is in a corner, one of the clip_against_ellipse_if_needed
// calls below will update it.
- float current_distance = -1.0;
+ float current_distance = -aa_range;
// Clip against each ellipse.
current_distance = clip_against_ellipse_if_needed(pos,
current_distance,
clip_center_radius_tl,
vec2(1.0),
aa_range);
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/pf_vector_cover.glsl
@@ -0,0 +1,77 @@
+/* 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
+
+#ifdef WR_VERTEX_SHADER
+
+in ivec4 aTargetRect;
+in ivec2 aStencilOrigin;
+in int aSubpixel;
+in int aPad;
+
+out vec2 vStencilUV;
+flat out int vSubpixel;
+
+void main(void) {
+ vec4 targetRect = vec4(aTargetRect);
+ vec2 stencilOrigin = vec2(aStencilOrigin);
+
+ vec2 targetOffset = mix(vec2(0.0), targetRect.zw, aPosition.xy);
+ vec2 targetPosition = targetRect.xy + targetOffset;
+ vec2 stencilOffset = targetOffset * vec2(aSubpixel == 0 ? 1.0 : 3.0, 1.0);
+ vec2 stencilPosition = stencilOrigin + stencilOffset;
+
+ gl_Position = uTransform * vec4(targetPosition, aPosition.z, 1.0);
+ vStencilUV = stencilPosition;
+ vSubpixel = aSubpixel;
+}
+
+#endif
+
+#ifdef WR_FRAGMENT_SHADER
+
+#define LCD_FILTER_FACTOR_0 (86.0 / 255.0)
+#define LCD_FILTER_FACTOR_1 (77.0 / 255.0)
+#define LCD_FILTER_FACTOR_2 (8.0 / 255.0)
+
+in vec2 vStencilUV;
+flat in int vSubpixel;
+
+/// Applies a slight horizontal blur to reduce color fringing on LCD screens
+/// when performing subpixel AA.
+///
+/// The algorithm should be identical to that of FreeType:
+/// https://www.freetype.org/freetype2/docs/reference/ft2-lcd_filtering.html
+float lcdFilter(float shadeL2, float shadeL1, float shade0, float shadeR1, float shadeR2) {
+ return LCD_FILTER_FACTOR_2 * shadeL2 +
+ LCD_FILTER_FACTOR_1 * shadeL1 +
+ LCD_FILTER_FACTOR_0 * shade0 +
+ LCD_FILTER_FACTOR_1 * shadeR1 +
+ LCD_FILTER_FACTOR_2 * shadeR2;
+}
+
+void main(void) {
+ ivec2 stencilUV = ivec2(vStencilUV);
+ float shade0 = abs(TEXEL_FETCH(sColor0, stencilUV, 0, ivec2(0, 0)).r);
+
+ if (vSubpixel == 0) {
+ oFragColor = vec4(shade0);
+ return;
+ }
+
+ vec3 shadeL = abs(vec3(TEXEL_FETCH(sColor0, stencilUV, 0, ivec2(-1, 0)).r,
+ TEXEL_FETCH(sColor0, stencilUV, 0, ivec2(-2, 0)).r,
+ TEXEL_FETCH(sColor0, stencilUV, 0, ivec2(-3, 0)).r));
+ vec3 shadeR = abs(vec3(TEXEL_FETCH(sColor0, stencilUV, 0, ivec2(1, 0)).r,
+ TEXEL_FETCH(sColor0, stencilUV, 0, ivec2(2, 0)).r,
+ TEXEL_FETCH(sColor0, stencilUV, 0, ivec2(3, 0)).r));
+
+ oFragColor = vec4(lcdFilter(shadeL.z, shadeL.y, shadeL.x, shade0, shadeR.x),
+ lcdFilter(shadeL.y, shadeL.x, shade0, shadeR.x, shadeR.y),
+ lcdFilter(shadeL.x, shade0, shadeR.x, shadeR.y, shadeR.z),
+ 1.0);
+}
+
+#endif
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/pf_vector_stencil.glsl
@@ -0,0 +1,111 @@
+/* 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
+
+#ifdef WR_VERTEX_SHADER
+
+in vec2 aFromPosition;
+in vec2 aCtrlPosition;
+in vec2 aToPosition;
+in vec2 aFromNormal;
+in vec2 aCtrlNormal;
+in vec2 aToNormal;
+in int aPathID;
+in int aPad;
+
+out vec2 vFrom;
+out vec2 vCtrl;
+out vec2 vTo;
+
+void main(void) {
+ // Unpack.
+ int pathID = int(aPathID);
+
+ ivec2 pathAddress = ivec2(0.0, aPathID);
+ mat2 transformLinear = mat2(TEXEL_FETCH(sColor1, pathAddress, 0, ivec2(0, 0)));
+ vec2 transformTranslation = TEXEL_FETCH(sColor1, pathAddress, 0, ivec2(1, 0)).xy;
+
+ vec4 miscInfo = TEXEL_FETCH(sColor1, pathAddress, 0, ivec2(2, 0));
+ float rectHeight = miscInfo.y;
+ vec2 emboldenAmount = miscInfo.zw * 0.5;
+
+ // TODO(pcwalton): Hint positions.
+ vec2 from = aFromPosition;
+ vec2 ctrl = aCtrlPosition;
+ vec2 to = aToPosition;
+
+ // Embolden as necessary.
+ from -= aFromNormal * emboldenAmount;
+ ctrl -= aCtrlNormal * emboldenAmount;
+ to -= aToNormal * emboldenAmount;
+
+ // Perform the transform.
+ from = transformLinear * from + transformTranslation;
+ ctrl = transformLinear * ctrl + transformTranslation;
+ to = transformLinear * to + transformTranslation;
+
+ // Choose correct quadrant for rotation.
+ vec2 corner = vec2(0.0, rectHeight) + transformTranslation;
+
+ // Compute edge vectors. De Casteljau subdivide if necessary.
+ // TODO(pcwalton): Actually do the two-pass rendering.
+
+ // Compute position and dilate. If too thin, discard to avoid artefacts.
+ vec2 position;
+ if (abs(from.x - to.x) < 0.0001)
+ position.x = 0.0;
+ else if (aPosition.x < 0.5)
+ position.x = floor(min(min(from.x, to.x), ctrl.x));
+ else
+ position.x = ceil(max(max(from.x, to.x), ctrl.x));
+ if (aPosition.y < 0.5)
+ position.y = floor(min(min(from.y, to.y), ctrl.y));
+ else
+ position.y = corner.y;
+
+ // Compute final position and depth.
+ vec4 clipPosition = uTransform * vec4(position, aPosition.z, 1.0);
+
+ // Finish up.
+ gl_Position = clipPosition;
+ vFrom = from - position;
+ vCtrl = ctrl - position;
+ vTo = to - position;
+}
+
+#endif
+
+#ifdef WR_FRAGMENT_SHADER
+
+uniform sampler2D uAreaLUT;
+
+in vec2 vFrom;
+in vec2 vCtrl;
+in vec2 vTo;
+
+void main(void) {
+ // Unpack.
+ vec2 from = vFrom, ctrl = vCtrl, to = vTo;
+
+ // Determine winding, and sort into a consistent order so we only need to find one root below.
+ bool winding = from.x < to.x;
+ vec2 left = winding ? from : to, right = winding ? to : from;
+ vec2 v0 = ctrl - left, v1 = right - ctrl;
+
+ // Shoot a vertical ray toward the curve.
+ vec2 window = clamp(vec2(from.x, to.x), -0.5, 0.5);
+ float offset = mix(window.x, window.y, 0.5) - left.x;
+ float t = offset / (v0.x + sqrt(v1.x * offset - v0.x * (offset - v0.x)));
+
+ // Compute position and derivative to form a line approximation.
+ float y = mix(mix(left.y, ctrl.y, t), mix(ctrl.y, right.y, t), t);
+ float d = mix(v0.y, v1.y, t) / mix(v0.x, v1.x, t);
+
+ // Look up area under that line, and scale horizontally to the window size.
+ float dX = window.x - window.y;
+ oFragColor = vec4(texture(sColor0, vec2(y + 8.0, abs(d * dX)) / 16.0).r * dX);
+}
+
+#endif
--- a/gfx/webrender/res/prim_shared.glsl
+++ b/gfx/webrender/res/prim_shared.glsl
@@ -484,29 +484,29 @@ vec4 get_node_pos(vec2 pos, ClipScrollNo
// get the normal to the scroll node plane
vec3 n = transpose(mat3(node.inv_transform)) * vec3(0.0, 0.0, 1.0);
return untransform(pos, n, a, node.inv_transform);
}
// Compute a snapping offset in world space (adjusted to pixel ratio),
// given local position on the scroll_node and a snap rectangle.
vec2 compute_snap_offset(vec2 local_pos,
- ClipScrollNode scroll_node,
+ mat4 transform,
RectWithSize snap_rect) {
// Ensure that the snap rect is at *least* one device pixel in size.
// TODO(gw): It's not clear to me that this is "correct". Specifically,
// how should it interact with sub-pixel snap rects when there
// is a scroll_node transform with scale present? But it does fix
// the test cases we have in Servo that are failing without it
// and seem better than not having this at all.
snap_rect.size = max(snap_rect.size, vec2(1.0 / uDevicePixelRatio));
// Transform the snap corners to the world space.
- vec4 world_snap_p0 = scroll_node.transform * vec4(snap_rect.p0, 0.0, 1.0);
- vec4 world_snap_p1 = scroll_node.transform * vec4(snap_rect.p0 + snap_rect.size, 0.0, 1.0);
+ vec4 world_snap_p0 = transform * vec4(snap_rect.p0, 0.0, 1.0);
+ vec4 world_snap_p1 = transform * vec4(snap_rect.p0 + snap_rect.size, 0.0, 1.0);
// Snap bounds in world coordinates, adjusted for pixel ratio. XY = top left, ZW = bottom right
vec4 world_snap = uDevicePixelRatio * vec4(world_snap_p0.xy, world_snap_p1.xy) /
vec4(world_snap_p0.ww, world_snap_p1.ww);
/// World offsets applied to the corners of the snap rectangle.
vec4 snap_offsets = floor(world_snap + 0.5) - world_snap;
/// Compute the position of this vertex inside the snap rectangle.
vec2 normalized_snap_pos = (local_pos - snap_rect.p0) / snap_rect.size;
@@ -530,17 +530,21 @@ VertexInfo write_vertex(RectWithSize ins
// Select the corner of the local rect that we are processing.
vec2 local_pos = instance_rect.p0 + instance_rect.size * aPosition.xy;
// Clamp to the two local clip rects.
vec2 clamped_local_pos = clamp_rect(local_pos, local_clip_rect);
/// Compute the snapping offset.
- vec2 snap_offset = compute_snap_offset(clamped_local_pos, scroll_node, snap_rect);
+ vec2 snap_offset = compute_snap_offset(
+ clamped_local_pos,
+ scroll_node.transform,
+ snap_rect
+ );
// Transform the current vertex to world space.
vec4 world_pos = scroll_node.transform * vec4(clamped_local_pos, 0.0, 1.0);
// Convert the world positions to device pixel space.
vec2 device_pos = world_pos.xy / world_pos.w * uDevicePixelRatio;
// Apply offsets for the render task to get correct screen location.
deleted file mode 100644
--- a/gfx/webrender/res/ps_hardware_composite.glsl
+++ /dev/null
@@ -1,39 +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
-
-varying vec3 vUv;
-flat varying vec4 vUvBounds;
-
-#ifdef WR_VERTEX_SHADER
-void main(void) {
- CompositeInstance ci = fetch_composite_instance();
- PictureTask dest_task = fetch_picture_task(ci.render_task_index);
- PictureTask src_task = fetch_picture_task(ci.src_task_index);
-
- vec2 dest_origin = dest_task.common_data.task_rect.p0 -
- dest_task.content_origin +
- vec2(ci.user_data0, ci.user_data1);
-
- vec2 local_pos = mix(dest_origin,
- dest_origin + vec2(ci.user_data2, ci.user_data3),
- aPosition.xy);
-
- vec2 texture_size = vec2(textureSize(sCacheRGBA8, 0));
- vec2 st0 = src_task.common_data.task_rect.p0;
- vec2 st1 = src_task.common_data.task_rect.p0 + src_task.common_data.task_rect.size;
- vUv = vec3(mix(st0, st1, aPosition.xy) / texture_size, src_task.common_data.texture_layer_index);
- vUvBounds = vec4(st0 + 0.5, st1 - 0.5) / texture_size.xyxy;
-
- gl_Position = uTransform * vec4(local_pos, ci.z, 1.0);
-}
-#endif
-
-#ifdef WR_FRAGMENT_SHADER
-void main(void) {
- vec2 uv = clamp(vUv.xy, vUvBounds.xy, vUvBounds.zw);
- oFragColor = texture(sColor0, vec3(uv, vUv.z));
-}
-#endif
--- a/gfx/webrender/res/ps_text_run.glsl
+++ b/gfx/webrender/res/ps_text_run.glsl
@@ -45,17 +45,21 @@ VertexInfo write_text_vertex(vec2 clampe
#ifdef WR_FEATURE_GLYPH_TRANSFORM
// For transformed subpixels, we just need to align the glyph origin to a device pixel.
// Only check the scroll node transform's translation since the scales and axes match.
vec2 world_snap_p0 = snap_rect.p0 + scroll_node.transform[3].xy * uDevicePixelRatio;
final_pos += floor(world_snap_p0 + 0.5) - world_snap_p0;
#elif !defined(WR_FEATURE_TRANSFORM)
// Compute the snapping offset only if the scroll node transform is axis-aligned.
- final_pos += compute_snap_offset(clamped_local_pos, scroll_node, snap_rect);
+ final_pos += compute_snap_offset(
+ clamped_local_pos,
+ scroll_node.transform,
+ snap_rect
+ );
#endif
gl_Position = uTransform * vec4(final_pos, z, 1.0);
VertexInfo vi = VertexInfo(
clamped_local_pos,
device_pos,
world_pos.w,
--- a/gfx/webrender/src/batch.rs
+++ b/gfx/webrender/src/batch.rs
@@ -1,13 +1,13 @@
/* 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, DeviceIntRect, DeviceIntSize, LayerToWorldScale};
+use api::{AlphaType, DeviceIntRect, DeviceIntSize};
use api::{DeviceUintRect, DeviceUintPoint, DeviceUintSize, ExternalImageType, FilterOp, ImageRendering, LayerRect};
use api::{DeviceIntPoint, SubpixelDirection, YuvColorSpace, YuvFormat};
use api::{LayerToWorldTransform, WorldPixel};
use border::{BorderCornerInstance, BorderCornerSide, BorderEdgeKind};
use clip::{ClipSource, ClipStore, ClipWorkItem};
use clip_scroll_tree::{CoordinateSystemId};
use euclid::{TypedTransform3D, vec3};
use glyph_rasterizer::GlyphFormat;
@@ -67,17 +67,16 @@ pub enum BrushBatchKind {
RadialGradient,
LinearGradient,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum BatchKind {
- HardwareComposite,
SplitComposite,
Transformable(TransformedRectKind, TransformBatchKind),
Brush(BrushBatchKind),
}
/// Optional textures that can be used as a source in the shaders.
/// Textures that are not used by the batch are equal to TextureId::invalid().
#[derive(Copy, Clone, Debug)]
@@ -634,62 +633,56 @@ impl AlphaBatchBuilder {
BlendMode::None
};
match prim_metadata.prim_kind {
PrimitiveKind::Brush => {
let brush = &ctx.prim_store.cpu_brushes[prim_metadata.cpu_prim_index.0];
match brush.kind {
- BrushKind::Picture { pic_index } => {
+ BrushKind::Picture { pic_index, source_kind, .. } => {
let picture =
&ctx.prim_store.pictures[pic_index.0];
- match picture.surface {
- Some(cache_task_id) => {
- let cache_task_address = render_tasks.get_task_address(cache_task_id);
- let textures = BatchTextures::render_target_cache();
-
- // If this picture is participating in a 3D rendering context,
- // then don't add it to any batches here. Instead, create a polygon
- // for it and add it to the current plane splitter.
- if picture.is_in_3d_context {
- // Push into parent plane splitter.
+ // If this picture is participating in a 3D rendering context,
+ // then don't add it to any batches here. Instead, create a polygon
+ // for it and add it to the current plane splitter.
+ if picture.is_in_3d_context {
+ // Push into parent plane splitter.
+ debug_assert!(picture.surface.is_some());
- let real_xf = &ctx.clip_scroll_tree
- .nodes[picture.reference_frame_index.0]
- .world_content_transform
- .into();
- let polygon = make_polygon(
- picture.real_local_rect,
- &real_xf,
- prim_index.0,
- );
-
- splitter.add(polygon);
+ let real_xf = &ctx.clip_scroll_tree
+ .nodes[picture.reference_frame_index.0]
+ .world_content_transform
+ .into();
+ let polygon = make_polygon(
+ picture.real_local_rect,
+ &real_xf,
+ prim_index.0,
+ );
- return;
- }
+ splitter.add(polygon);
+
+ return;
+ }
- // Depending on the composite mode of the picture, we generate the
- // old style Composite primitive instances. In the future, we'll
- // remove these and pass them through the brush batching pipeline.
- // This will allow us to unify some of the shaders, apply clip masks
- // when compositing pictures, and also correctly apply pixel snapping
- // to picture compositing operations.
- let source_id = cache_task_id;
-
- match picture.composite_mode.expect("bug: only composites here") {
- PictureCompositeMode::Filter(filter) => {
- match filter {
- FilterOp::Blur(..) => {
+ let add_to_parent_pic = match picture.composite_mode {
+ Some(PictureCompositeMode::Filter(filter)) => {
+ match filter {
+ FilterOp::Blur(..) => {
+ match picture.surface {
+ Some(cache_task_id) => {
let kind = BatchKind::Brush(
BrushBatchKind::Image(ImageBufferKind::Texture2DArray)
);
- let key = BatchKey::new(kind, non_segmented_blend_mode, textures);
+ let key = BatchKey::new(
+ kind,
+ non_segmented_blend_mode,
+ BatchTextures::render_target_cache(),
+ );
let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
let uv_rect_address = render_tasks[cache_task_id]
.get_texture_handle()
.as_int(gpu_cache);
let instance = BrushInstance {
picture_address: task_address,
@@ -698,94 +691,91 @@ impl AlphaBatchBuilder {
scroll_id,
clip_task_address,
z,
segment_index: 0,
edge_flags: EdgeAaSegmentMask::empty(),
brush_flags: BrushFlags::empty(),
user_data: [
uv_rect_address,
- BrushImageSourceKind::Color as i32,
+ (BrushImageSourceKind::Color as i32) << 16 |
RasterizationSpace::Screen as i32,
+ picture.extra_gpu_data_handle.as_int(gpu_cache),
],
};
batch.push(PrimitiveInstance::from(instance));
+ false
}
- FilterOp::DropShadow(offset, _, _) => {
- let kind = BatchKind::Brush(
- BrushBatchKind::Image(ImageBufferKind::Texture2DArray),
- );
- let key = BatchKey::new(kind, non_segmented_blend_mode, textures);
-
- let uv_rect_address = render_tasks[cache_task_id]
- .get_texture_handle()
- .as_int(gpu_cache);
+ None => {
+ true
+ }
+ }
+ }
+ FilterOp::DropShadow(..) => {
+ if let Some(cache_task_id) = picture.surface {
+ let kind = BatchKind::Brush(
+ BrushBatchKind::Image(ImageBufferKind::Texture2DArray),
+ );
- let instance = BrushInstance {
- picture_address: task_address,
- prim_address: prim_cache_address,
- clip_chain_rect_index,
- scroll_id,
- clip_task_address,
- z,
- segment_index: 0,
- edge_flags: EdgeAaSegmentMask::empty(),
- brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
- user_data: [
- uv_rect_address,
- BrushImageSourceKind::ColorAlphaMask as i32,
- // TODO(gw): This is totally wrong, but the drop-shadow code itself
- // is completely wrong, and doesn't work correctly with
- // transformed Picture sources. I'm leaving this as is for
- // now, and will fix drop-shadows properly, as a follow up.
- RasterizationSpace::Local as i32,
- ],
- };
-
- {
- let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
- batch.push(PrimitiveInstance::from(instance));
+ let (textures, task_id) = match source_kind {
+ BrushImageSourceKind::Color => {
+ let secondary_id = picture.secondary_render_task_id.expect("no secondary!?");
+ let saved_index = render_tasks[secondary_id].saved_index.expect("no saved index!?");
+ debug_assert_ne!(saved_index, SavedTargetIndex::PENDING);
+ let textures = BatchTextures {
+ colors: [
+ SourceTexture::RenderTaskCache(saved_index),
+ SourceTexture::Invalid,
+ SourceTexture::Invalid,
+ ],
+ };
+ (textures, secondary_id)
}
+ BrushImageSourceKind::ColorAlphaMask => {
+ (BatchTextures::render_target_cache(), cache_task_id)
+ }
+ };
- let secondary_id = picture.secondary_render_task_id.expect("no secondary!?");
- let saved_index = render_tasks[secondary_id].saved_index.expect("no saved index!?");
- debug_assert_ne!(saved_index, SavedTargetIndex::PENDING);
- let secondary_task_address = render_tasks.get_task_address(secondary_id);
- let secondary_textures = BatchTextures {
- colors: [
- SourceTexture::RenderTaskCache(saved_index),
- SourceTexture::Invalid,
- SourceTexture::Invalid,
- ],
- };
- let key = BatchKey::new(
- BatchKind::HardwareComposite,
- BlendMode::PremultipliedAlpha,
- secondary_textures,
- );
- let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
- let content_rect = prim_metadata.local_rect.translate(&-offset);
- let rect =
- (content_rect * LayerToWorldScale::new(1.0) * ctx.device_pixel_scale).round()
- .to_i32();
+ let key = BatchKey::new(
+ kind,
+ non_segmented_blend_mode,
+ textures,
+ );
+
+ let uv_rect_address = render_tasks[task_id]
+ .get_texture_handle()
+ .as_int(gpu_cache);
- let instance = CompositePrimitiveInstance::new(
- task_address,
- secondary_task_address,
- RenderTaskAddress(0),
- rect.origin.x,
- rect.origin.y,
- z,
- rect.size.width,
- rect.size.height,
- );
+ let instance = BrushInstance {
+ picture_address: task_address,
+ prim_address: prim_cache_address,
+ clip_chain_rect_index,
+ scroll_id,
+ clip_task_address,
+ z,
+ segment_index: 0,
+ edge_flags: EdgeAaSegmentMask::empty(),
+ brush_flags: BrushFlags::empty(),
+ user_data: [
+ uv_rect_address,
+ (source_kind as i32) << 16 |
+ RasterizationSpace::Screen as i32,
+ picture.extra_gpu_data_handle.as_int(gpu_cache),
+ ],
+ };
- batch.push(PrimitiveInstance::from(instance));
- }
- _ => {
+ let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
+ batch.push(PrimitiveInstance::from(instance));
+ }
+
+ false
+ }
+ _ => {
+ match picture.surface {
+ Some(cache_task_id) => {
let key = BatchKey::new(
BatchKind::Brush(BrushBatchKind::Blend),
BlendMode::PremultipliedAlpha,
BatchTextures::render_target_cache(),
);
let filter_mode = match filter {
FilterOp::Blur(..) => 0,
@@ -819,16 +809,18 @@ impl AlphaBatchBuilder {
FilterOp::DropShadow(..) => {
unreachable!();
}
FilterOp::ColorMatrix(_) => {
picture.extra_gpu_data_handle.as_int(gpu_cache)
}
};
+ let cache_task_address = render_tasks.get_task_address(cache_task_id);
+
let instance = BrushInstance {
picture_address: task_address,
prim_address: prim_cache_address,
clip_chain_rect_index,
scroll_id,
clip_task_address,
z,
segment_index: 0,
@@ -838,100 +830,117 @@ impl AlphaBatchBuilder {
cache_task_address.0 as i32,
filter_mode,
user_data,
],
};
let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
batch.push(PrimitiveInstance::from(instance));
+ false
+ }
+ None => {
+ true
}
}
}
- PictureCompositeMode::MixBlend(mode) => {
- let backdrop_id = picture.secondary_render_task_id.expect("no backdrop!?");
-
- let key = BatchKey::new(
- BatchKind::Brush(
- BrushBatchKind::MixBlend {
- task_id,
- source_id,
- backdrop_id,
- },
- ),
- BlendMode::PremultipliedAlpha,
- BatchTextures::no_texture(),
- );
- let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
- let backdrop_task_address = render_tasks.get_task_address(backdrop_id);
- let source_task_address = render_tasks.get_task_address(source_id);
-
- let instance = BrushInstance {
- picture_address: task_address,
- prim_address: prim_cache_address,
- clip_chain_rect_index,
- scroll_id,
- clip_task_address,
- z,
- segment_index: 0,
- edge_flags: EdgeAaSegmentMask::empty(),
- brush_flags: BrushFlags::empty(),
- user_data: [
- mode as u32 as i32,
- backdrop_task_address.0 as i32,
- source_task_address.0 as i32,
- ],
- };
-
- batch.push(PrimitiveInstance::from(instance));
- }
- PictureCompositeMode::Blit => {
- let kind = BatchKind::Brush(
- BrushBatchKind::Image(ImageBufferKind::Texture2DArray)
- );
- let key = BatchKey::new(kind, non_segmented_blend_mode, textures);
- let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
-
- let uv_rect_address = render_tasks[cache_task_id]
- .get_texture_handle()
- .as_int(gpu_cache);
-
- let instance = BrushInstance {
- picture_address: task_address,
- prim_address: prim_cache_address,
- clip_chain_rect_index,
- scroll_id,
- clip_task_address,
- z,
- segment_index: 0,
- edge_flags: EdgeAaSegmentMask::empty(),
- brush_flags: BrushFlags::empty(),
- user_data: [
- uv_rect_address,
- BrushImageSourceKind::Color as i32,
- RasterizationSpace::Screen as i32,
- ],
- };
- batch.push(PrimitiveInstance::from(instance));
- }
}
}
+ Some(PictureCompositeMode::MixBlend(mode)) => {
+ let cache_task_id = picture.surface.expect("bug: no surface allocated");
+ let backdrop_id = picture.secondary_render_task_id.expect("no backdrop!?");
+
+ let key = BatchKey::new(
+ BatchKind::Brush(
+ BrushBatchKind::MixBlend {
+ task_id,
+ source_id: cache_task_id,
+ backdrop_id,
+ },
+ ),
+ BlendMode::PremultipliedAlpha,
+ BatchTextures::no_texture(),
+ );
+ let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
+ let backdrop_task_address = render_tasks.get_task_address(backdrop_id);
+ let source_task_address = render_tasks.get_task_address(cache_task_id);
+
+ let instance = BrushInstance {
+ picture_address: task_address,
+ prim_address: prim_cache_address,
+ clip_chain_rect_index,
+ scroll_id,
+ clip_task_address,
+ z,
+ segment_index: 0,
+ edge_flags: EdgeAaSegmentMask::empty(),
+ brush_flags: BrushFlags::empty(),
+ user_data: [
+ mode as u32 as i32,
+ backdrop_task_address.0 as i32,
+ source_task_address.0 as i32,
+ ],
+ };
+
+ batch.push(PrimitiveInstance::from(instance));
+ false
+ }
+ Some(PictureCompositeMode::Blit) => {
+ let cache_task_id = picture.surface.expect("bug: no surface allocated");
+ let kind = BatchKind::Brush(
+ BrushBatchKind::Image(ImageBufferKind::Texture2DArray)
+ );
+ let key = BatchKey::new(
+ kind,
+ non_segmented_blend_mode,
+ BatchTextures::render_target_cache(),
+ );
+ let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
+
+ let uv_rect_address = render_tasks[cache_task_id]
+ .get_texture_handle()
+ .as_int(gpu_cache);
+
+ let instance = BrushInstance {
+ picture_address: task_address,
+ prim_address: prim_cache_address,
+ clip_chain_rect_index,
+ scroll_id,
+ clip_task_address,
+ z,
+ segment_index: 0,
+ edge_flags: EdgeAaSegmentMask::empty(),
+ brush_flags: BrushFlags::empty(),
+ user_data: [
+ uv_rect_address,
+ (BrushImageSourceKind::Color as i32) << 16 |
+ RasterizationSpace::Screen as i32,
+ picture.extra_gpu_data_handle.as_int(gpu_cache),
+ ],
+ };
+ batch.push(PrimitiveInstance::from(instance));
+ false
+ }
None => {
- // If this picture is being drawn into an existing target (i.e. with
- // no composition operation), recurse and add to the current batch list.
- self.add_pic_to_batch(
- picture,
- task_id,
- ctx,
- gpu_cache,
- render_tasks,
- deferred_resolves,
- z_generator,
- );
+ true
}
+ };
+
+ // If this picture is being drawn into an existing target (i.e. with
+ // no composition operation), recurse and add to the current batch list.
+ if add_to_parent_pic {
+ self.add_pic_to_batch(
+ picture,
+ task_id,
+ ctx,
+ gpu_cache,
+ render_tasks,
+ deferred_resolves,
+ z_generator,
+ );
}
}
_ => {
if let Some((batch_kind, textures, user_data)) = brush.get_batch_params(
ctx.resource_cache,
gpu_cache,
deferred_resolves,
&ctx.cached_gradients,
@@ -1223,17 +1232,17 @@ impl AlphaBatchBuilder {
}
}
}
}
impl BrushPrimitive {
pub fn get_picture_index(&self) -> PictureIndex {
match self.kind {
- BrushKind::Picture { pic_index } => {
+ BrushKind::Picture { pic_index, .. } => {
pic_index
}
_ => {
panic!("bug: not a picture brush!!");
}
}
}
@@ -1258,18 +1267,19 @@ impl BrushPrimitive {
} else {
let textures = BatchTextures::color(cache_item.texture_id);
Some((
BrushBatchKind::Image(get_buffer_kind(cache_item.texture_id)),
textures,
[
cache_item.uv_rect_handle.as_int(gpu_cache),
- BrushImageSourceKind::Color as i32,
- RasterizationSpace::Local as i32,
+ (BrushImageSourceKind::Color as i32) << 16|
+ RasterizationSpace::Local as i32,
+ 0,
],
))
}
}
BrushKind::Picture { .. } => {
panic!("bug: get_batch_key is handled at higher level for pictures");
}
BrushKind::Solid { .. } => {
--- a/gfx/webrender/src/border.rs
+++ b/gfx/webrender/src/border.rs
@@ -161,17 +161,17 @@ impl NormalBorderHelpers for NormalBorde
(BorderStyle::Solid, BorderStyle::Solid) => {
if edge0.color == edge1.color && radius.width == 0.0 && radius.height == 0.0 {
BorderCornerKind::Solid
} else {
BorderCornerKind::Clip(BorderCornerInstance::Single)
}
}
- // Inset / outset borders just modtify the color of edges, so can be
+ // Inset / outset borders just modify the color of edges, so can be
// drawn with the normal border corner shader.
(BorderStyle::Outset, BorderStyle::Outset) |
(BorderStyle::Inset, BorderStyle::Inset) |
(BorderStyle::Double, BorderStyle::Double) |
(BorderStyle::Groove, BorderStyle::Groove) |
(BorderStyle::Ridge, BorderStyle::Ridge) => {
BorderCornerKind::Clip(BorderCornerInstance::Single)
}
@@ -440,17 +440,17 @@ impl<'a> DisplayListFlattener<'a> {
info.rect.origin.x + info.rect.size.width - right_len,
info.rect.origin.y + info.rect.size.height - bottom_len,
);
let p3 = info.rect.bottom_right();
let segment = |x0, y0, x1, y1| BrushSegment::new(
LayerPoint::new(x0, y0),
LayerSize::new(x1-x0, y1-y0),
- false,
+ true,
EdgeAaSegmentMask::all() // Note: this doesn't seem right, needs revision
);
// Add a solid rectangle for each visible edge/corner combination.
if top_edge == BorderEdgeKind::Solid {
let descriptor = BrushSegmentDescriptor {
segments: vec![
segment(p0.x, p0.y, p1.x, p1.y),
--- a/gfx/webrender/src/capture.rs
+++ b/gfx/webrender/src/capture.rs
@@ -1,16 +1,16 @@
/* 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 std::fs::File;
use std::path::{Path, PathBuf};
-use api::{CaptureBits, ExternalImageData, ExternalImageId, ImageDescriptor, TexelRect};
+use api::{CaptureBits, ExternalImageData, ImageDescriptor, TexelRect};
#[cfg(feature = "png")]
use device::ReadPixelsFormat;
use ron;
use serde;
pub struct CaptureConfig {
pub root: PathBuf,
@@ -118,15 +118,13 @@ pub struct ExternalCaptureImage {
/// A short description of an external image to be saved separately as
/// "externals/XX.ron", redirecting into a specific texture/blob with
/// the corresponding UV rectangle.
#[derive(Deserialize, Serialize)]
pub struct PlainExternalImage {
/// Path to the RON file describing the texel data.
pub data: String,
- /// Public ID of the external image.
- pub id: ExternalImageId,
- /// Channel index of an external image.
- pub channel_index: u8,
+ /// External image data source.
+ pub external: ExternalImageData,
/// UV sub-rectangle of the image.
pub uv: TexelRect,
}
--- a/gfx/webrender/src/device.rs
+++ b/gfx/webrender/src/device.rs
@@ -1,32 +1,34 @@
/* 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 super::shader_source;
-use api::{ColorF, ImageDescriptor, ImageFormat};
+use api::{ColorF, ImageFormat};
use api::{DeviceIntPoint, DeviceIntRect, DeviceUintRect, DeviceUintSize};
use api::TextureTarget;
+#[cfg(any(feature = "debug_renderer", feature="capture"))]
+use api::ImageDescriptor;
use euclid::Transform3D;
use gleam::gl;
use internal_types::{FastHashMap, RenderTargetInfo};
use smallvec::SmallVec;
use std::cell::RefCell;
use std::fs::File;
use std::io::Read;
use std::marker::PhantomData;
use std::mem;
use std::ops::Add;
use std::path::PathBuf;
use std::ptr;
use std::rc::Rc;
+use std::slice;
use std::thread;
-
#[derive(Debug, Copy, Clone, PartialEq, Ord, Eq, PartialOrd)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct FrameId(usize);
impl FrameId {
pub fn new(value: usize) -> Self {
FrameId(value)
@@ -55,32 +57,34 @@ const SHADER_LINE_MARKER: &str = "#line
pub struct TextureSlot(pub usize);
// In some places we need to temporarily bind a texture to any slot.
const DEFAULT_TEXTURE: TextureSlot = TextureSlot(0);
#[repr(u32)]
pub enum DepthFunction {
+ #[cfg(feature = "debug_renderer")]
Less = gl::LESS,
LessEqual = gl::LEQUAL,
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum TextureFilter {
Nearest,
Linear,
Trilinear,
}
#[derive(Debug)]
pub enum VertexAttributeKind {
F32,
+ #[cfg(feature = "debug_renderer")]
U8Norm,
U16Norm,
I32,
U16,
}
#[derive(Debug)]
pub struct VertexAttribute {
@@ -104,16 +108,21 @@ enum FBOTarget {
#[derive(Debug, Clone)]
pub enum UploadMethod {
/// Just call `glTexSubImage` directly with the CPU data pointer
Immediate,
/// Accumulate the changes in PBO first before transferring to a texture.
PixelBuffer(VertexUsageHint),
}
+/// Plain old data that can be used to initialize a texture.
+pub unsafe trait Texel: Copy {}
+unsafe impl Texel for u8 {}
+unsafe impl Texel for f32 {}
+
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ReadPixelsFormat {
Standard(ImageFormat),
Rgba8,
}
pub fn get_gl_target(target: TextureTarget) -> gl::GLuint {
match target {
@@ -225,16 +234,17 @@ pub fn build_shader_strings(
pub trait FileWatcherHandler: Send {
fn file_changed(&self, path: PathBuf);
}
impl VertexAttributeKind {
fn size_in_bytes(&self) -> u32 {
match *self {
VertexAttributeKind::F32 => 4,
+ #[cfg(feature = "debug_renderer")]
VertexAttributeKind::U8Norm => 1,
VertexAttributeKind::U16Norm => 2,
VertexAttributeKind::I32 => 4,
VertexAttributeKind::U16 => 2,
}
}
}
@@ -260,16 +270,17 @@ impl VertexAttribute {
attr_index,
self.count as gl::GLint,
gl::FLOAT,
false,
stride,
offset,
);
}
+ #[cfg(feature = "debug_renderer")]
VertexAttributeKind::U8Norm => {
gl.vertex_attrib_pointer(
attr_index,
self.count as gl::GLint,
gl::UNSIGNED_BYTE,
true,
stride,
offset,
@@ -453,20 +464,22 @@ impl Texture {
pub fn get_layer_count(&self) -> i32 {
self.layer_count
}
pub fn get_format(&self) -> ImageFormat {
self.format
}
+ #[cfg(any(feature = "debug_renderer", feature = "capture"))]
pub fn get_filter(&self) -> TextureFilter {
self.filter
}
+ #[cfg(any(feature = "debug_renderer", feature = "capture"))]
pub fn get_render_target(&self) -> Option<RenderTargetInfo> {
self.render_target.clone()
}
pub fn has_depth(&self) -> bool {
self.depth_rb.is_some()
}
@@ -631,23 +644,24 @@ impl VertexUsageHint {
#[derive(Copy, Clone, Debug)]
pub struct UniformLocation(gl::GLint);
impl UniformLocation {
pub const INVALID: Self = UniformLocation(-1);
}
+#[cfg(feature = "debug_renderer")]
pub struct Capabilities {
pub supports_multisampling: bool,
}
#[derive(Clone, Debug)]
pub enum ShaderError {
- Compilation(String, String), // name, error mssage
+ Compilation(String, String), // name, error message
Link(String, String), // name, error message
}
pub struct Device {
gl: Rc<gl::Gl>,
// device state
bound_textures: [gl::GLuint; 16],
bound_program: gl::GLuint,
@@ -656,17 +670,18 @@ pub struct Device {
bound_draw_fbo: FBOId,
program_mode_id: UniformLocation,
default_read_fbo: gl::GLuint,
default_draw_fbo: gl::GLuint,
device_pixel_ratio: f32,
upload_method: UploadMethod,
- // HW or API capabilties
+ // HW or API capabilities
+ #[cfg(feature = "debug_renderer")]
capabilities: Capabilities,
// debug
inside_frame: bool,
// resources
resource_override_path: Option<PathBuf>,
@@ -703,16 +718,17 @@ impl Device {
gl,
resource_override_path,
// This is initialized to 1 by default, but it is reset
// at the beginning of each frame in `Renderer::bind_frame_data`.
device_pixel_ratio: 1.0,
upload_method,
inside_frame: false,
+ #[cfg(feature = "debug_renderer")]
capabilities: Capabilities {
supports_multisampling: false, //TODO
},
bound_textures: [0; 16],
bound_program: 0,
bound_vao: 0,
bound_read_fbo: FBOId(0),
@@ -744,16 +760,17 @@ impl Device {
pub fn update_program_cache(&mut self, cached_programs: Rc<ProgramCache>) {
self.cached_programs = Some(cached_programs);
}
pub fn max_texture_size(&self) -> u32 {
self.max_texture_size
}
+ #[cfg(feature = "debug_renderer")]
pub fn get_capabilities(&self) -> &Capabilities {
&self.capabilities
}
pub fn reset_state(&mut self) {
self.bound_textures = [0; 16];
self.bound_vao = 0;
self.bound_read_fbo = FBOId(0);
@@ -781,17 +798,17 @@ impl Device {
Ok(id)
}
}
pub fn begin_frame(&mut self) -> FrameId {
debug_assert!(!self.inside_frame);
self.inside_frame = true;
- // Retrive the currently set FBO.
+ // Retrieve the currently set FBO.
let default_read_fbo = self.gl.get_integer_v(gl::READ_FRAMEBUFFER_BINDING);
self.default_read_fbo = default_read_fbo as gl::GLuint;
let default_draw_fbo = self.gl.get_integer_v(gl::DRAW_FRAMEBUFFER_BINDING);
self.default_draw_fbo = default_draw_fbo as gl::GLuint;
// Texture state
for i in 0 .. self.bound_textures.len() {
self.bound_textures[i] = 0;
@@ -990,38 +1007,38 @@ impl Device {
texture.width = new_size.width;
texture.height = new_size.height;
let rt_info = texture.render_target
.clone()
.expect("Only renderable textures are expected for resize here");
self.bind_texture(DEFAULT_TEXTURE, texture);
self.set_texture_parameters(texture.target, texture.filter);
- self.update_target_storage(texture, &rt_info, true, None);
+ self.update_target_storage::<u8>(texture, &rt_info, true, None);
let rect = DeviceIntRect::new(DeviceIntPoint::zero(), old_size.to_i32());
for (read_fbo, &draw_fbo) in old_fbos.into_iter().zip(&texture.fbo_ids) {
self.bind_read_target_impl(read_fbo);
self.bind_draw_target_impl(draw_fbo);
self.blit_render_target(rect, rect);
self.delete_fbo(read_fbo);
}
self.gl.delete_textures(&[old_texture_id]);
self.bind_read_target(None);
}
- pub fn init_texture(
+ pub fn init_texture<T: Texel>(
&mut self,
texture: &mut Texture,
mut width: u32,
mut height: u32,
filter: TextureFilter,
render_target: Option<RenderTargetInfo>,
layer_count: i32,
- pixels: Option<&[u8]>,
+ pixels: Option<&[T]>,
) {
debug_assert!(self.inside_frame);
if width > self.max_texture_size || height > self.max_texture_size {
error!("Attempting to allocate a texture of size {}x{} above the limit, trimming", width, height);
width = width.min(self.max_texture_size);
height = height.min(self.max_texture_size);
}
@@ -1044,22 +1061,22 @@ impl Device {
}
None => {
self.update_texture_storage(texture, pixels);
}
}
}
/// Updates the render target storage for the texture, creating FBOs as required.
- fn update_target_storage(
+ fn update_target_storage<T: Texel>(
&mut self,
texture: &mut Texture,
rt_info: &RenderTargetInfo,
is_resized: bool,
- pixels: Option<&[u8]>,
+ pixels: Option<&[T]>,
) {
assert!(texture.layer_count > 0 || texture.width + texture.height == 0);
let needed_layer_count = texture.layer_count - texture.fbo_ids.len() as i32;
let allocate_color = needed_layer_count != 0 || is_resized || pixels.is_some();
if allocate_color {
let desc = gl_describe_format(self.gl(), texture.format);
@@ -1070,31 +1087,31 @@ impl Device {
0,
desc.internal,
texture.width as _,
texture.height as _,
texture.layer_count,
0,
desc.external,
desc.pixel_type,
- pixels,
+ pixels.map(texels_to_u8_slice),
)
}
_ => {
assert_eq!(texture.layer_count, 1);
self.gl.tex_image_2d(
texture.target,
0,
desc.internal,
texture.width as _,
texture.height as _,
0,
desc.external,
desc.pixel_type,
- pixels,
+ pixels.map(texels_to_u8_slice),
)
}
}
}
if needed_layer_count > 0 {
// Create more framebuffers to fill the gap
let new_fbos = self.gl.gen_framebuffers(needed_layer_count);
@@ -1167,44 +1184,44 @@ impl Device {
gl::RENDERBUFFER,
depth_rb,
);
}
self.bind_external_draw_target(original_bound_fbo);
}
}
- fn update_texture_storage(&mut self, texture: &Texture, pixels: Option<&[u8]>) {
+ fn update_texture_storage<T: Texel>(&mut self, texture: &Texture, pixels: Option<&[T]>) {
let desc = gl_describe_format(self.gl(), texture.format);
match texture.target {
gl::TEXTURE_2D_ARRAY => {
self.gl.tex_image_3d(
gl::TEXTURE_2D_ARRAY,
0,
desc.internal,
texture.width as _,
texture.height as _,
texture.layer_count,
0,
desc.external,
desc.pixel_type,
- pixels,
+ pixels.map(texels_to_u8_slice),
);
}
gl::TEXTURE_2D | gl::TEXTURE_RECTANGLE | gl::TEXTURE_EXTERNAL_OES => {
self.gl.tex_image_2d(
texture.target,
0,
desc.internal,
texture.width as _,
texture.height as _,
0,
desc.external,
desc.pixel_type,
- pixels,
+ pixels.map(texels_to_u8_slice),
);
}
_ => panic!("BUG: Unexpected texture target!"),
}
}
pub fn blit_render_target(&mut self, src_rect: DeviceIntRect, dest_rect: DeviceIntRect) {
debug_assert!(self.inside_frame);
@@ -1283,16 +1300,23 @@ impl Device {
texture.width = 0;
texture.height = 0;
texture.layer_count = 0;
}
pub fn delete_texture(&mut self, mut texture: Texture) {
self.free_texture_storage(&mut texture);
self.gl.delete_textures(&[texture.id]);
+
+ for bound_texture in &mut self.bound_textures {
+ if *bound_texture == texture.id {
+ *bound_texture = 0
+ }
+ }
+
texture.id = 0;
}
#[cfg(feature = "replay")]
pub fn delete_external_texture(&mut self, mut external: ExternalTexture) {
self.bind_external_texture(DEFAULT_TEXTURE, &external);
//Note: the format descriptor here doesn't really matter
self.free_texture_storage_impl(external.target, FormatDesc {
@@ -1446,16 +1470,17 @@ impl Device {
if u_location != -1 {
self.bind_program(program);
self.gl
.uniform_1i(u_location, binding.1.into().0 as gl::GLint);
}
}
}
+ #[cfg(feature = "debug_renderer")]
pub fn get_uniform_location(&self, program: &Program, name: &str) -> UniformLocation {
UniformLocation(self.gl.get_uniform_location(program.id, name))
}
pub fn set_uniforms(
&self,
program: &Program,
transform: &Transform3D<f32>,
@@ -1513,16 +1538,17 @@ impl Device {
gl: &*self.gl,
texture,
},
buffer,
marker: PhantomData,
}
}
+ #[cfg(any(feature = "debug_renderer", feature = "capture"))]
pub fn read_pixels(&mut self, img_desc: &ImageDescriptor) -> Vec<u8> {
let desc = gl_describe_format(self.gl(), img_desc.format);
self.gl.read_pixels(
0, 0,
img_desc.width as i32,
img_desc.height as i32,
desc.external,
desc.pixel_type,
@@ -1559,16 +1585,17 @@ impl Device {
rect.size.height as _,
desc.external,
desc.pixel_type,
output,
);
}
/// Get texels of a texture into the specified output slice.
+ #[cfg(feature = "debug_renderer")]
pub fn get_tex_image_into(
&mut self,
texture: &Texture,
format: ImageFormat,
output: &mut [u8],
) {
self.bind_texture(DEFAULT_TEXTURE, texture);
let desc = gl_describe_format(self.gl(), format);
@@ -1577,16 +1604,17 @@ impl Device {
0,
desc.external,
desc.pixel_type,
output,
);
}
/// Attaches the provided texture to the current Read FBO binding.
+ #[cfg(any(feature = "debug_renderer", feature="capture"))]
fn attach_read_texture_raw(
&mut self, texture_id: gl::GLuint, target: gl::GLuint, layer_id: i32
) {
match target {
gl::TEXTURE_2D_ARRAY => {
self.gl.framebuffer_texture_layer(
gl::READ_FRAMEBUFFER,
gl::COLOR_ATTACHMENT0,
@@ -1603,22 +1631,24 @@ impl Device {
target,
texture_id,
0,
)
}
}
}
+ #[cfg(any(feature = "debug_renderer", feature="capture"))]
pub fn attach_read_texture_external(
&mut self, texture_id: gl::GLuint, target: TextureTarget, layer_id: i32
) {
self.attach_read_texture_raw(texture_id, get_gl_target(target), layer_id)
}
+ #[cfg(any(feature = "debug_renderer", feature="capture"))]
pub fn attach_read_texture(&mut self, texture: &Texture, layer_id: i32) {
self.attach_read_texture_raw(texture.id, texture.target, layer_id)
}
fn bind_vao_impl(&mut self, id: gl::GLuint) {
debug_assert!(self.inside_frame);
if self.bound_vao != id {
@@ -1844,31 +1874,33 @@ impl Device {
self.gl.draw_elements(
gl::TRIANGLES,
index_count,
gl::UNSIGNED_SHORT,
first_vertex as u32 * 2,
);
}
+ #[cfg(feature = "debug_renderer")]
pub fn draw_triangles_u32(&mut self, first_vertex: i32, index_count: i32) {
debug_assert!(self.inside_frame);
self.gl.draw_elements(
gl::TRIANGLES,
index_count,
gl::UNSIGNED_INT,
first_vertex as u32 * 4,
);
}
pub fn draw_nonindexed_points(&mut self, first_vertex: i32, vertex_count: i32) {
debug_assert!(self.inside_frame);
self.gl.draw_arrays(gl::POINTS, first_vertex, vertex_count);
}
+ #[cfg(feature = "debug_renderer")]
pub fn draw_nonindexed_lines(&mut self, first_vertex: i32, vertex_count: i32) {
debug_assert!(self.inside_frame);
self.gl.draw_arrays(gl::LINES, first_vertex, vertex_count);
}
pub fn draw_indexed_triangles_instanced_u16(&mut self, index_count: i32, instance_count: i32) {
debug_assert!(self.inside_frame);
self.gl.draw_elements_instanced(
@@ -2009,26 +2041,29 @@ impl Device {
.blend_func_separate(gl::ZERO, gl::SRC_COLOR, gl::ZERO, gl::SRC_ALPHA);
self.gl.blend_equation(gl::FUNC_ADD);
}
pub fn set_blend_mode_max(&self) {
self.gl
.blend_func_separate(gl::ONE, gl::ONE, gl::ONE, gl::ONE);
self.gl.blend_equation_separate(gl::MAX, gl::FUNC_ADD);
}
+ #[cfg(feature = "debug_renderer")]
pub fn set_blend_mode_min(&self) {
self.gl
.blend_func_separate(gl::ONE, gl::ONE, gl::ONE, gl::ONE);
self.gl.blend_equation_separate(gl::MIN, gl::FUNC_ADD);
}
pub fn set_blend_mode_subpixel_pass0(&self) {
self.gl.blend_func(gl::ZERO, gl::ONE_MINUS_SRC_COLOR);
+ self.gl.blend_equation(gl::FUNC_ADD);
}
pub fn set_blend_mode_subpixel_pass1(&self) {
self.gl.blend_func(gl::ONE, gl::ONE);
+ self.gl.blend_equation(gl::FUNC_ADD);
}
pub fn set_blend_mode_subpixel_with_bg_color_pass0(&self) {
self.gl.blend_func_separate(gl::ZERO, gl::ONE_MINUS_SRC_COLOR, gl::ZERO, gl::ONE);
self.gl.blend_equation(gl::FUNC_ADD);
}
pub fn set_blend_mode_subpixel_with_bg_color_pass1(&self) {
self.gl.blend_func_separate(gl::ONE_MINUS_DST_ALPHA, gl::ONE, gl::ZERO, gl::ONE);
self.gl.blend_equation(gl::FUNC_ADD);
@@ -2041,16 +2076,17 @@ impl Device {
// color is an unpremultiplied color.
self.gl.blend_color(color.r, color.g, color.b, 1.0);
self.gl
.blend_func(gl::CONSTANT_COLOR, gl::ONE_MINUS_SRC_COLOR);
self.gl.blend_equation(gl::FUNC_ADD);
}
pub fn set_blend_mode_subpixel_dual_source(&self) {
self.gl.blend_func(gl::ONE, gl::ONE_MINUS_SRC1_COLOR);
+ self.gl.blend_equation(gl::FUNC_ADD);
}
pub fn supports_extension(&self, extension: &str) -> bool {
self.extensions.iter().any(|s| s == extension)
}
}
struct FormatDesc {
@@ -2255,8 +2291,14 @@ impl<'a> UploadTarget<'a> {
}
// Reset row length to 0, otherwise the stride would apply to all texture uploads.
if chunk.stride.is_some() {
self.gl.pixel_store_i(gl::UNPACK_ROW_LENGTH, 0 as _);
}
}
}
+
+fn texels_to_u8_slice<T: Texel>(texels: &[T]) -> &[u8] {
+ unsafe {
+ slice::from_raw_parts(texels.as_ptr() as *const u8, texels.len() * mem::size_of::<T>())
+ }
+}
--- a/gfx/webrender/src/display_list_flattener.rs
+++ b/gfx/webrender/src/display_list_flattener.rs
@@ -9,16 +9,17 @@ use api::{DevicePixelScale, DeviceUintRe
use api::{FilterOp, FontInstanceKey, FontRenderMode, GlyphInstance, GlyphOptions, GradientStop};
use api::{IframeDisplayItem, ImageKey, ImageRendering, ItemRange, LayerPoint, LayerPrimitiveInfo};
use api::{LayerRect, LayerSize, LayerVector2D, LayoutRect, LayoutSize, LayoutTransform};
use api::{LayoutVector2D, LineOrientation, LineStyle, LocalClip, PipelineId, PropertyBinding};
use api::{RepeatMode, ScrollFrameDisplayItem, ScrollPolicy, ScrollSensitivity, Shadow};
use api::{SpecificDisplayItem, StackingContext, StickyFrameDisplayItem, TexelRect, TileOffset};
use api::{TransformStyle, YuvColorSpace, YuvData};
use app_units::Au;
+use batch::BrushImageSourceKind;
use border::ImageBorderSegment;
use clip::{ClipRegion, ClipSource, ClipSources, ClipStore};
use clip_scroll_node::{ClipScrollNode, NodeType, StickyFrameInfo};
use clip_scroll_tree::{ClipChainIndex, ClipScrollNodeIndex, ClipScrollTree};
use euclid::{SideOffsets2D, vec2};
use frame_builder::{FrameBuilder, FrameBuilderConfig};
use glyph_rasterizer::FontInstance;
use hit_test::{HitTestingItem, HitTestingRun};
@@ -1050,17 +1051,21 @@ impl<'a> DisplayListFlattener<'a> {
None,
false,
pipeline_id,
current_reference_frame_index,
None,
true,
);
- let prim = BrushPrimitive::new_picture(container_index);
+ let prim = BrushPrimitive::new_picture(
+ container_index,
+ BrushImageSourceKind::Color,
+ LayerVector2D::zero(),
+ );
let prim_index = self.prim_store.add_primitive(
&LayerRect::zero(),
&max_clip,
is_backface_visible,
None,
None,
PrimitiveContainer::Brush(prim),
@@ -1103,29 +1108,63 @@ impl<'a> DisplayListFlattener<'a> {
Some(PictureCompositeMode::Filter(*filter)),
false,
pipeline_id,
current_reference_frame_index,
None,
true,
);
- let src_prim = BrushPrimitive::new_picture(src_pic_index);
+ // For drop shadows, add an extra brush::picture primitive
+ // that will draw the picture as an alpha mask.
+ let shadow_prim_index = match *filter {
+ FilterOp::DropShadow(offset, ..) => {
+ let shadow_prim = BrushPrimitive::new_picture(
+ src_pic_index,
+ BrushImageSourceKind::ColorAlphaMask,
+ offset,
+ );
+ Some(self.prim_store.add_primitive(
+ &LayerRect::zero(),
+ &max_clip,
+ is_backface_visible,
+ None,
+ None,
+ PrimitiveContainer::Brush(shadow_prim),
+ ))
+ }
+ _ => {
+ None
+ }
+ };
+ let src_prim = BrushPrimitive::new_picture(
+ src_pic_index,
+ BrushImageSourceKind::Color,
+ LayoutVector2D::zero(),
+ );
let src_prim_index = self.prim_store.add_primitive(
&LayerRect::zero(),
&max_clip,
is_backface_visible,
None,
None,
PrimitiveContainer::Brush(src_prim),
);
let parent_pic = &mut self.prim_store.pictures[parent_pic_index.0];
parent_pic_index = src_pic_index;
+
+ if let Some(shadow_prim_index) = shadow_prim_index {
+ parent_pic.add_primitive(
+ shadow_prim_index,
+ clip_and_scroll,
+ );
+ }
+
parent_pic.add_primitive(
src_prim_index,
clip_and_scroll,
);
self.picture_stack.push(src_pic_index);
}
@@ -1135,17 +1174,21 @@ impl<'a> DisplayListFlattener<'a> {
Some(PictureCompositeMode::MixBlend(mix_blend_mode)),
false,
pipeline_id,
current_reference_frame_index,
None,
true,
);
- let src_prim = BrushPrimitive::new_picture(src_pic_index);
+ let src_prim = BrushPrimitive::new_picture(
+ src_pic_index,
+ BrushImageSourceKind::Color,
+ LayoutVector2D::zero(),
+ );
let src_prim_index = self.prim_store.add_primitive(
&LayerRect::zero(),
&max_clip,
is_backface_visible,
None,
None,
PrimitiveContainer::Brush(src_prim),
@@ -1190,17 +1233,21 @@ impl<'a> DisplayListFlattener<'a> {
participating_in_3d_context,
pipeline_id,
current_reference_frame_index,
frame_output_pipeline_id,
true,
);
// Create a brush primitive that draws this picture.
- let sc_prim = BrushPrimitive::new_picture(pic_index);
+ let sc_prim = BrushPrimitive::new_picture(
+ pic_index,
+ BrushImageSourceKind::Color,
+ LayoutVector2D::zero(),
+ );
// Add the brush to the parent picture.
let sc_prim_index = self.prim_store.add_primitive(
&LayerRect::zero(),
&max_clip,
is_backface_visible,
None,
None,
@@ -1405,31 +1452,41 @@ impl<'a> DisplayListFlattener<'a> {
let current_reference_frame_index = self.current_reference_frame_index();
let max_clip = LayerRect::max_rect();
// Quote from https://drafts.csswg.org/css-backgrounds-3/#shadow-blur
// "the image that would be generated by applying to the shadow a
// Gaussian blur with a standard deviation equal to half the blur radius."
let std_deviation = shadow.blur_radius * 0.5;
+ // If the shadow has no blur, any elements will get directly rendered
+ // into the parent picture surface, instead of allocating and drawing
+ // into an intermediate surface. In this case, we will need to apply
+ // the local clip rect to primitives.
+ let apply_local_clip_rect = shadow.blur_radius == 0.0;
+
// Create a picture that the shadow primitives will be added to. If the
// blur radius is 0, the code in Picture::prepare_for_render will
// detect this and mark the picture to be drawn directly into the
// parent picture, which avoids an intermediate surface and blur.
let shadow_pic_index = self.prim_store.add_image_picture(
Some(PictureCompositeMode::Filter(FilterOp::Blur(std_deviation))),
false,
pipeline_id,
current_reference_frame_index,
None,
- false,
+ apply_local_clip_rect,
);
// Create the primitive to draw the shadow picture into the scene.
- let shadow_prim = BrushPrimitive::new_picture(shadow_pic_index);
+ let shadow_prim = BrushPrimitive::new_picture(
+ shadow_pic_index,
+ BrushImageSourceKind::Color,
+ LayoutVector2D::zero(),
+ );
let shadow_prim_index = self.prim_store.add_primitive(
&LayerRect::zero(),
&max_clip,
info.is_backface_visible,
None,
None,
PrimitiveContainer::Brush(shadow_prim),
);
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -1,33 +1,33 @@
/* 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::{BuiltDisplayList, ColorF, DeviceIntPoint, DeviceIntRect, DevicePixelScale};
use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, DocumentLayer, FontRenderMode};
-use api::{LayerRect, LayerSize, PipelineId, PremultipliedColorF, WorldPoint};
+use api::{LayerRect, LayerSize, PipelineId, WorldPoint};
use clip::{ClipChain, ClipStore};
use clip_scroll_node::{ClipScrollNode};
use clip_scroll_tree::{ClipScrollNodeIndex, ClipScrollTree};
use display_list_flattener::{DisplayListFlattener};
use gpu_cache::GpuCache;
use gpu_types::{ClipChainRectIndex, ClipScrollNodeData};
use hit_test::{HitTester, HitTestingRun};
use internal_types::{FastHashMap};
use prim_store::{CachedGradient, PrimitiveIndex, PrimitiveRun, PrimitiveStore};
use profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters};
use render_backend::FrameId;
-use render_task::{ClearMode, RenderTask, RenderTaskId, RenderTaskLocation, RenderTaskTree};
+use render_task::{RenderTask, RenderTaskId, RenderTaskLocation, RenderTaskTree};
use resource_cache::{ResourceCache};
use scene::{ScenePipeline, SceneProperties};
use std::{mem, f32};
use std::sync::Arc;
-use tiling::{Frame, RenderPass, RenderPassKind, RenderTargetContext, RenderTargetKind};
-use tiling::ScrollbarPrimitive;
+use tiling::{Frame, RenderPass, RenderPassKind, RenderTargetContext};
+use tiling::{ScrollbarPrimitive, SpecialRenderPasses};
use util::{self, MaxRect, WorldToLayerFastTransform};
#[derive(Clone, Copy)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct FrameBuilderConfig {
pub enable_scrollbars: bool,
pub default_font_render_mode: FontRenderMode,
@@ -60,16 +60,17 @@ pub struct FrameBuildingContext<'a> {
pub struct FrameBuildingState<'a> {
pub render_tasks: &'a mut RenderTaskTree,
pub profile_counters: &'a mut FrameProfileCounters,
pub clip_store: &'a mut ClipStore,
pub local_clip_rects: &'a mut Vec<LayerRect>,
pub resource_cache: &'a mut ResourceCache,
pub gpu_cache: &'a mut GpuCache,
pub cached_gradients: &'a mut [CachedGradient],
+ pub special_render_passes: &'a mut SpecialRenderPasses,
}
pub struct PictureContext<'a> {
pub pipeline_id: PipelineId,
pub prim_runs: Vec<PrimitiveRun>,
pub original_reference_frame_index: Option<ClipScrollNodeIndex>,
pub display_list: &'a BuiltDisplayList,
pub inv_world_transform: Option<WorldToLayerFastTransform>,
@@ -152,16 +153,17 @@ impl FrameBuilder {
/// primitives in screen space.
fn build_layer_screen_rects_and_cull_layers(
&mut self,
clip_scroll_tree: &ClipScrollTree,
pipelines: &FastHashMap<PipelineId, Arc<ScenePipeline>>,
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
render_tasks: &mut RenderTaskTree,
+ special_render_passes: &mut SpecialRenderPasses,
profile_counters: &mut FrameProfileCounters,
device_pixel_scale: DevicePixelScale,
scene_properties: &SceneProperties,
local_clip_rects: &mut Vec<LayerRect>,
node_data: &[ClipScrollNodeData],
) -> Option<RenderTaskId> {
profile_scope!("cull");
@@ -189,16 +191,17 @@ impl FrameBuilder {
let mut frame_state = FrameBuildingState {
render_tasks,
profile_counters,
clip_store: &mut self.clip_store,
local_clip_rects,
resource_cache,
gpu_cache,
+ special_render_passes,
cached_gradients: &mut self.cached_gradients,
};
let pic_context = PictureContext {
pipeline_id: root_clip_scroll_node.pipeline_id,
prim_runs: mem::replace(&mut self.prim_store.pictures[0].runs, Vec::new()),
original_reference_frame_index: None,
display_list,
@@ -218,20 +221,17 @@ impl FrameBuilder {
);
let pic = &mut self.prim_store.pictures[0];
pic.runs = pic_context.prim_runs;
let root_render_task = RenderTask::new_picture(
RenderTaskLocation::Fixed(frame_context.screen_rect),
PrimitiveIndex(0),
- RenderTargetKind::Color,
DeviceIntPoint::zero(),
- PremultipliedColorF::TRANSPARENT,
- ClearMode::Transparent,
pic_state.tasks,
);
let render_task_id = frame_state.render_tasks.add(root_render_task);
pic.surface = Some(render_task_id);
Some(render_task_id)
}
@@ -308,69 +308,79 @@ impl FrameBuilder {
&mut node_data,
scene_properties,
);
self.update_scroll_bars(clip_scroll_tree, gpu_cache);
let mut render_tasks = RenderTaskTree::new(frame_id);
+ let screen_size = self.screen_rect.size.to_i32();
+ let mut special_render_passes = SpecialRenderPasses::new(&screen_size);
+
let main_render_task_id = self.build_layer_screen_rects_and_cull_layers(
clip_scroll_tree,
pipelines,
resource_cache,
gpu_cache,
&mut render_tasks,
+ &mut special_render_passes,
&mut profile_counters,
device_pixel_scale,
scene_properties,
&mut clip_chain_local_clip_rects,
&node_data,
);
- let mut passes = Vec::new();
- resource_cache.block_until_all_resources_added(gpu_cache, texture_cache_profile);
+ resource_cache.block_until_all_resources_added(gpu_cache,
+ &mut render_tasks,
+ texture_cache_profile);
+
+ let mut passes = vec![
+ special_render_passes.alpha_glyph_pass,
+ special_render_passes.color_glyph_pass,
+ ];
if let Some(main_render_task_id) = main_render_task_id {
let mut required_pass_count = 0;
render_tasks.max_depth(main_render_task_id, 0, &mut required_pass_count);
assert_ne!(required_pass_count, 0);
// Do the allocations now, assigning each tile's tasks to a render
// pass and target as required.
for _ in 0 .. required_pass_count - 1 {
- passes.push(RenderPass::new_off_screen(self.screen_rect.size.to_i32()));
+ passes.push(RenderPass::new_off_screen(screen_size));
}
- passes.push(RenderPass::new_main_framebuffer(self.screen_rect.size.to_i32()));
+ passes.push(RenderPass::new_main_framebuffer(screen_size));
render_tasks.assign_to_passes(
main_render_task_id,
required_pass_count - 1,
- &mut passes,
+ &mut passes[2..],
);
}
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 in &mut passes {
- let ctx = RenderTargetContext {
+ let mut ctx = RenderTargetContext {
device_pixel_scale,
prim_store: &self.prim_store,
resource_cache,
clip_scroll_tree,
use_dual_source_blending,
node_data: &node_data,
cached_gradients: &self.cached_gradients,
};
pass.build(
- &ctx,
+ &mut ctx,
gpu_cache,
&mut render_tasks,
&mut deferred_resolves,
&self.clip_store,
);
if let RenderPassKind::OffScreen { ref texture_cache, .. } = pass.kind {
has_texture_cache_tasks |= !texture_cache.is_empty();
--- a/gfx/webrender/src/glyph_cache.rs
+++ b/gfx/webrender/src/glyph_cache.rs
@@ -1,37 +1,95 @@
/* 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/. */
+#[cfg(feature = "pathfinder")]
+use api::DeviceIntPoint;
use api::GlyphKey;
use glyph_rasterizer::{FontInstance, GlyphFormat};
use internal_types::FastHashMap;
+use render_task::RenderTaskCache;
+#[cfg(feature = "pathfinder")]
+use render_task::RenderTaskCacheKey;
use resource_cache::ResourceClassCache;
-use texture_cache::{TextureCache, TextureCacheHandle, EvictionNotice};
+use std::sync::Arc;
+use texture_cache::{EvictionNotice, TextureCache};
+#[cfg(not(feature = "pathfinder"))]
+use texture_cache::TextureCacheHandle;
+#[cfg(feature = "pathfinder")]
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+#[derive(Clone, Debug)]
+pub struct CachedGlyphInfo {
+ pub render_task_cache_key: RenderTaskCacheKey,
+ pub format: GlyphFormat,
+ pub origin: DeviceIntPoint,
+}
+
+#[cfg(not(feature = "pathfinder"))]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct CachedGlyphInfo {
pub texture_cache_handle: TextureCacheHandle,
pub format: GlyphFormat,
}
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum GlyphCacheEntry {
// A glyph that has been successfully rasterized.
Cached(CachedGlyphInfo),
// A glyph that should not be rasterized (i.e. a space).
Blank,
// A glyph that has been submitted to the font backend for rasterization,
// but is still pending a result.
+ #[allow(dead_code)]
Pending,
}
+impl GlyphCacheEntry {
+ #[cfg(feature = "pathfinder")]
+ fn is_allocated(&self, texture_cache: &TextureCache, render_task_cache: &RenderTaskCache)
+ -> bool {
+ match *self {
+ GlyphCacheEntry::Cached(ref glyph) => {
+ let render_task_cache_key = &glyph.render_task_cache_key;
+ render_task_cache.cache_item_is_allocated_for_render_task(texture_cache,
+ &render_task_cache_key)
+ }
+ GlyphCacheEntry::Pending => true,
+ // If the cache only has blank glyphs left, just get rid of it.
+ GlyphCacheEntry::Blank => false,
+ }
+ }
+
+ #[cfg(not(feature = "pathfinder"))]
+ fn is_allocated(&self, texture_cache: &TextureCache, _: &RenderTaskCache) -> bool {
+ match *self {
+ GlyphCacheEntry::Cached(ref glyph) => {
+ texture_cache.is_allocated(&glyph.texture_cache_handle)
+ }
+ GlyphCacheEntry::Pending => true,
+ // If the cache only has blank glyphs left, just get rid of it.
+ GlyphCacheEntry::Blank => false,
+ }
+ }
+}
+
+#[allow(dead_code)]
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+#[derive(Clone)]
+pub enum CachedGlyphData {
+ Memory(Arc<Vec<u8>>),
+ Gpu,
+}
+
pub type GlyphKeyCache = ResourceClassCache<GlyphKey, GlyphCacheEntry, EvictionNotice>;
impl GlyphKeyCache {
pub fn eviction_notice(&self) -> &EvictionNotice {
&self.user_data
}
}
@@ -81,38 +139,36 @@ impl GlyphCache {
for key in caches_to_destroy {
let mut cache = self.glyph_key_caches.remove(&key).unwrap();
cache.clear();
}
}
// Clear out evicted entries from glyph key caches and, if possible,
// also remove entirely any subsequently empty glyph key caches.
- fn clear_evicted(&mut self, texture_cache: &TextureCache) {
+ fn clear_evicted(&mut self,
+ texture_cache: &TextureCache,
+ render_task_cache: &RenderTaskCache) {
self.glyph_key_caches.retain(|_, cache| {
// Scan for any glyph key caches that have evictions.
if cache.eviction_notice().check() {
// If there are evictions, filter out any glyphs evicted from the
// texture cache from the glyph key cache.
let mut keep_cache = false;
cache.retain(|_, entry| {
- let keep_glyph = match *entry {
- GlyphCacheEntry::Cached(ref glyph) =>
- texture_cache.is_allocated(&glyph.texture_cache_handle),
- GlyphCacheEntry::Pending => true,
- // If the cache only has blank glyphs left, just get rid of it.
- GlyphCacheEntry::Blank => false,
- };
+ let keep_glyph = entry.is_allocated(texture_cache, render_task_cache);
keep_cache |= keep_glyph;
keep_glyph
});
// Only keep the glyph key cache if it still has valid glyphs.
keep_cache
} else {
true
}
});
}
- pub fn begin_frame(&mut self, texture_cache: &TextureCache) {
- self.clear_evicted(texture_cache);
+ pub fn begin_frame(&mut self,
+ texture_cache: &TextureCache,
+ render_task_cache: &RenderTaskCache) {
+ self.clear_evicted(texture_cache, render_task_cache);
}
}
--- a/gfx/webrender/src/glyph_rasterizer.rs
+++ b/gfx/webrender/src/glyph_rasterizer.rs
@@ -2,36 +2,85 @@
* 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/. */
#[cfg(test)]
use api::{IdNamespace, LayoutPoint};
use api::{ColorF, ColorU};
use api::{FontInstanceFlags, FontInstancePlatformOptions};
use api::{FontKey, FontRenderMode, FontTemplate, FontVariation};
-use api::{GlyphDimensions, GlyphKey, SubpixelDirection};
-use api::{ImageData, ImageDescriptor, ImageFormat, LayerToWorldTransform};
+use api::{GlyphDimensions, GlyphKey, LayerToWorldTransform, SubpixelDirection};
+#[cfg(any(test, feature = "pathfinder"))]
+use api::DeviceIntSize;
+#[cfg(not(feature = "pathfinder"))]
+use api::{ImageData, ImageDescriptor, ImageFormat};
use app_units::Au;
+#[cfg(not(feature = "pathfinder"))]
use device::TextureFilter;
-use glyph_cache::{GlyphCache, GlyphCacheEntry, CachedGlyphInfo};
+#[cfg(feature = "pathfinder")]
+use euclid::{TypedPoint2D, TypedSize2D, TypedVector2D};
+use glyph_cache::{CachedGlyphInfo, GlyphCache, GlyphCacheEntry};
use gpu_cache::GpuCache;
use internal_types::ResourceCacheError;
+#[cfg(feature = "pathfinder")]
+use pathfinder_font_renderer;
+#[cfg(feature = "pathfinder")]
+use pathfinder_partitioner::mesh::Mesh as PathfinderMesh;
+#[cfg(feature = "pathfinder")]
+use pathfinder_path_utils::cubic_to_quadratic::CubicToQuadraticTransformer;
use platform::font::FontContext;
use profiler::TextureCacheProfileCounters;
use rayon::ThreadPool;
+#[cfg(not(feature = "pathfinder"))]
use rayon::prelude::*;
+#[cfg(test)]
+use render_backend::FrameId;
+use render_task::{RenderTaskCache, RenderTaskTree};
+#[cfg(feature = "pathfinder")]
+use render_task::{RenderTask, RenderTaskCacheKey, RenderTaskCacheKeyKind};
+#[cfg(feature = "pathfinder")]
+use render_task::{RenderTaskId, RenderTaskLocation};
+#[cfg(feature = "pathfinder")]
+use resource_cache::CacheItem;
use std::cmp;
use std::collections::hash_map::Entry;
+use std::f32;
use std::hash::{Hash, Hasher};
use std::mem;
use std::sync::{Arc, Mutex, MutexGuard};
use std::sync::mpsc::{channel, Receiver, Sender};
-use texture_cache::{TextureCache, TextureCacheHandle};
+use texture_cache::TextureCache;
+#[cfg(not(feature = "pathfinder"))]
+use texture_cache::TextureCacheHandle;
#[cfg(test)]
use thread_profiler::register_thread_with_profiler;
+#[cfg(feature = "pathfinder")]
+use tiling::RenderTargetKind;
+use tiling::SpecialRenderPasses;
+#[cfg(feature = "pathfinder")]
+use webrender_api::{DeviceIntPoint, DevicePixel};
+
+/// Should match macOS 10.13 High Sierra.
+///
+/// We multiply by sqrt(2) to compensate for the fact that dilation amounts are relative to the
+/// pixel square on macOS and relative to the vertex normal in Pathfinder.
+#[cfg(feature = "pathfinder")]
+const STEM_DARKENING_FACTOR_X: f32 = 0.0121 * f32::consts::SQRT_2;
+#[cfg(feature = "pathfinder")]
+const STEM_DARKENING_FACTOR_Y: f32 = 0.0121 * 1.25 * f32::consts::SQRT_2;
+
+/// Likewise, should match macOS 10.13 High Sierra.
+#[cfg(feature = "pathfinder")]
+const MAX_STEM_DARKENING_AMOUNT: f32 = 0.3 * f32::consts::SQRT_2;
+
+#[cfg(feature = "pathfinder")]
+const CUBIC_TO_QUADRATIC_APPROX_TOLERANCE: f32 = 0.01;
+
+#[cfg(feature = "pathfinder")]
+type PathfinderFontContext = pathfinder_font_renderer::FontContext<FontKey>;
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct FontTransform {
pub scale_x: f32,
pub skew_x: f32,
pub skew_y: f32,
@@ -74,40 +123,44 @@ impl FontTransform {
FontTransform::new(
(self.scale_x * Self::QUANTIZE_SCALE).round() / Self::QUANTIZE_SCALE,
(self.skew_x * Self::QUANTIZE_SCALE).round() / Self::QUANTIZE_SCALE,
(self.skew_y * Self::QUANTIZE_SCALE).round() / Self::QUANTIZE_SCALE,
(self.scale_y * Self::QUANTIZE_SCALE).round() / Self::QUANTIZE_SCALE,
)
}
+ #[cfg(not(feature = "pathfinder"))]
pub fn determinant(&self) -> f64 {
self.scale_x as f64 * self.scale_y as f64 - self.skew_y as f64 * self.skew_x as f64
}
+ #[cfg(not(feature = "pathfinder"))]
pub fn compute_scale(&self) -> Option<(f64, f64)> {
let det = self.determinant();
if det != 0.0 {
let x_scale = (self.scale_x as f64).hypot(self.skew_y as f64);
let y_scale = det.abs() / x_scale;
Some((x_scale, y_scale))
} else {
None
}
}
+ #[cfg(not(feature = "pathfinder"))]
pub fn pre_scale(&self, scale_x: f32, scale_y: f32) -> Self {
FontTransform::new(
self.scale_x * scale_x,
self.skew_x * scale_y,
self.skew_y * scale_x,
self.scale_y * scale_y,
)
}
+ #[cfg(not(feature = "pathfinder"))]
pub fn invert_scale(&self, x_scale: f64, y_scale: f64) -> Self {
self.pre_scale(x_scale.recip() as f32, y_scale.recip() as f32)
}
pub fn synthesize_italics(&self, skew_factor: f32) -> Self {
FontTransform::new(
self.scale_x,
self.skew_x - self.scale_x * skew_factor,
@@ -249,29 +302,37 @@ pub struct RasterizedGlyph {
pub height: u32,
pub scale: f32,
pub format: GlyphFormat,
pub bytes: Vec<u8>,
}
pub struct FontContexts {
// These worker are mostly accessed from their corresponding worker threads.
- // The goal is that there should be no noticeable contention on the muteces.
+ // The goal is that there should be no noticeable contention on the mutexes.
worker_contexts: Vec<Mutex<FontContext>>,
- // This worker should be accessed by threads that don't belong to thre thread pool
+ // This worker should be accessed by threads that don't belong to the thread pool
// (in theory that's only the render backend thread so no contention expected either).
shared_context: Mutex<FontContext>,
+ #[cfg(feature = "pathfinder")]
+ pathfinder_context: Box<Mutex<PathfinderFontContext>>,
+ #[cfg(not(feature = "pathfinder"))]
+ #[allow(dead_code)]
+ pathfinder_context: (),
+
// Stored here as a convenience to get the current thread index.
+ #[allow(dead_code)]
workers: Arc<ThreadPool>,
}
impl FontContexts {
/// Get access to the font context associated to the current thread.
+ #[cfg(not(feature = "pathfinder"))]
pub fn lock_current_context(&self) -> MutexGuard<FontContext> {
let id = self.current_worker_id();
self.lock_context(id)
}
/// Get access to any particular font context.
///
/// The id is ```Some(i)``` where i is an index between 0 and num_worker_contexts
@@ -284,73 +345,90 @@ impl FontContexts {
}
}
/// Get access to the font context usable outside of the thread pool.
pub fn lock_shared_context(&self) -> MutexGuard<FontContext> {
self.shared_context.lock().unwrap()
}
+ #[cfg(feature = "pathfinder")]
+ pub fn lock_pathfinder_context(&self) -> MutexGuard<PathfinderFontContext> {
+ self.pathfinder_context.lock().unwrap()
+ }
+
// number of contexts associated to workers
pub fn num_worker_contexts(&self) -> usize {
self.worker_contexts.len()
}
+ #[cfg(not(feature = "pathfinder"))]
fn current_worker_id(&self) -> Option<usize> {
self.workers.current_thread_index()
}
}
pub struct GlyphRasterizer {
+ #[allow(dead_code)]
workers: Arc<ThreadPool>,
font_contexts: Arc<FontContexts>,
// Maintain a set of glyphs that have been requested this
// frame. This ensures the glyph thread won't rasterize
// the same glyph more than once in a frame. This is required
// because the glyph cache hash table is not updated
// until the end of the frame when we wait for glyph requests
// to be resolved.
+ #[allow(dead_code)]
pending_glyphs: usize,
// Receives the rendered glyphs.
+ #[allow(dead_code)]
glyph_rx: Receiver<GlyphRasterJobs>,
+ #[allow(dead_code)]
glyph_tx: Sender<GlyphRasterJobs>,
// We defer removing fonts to the end of the frame so that:
// - this work is done outside of the critical path,
// - we don't have to worry about the ordering of events if a font is used on
// a frame where it is used (although it seems unlikely).
fonts_to_remove: Vec<FontKey>,
+
+ #[allow(dead_code)]
+ next_gpu_glyph_cache_key: GpuGlyphCacheKey,
}
impl GlyphRasterizer {
pub fn new(workers: Arc<ThreadPool>) -> Result<Self, ResourceCacheError> {
let (glyph_tx, glyph_rx) = channel();
let num_workers = workers.current_num_threads();
let mut contexts = Vec::with_capacity(num_workers);
let shared_context = FontContext::new()?;
for _ in 0 .. num_workers {
contexts.push(Mutex::new(FontContext::new()?));
}
+ let pathfinder_context = create_pathfinder_font_context()?;
+
Ok(GlyphRasterizer {
font_contexts: Arc::new(FontContexts {
worker_contexts: contexts,
shared_context: Mutex::new(shared_context),
+ pathfinder_context: pathfinder_context,
workers: Arc::clone(&workers),
}),
pending_glyphs: 0,
glyph_rx,
glyph_tx,
workers,
fonts_to_remove: Vec::new(),
+ next_gpu_glyph_cache_key: GpuGlyphCacheKey(0),
})
}
pub fn add_font(&mut self, font_key: FontKey, template: FontTemplate) {
let font_contexts = Arc::clone(&self.font_contexts);
// It's important to synchronously add the font for the shared context because
// we use it to check that fonts have been properly added when requesting glyphs.
font_contexts
@@ -363,33 +441,192 @@ impl GlyphRasterizer {
// before rendering a glyph.
// We can also move this into a worker to free up some cycles in the calling (render backend)
// thread.
for i in 0 .. font_contexts.num_worker_contexts() {
font_contexts
.lock_context(Some(i))
.add_font(&font_key, &template);
}
+
+ self.add_font_to_pathfinder(&font_key, &template);
}
+ #[cfg(feature = "pathfinder")]
+ fn add_font_to_pathfinder(&mut self, font_key: &FontKey, template: &FontTemplate) {
+ let font_contexts = Arc::clone(&self.font_contexts);
+ font_contexts.lock_pathfinder_context().add_font(&font_key, &template);
+ }
+
+ #[cfg(not(feature = "pathfinder"))]
+ fn add_font_to_pathfinder(&mut self, _: &FontKey, _: &FontTemplate) {}
+
pub fn delete_font(&mut self, font_key: FontKey) {
self.fonts_to_remove.push(font_key);
}
pub fn prepare_font(&self, font: &mut FontInstance) {
FontContext::prepare_font(font);
}
+ #[cfg(feature = "pathfinder")]
+ pub fn get_cache_item_for_glyph(&self,
+ glyph_key: &GlyphKey,
+ font: &FontInstance,
+ glyph_cache: &GlyphCache,
+ texture_cache: &TextureCache,
+ render_task_cache: &RenderTaskCache)
+ -> Option<(CacheItem, GlyphFormat)> {
+ let glyph_key_cache = glyph_cache.get_glyph_key_cache_for_font(font);
+ let render_task_cache_key = match *glyph_key_cache.get(glyph_key) {
+ GlyphCacheEntry::Cached(ref cached_glyph) => {
+ (*cached_glyph).render_task_cache_key.clone()
+ }
+ GlyphCacheEntry::Blank => return None,
+ GlyphCacheEntry::Pending => {
+ panic!("GlyphRasterizer::get_cache_item_for_glyph(): Glyph should have been \
+ cached by now!")
+ }
+ };
+ let cache_item = render_task_cache.get_cache_item_for_render_task(texture_cache,
+ &render_task_cache_key);
+ Some((cache_item, font.get_glyph_format()))
+ }
+
+ #[cfg(feature = "pathfinder")]
+ fn request_glyph_from_pathfinder_if_necessary(&mut self,
+ glyph_key: &GlyphKey,
+ font: &FontInstance,
+ cached_glyph_info: CachedGlyphInfo,
+ texture_cache: &mut TextureCache,
+ gpu_cache: &mut GpuCache,
+ render_task_cache: &mut RenderTaskCache,
+ render_task_tree: &mut RenderTaskTree,
+ render_passes: &mut SpecialRenderPasses)
+ -> Result<(CacheItem, GlyphFormat), ()> {
+ let mut pathfinder_font_context = self.font_contexts.lock_pathfinder_context();
+ let render_task_cache_key = cached_glyph_info.render_task_cache_key;
+ let (glyph_origin, glyph_size) = (cached_glyph_info.origin, render_task_cache_key.size);
+ let user_data = [glyph_origin.x as f32, (glyph_origin.y - glyph_size.height) as f32, 1.0];
+ let cache_item = try!(render_task_cache.request_render_task(render_task_cache_key,
+ texture_cache,
+ gpu_cache,
+ render_task_tree,
+ Some(user_data),
+ |render_tasks| {
+ // TODO(pcwalton): Non-subpixel font render mode.
+ request_render_task_from_pathfinder(glyph_key,
+ font,
+ &glyph_origin,
+ &glyph_size,
+ &mut *pathfinder_font_context,
+ font.render_mode,
+ render_tasks,
+ render_passes)
+ }));
+ Ok((cache_item, font.get_glyph_format()))
+ }
+
+ #[cfg(feature = "pathfinder")]
pub fn request_glyphs(
&mut self,
glyph_cache: &mut GlyphCache,
font: FontInstance,
glyph_keys: &[GlyphKey],
texture_cache: &mut TextureCache,
gpu_cache: &mut GpuCache,
+ render_task_cache: &mut RenderTaskCache,
+ render_task_tree: &mut RenderTaskTree,
+ render_passes: &mut SpecialRenderPasses,
+ ) {
+ debug_assert!(self.font_contexts.lock_shared_context().has_font(&font.font_key));
+
+ let glyph_key_cache = glyph_cache.get_glyph_key_cache_for_font_mut(font.clone());
+
+ // select glyphs that have not been requested yet.
+ for glyph_key in glyph_keys {
+ let mut cached_glyph_info = None;
+ match glyph_key_cache.entry(glyph_key.clone()) {
+ Entry::Occupied(mut entry) => {
+ let value = entry.into_mut();
+ match *value {
+ GlyphCacheEntry::Cached(ref glyph_info) => {
+ cached_glyph_info = Some(glyph_info.clone())
+ }
+ GlyphCacheEntry::Blank | GlyphCacheEntry::Pending => {}
+ }
+ }
+ Entry::Vacant(_) => {}
+ }
+
+ let cached_glyph_info = match cached_glyph_info {
+ Some(cached_glyph_info) => cached_glyph_info,
+ None => {
+ let mut pathfinder_font_context = self.font_contexts.lock_pathfinder_context();
+
+ let pathfinder_font_instance = pathfinder_font_renderer::FontInstance {
+ font_key: font.font_key.clone(),
+ size: font.size,
+ };
+
+ let pathfinder_subpixel_offset =
+ pathfinder_font_renderer::SubpixelOffset(glyph_key.subpixel_offset as u8);
+ let pathfinder_glyph_key =
+ pathfinder_font_renderer::GlyphKey::new(glyph_key.index,
+ pathfinder_subpixel_offset);
+ let glyph_dimensions =
+ match pathfinder_font_context.glyph_dimensions(&pathfinder_font_instance,
+ &pathfinder_glyph_key,
+ false) {
+ Ok(glyph_dimensions) => glyph_dimensions,
+ Err(_) => continue,
+ };
+
+ let cached_glyph_info = CachedGlyphInfo {
+ render_task_cache_key: RenderTaskCacheKey {
+ size: TypedSize2D::from_untyped(&glyph_dimensions.size.to_i32()),
+ kind: RenderTaskCacheKeyKind::Glyph(self.next_gpu_glyph_cache_key),
+ },
+ format: font.get_glyph_format(),
+ origin: DeviceIntPoint::new(glyph_dimensions.origin.x as i32,
+ -glyph_dimensions.origin.y as i32),
+ };
+ self.next_gpu_glyph_cache_key.0 += 1;
+ cached_glyph_info
+ }
+ };
+
+ let cache_entry =
+ match self.request_glyph_from_pathfinder_if_necessary(glyph_key,
+ &font,
+ cached_glyph_info.clone(),
+ texture_cache,
+ gpu_cache,
+ render_task_cache,
+ render_task_tree,
+ render_passes) {
+ Ok(_) => GlyphCacheEntry::Cached(cached_glyph_info),
+ Err(_) => GlyphCacheEntry::Blank,
+ };
+
+ glyph_key_cache.insert(glyph_key.clone(), cache_entry);
+ }
+ }
+
+ #[cfg(not(feature = "pathfinder"))]
+ pub fn request_glyphs(
+ &mut self,
+ glyph_cache: &mut GlyphCache,
+ font: FontInstance,
+ glyph_keys: &[GlyphKey],
+ texture_cache: &mut TextureCache,
+ gpu_cache: &mut GpuCache,
+ _: &mut RenderTaskCache,
+ _: &mut RenderTaskTree,
+ _: &mut SpecialRenderPasses,
) {
assert!(
self.font_contexts
.lock_shared_context()
.has_font(&font.font_key)
);
let mut new_glyphs = Vec::new();
@@ -403,56 +640,63 @@ impl GlyphRasterizer {
match *value {
GlyphCacheEntry::Cached(ref glyph) => {
// Skip the glyph if it is already has a valid texture cache handle.
if !texture_cache.request(&glyph.texture_cache_handle, gpu_cache) {
continue;
}
}
// Otherwise, skip the entry if it is blank or pending.
- GlyphCacheEntry::Blank |
- GlyphCacheEntry::Pending => continue,
+ GlyphCacheEntry::Blank | GlyphCacheEntry::Pending => continue,
}
+
// This case gets hit when we already rasterized the glyph, but the
// glyph has been evicted from the texture cache. Just force it to
// pending so it gets rematerialized.
*value = GlyphCacheEntry::Pending;
+ new_glyphs.push((*key).clone());
}
Entry::Vacant(entry) => {
// This is the first time we've seen the glyph, so mark it as pending.
entry.insert(GlyphCacheEntry::Pending);
+ new_glyphs.push((*key).clone());
}
}
-
- new_glyphs.push(key.clone());
}
if new_glyphs.is_empty() {
return;
}
self.pending_glyphs += 1;
+
+ self.request_glyphs_from_backend(font, new_glyphs);
+ }
+
+ #[cfg(not(feature = "pathfinder"))]
+ fn request_glyphs_from_backend(&mut self, font: FontInstance, glyphs: Vec<GlyphKey>) {
let font_contexts = Arc::clone(&self.font_contexts);
let glyph_tx = self.glyph_tx.clone();
+
// spawn an async task to get off of the render backend thread as early as
// possible and in that task use rayon's fork join dispatch to rasterize the
// glyphs in the thread pool.
self.workers.spawn(move || {
- let jobs = new_glyphs
+ let jobs = glyphs
.par_iter()
.map(|key: &GlyphKey| {
profile_scope!("glyph-raster");
let mut context = font_contexts.lock_current_context();
let job = GlyphRasterJob {
key: key.clone(),
result: context.rasterize_glyph(&font, key),
};
// Sanity check.
- if let Some(ref glyph) = job.result {
+ if let GlyphRasterResult::Bitmap(ref glyph) = job.result {
let bpp = 4; // We always render glyphs in 32 bits RGBA format.
assert_eq!(
glyph.bytes.len(),
bpp * (glyph.width * glyph.height) as usize
);
}
job
@@ -474,47 +718,68 @@ impl GlyphRasterizer {
}
pub fn get_glyph_index(&mut self, font_key: FontKey, ch: char) -> Option<u32> {
self.font_contexts
.lock_shared_context()
.get_glyph_index(font_key, ch)
}
+ #[cfg(feature = "pathfinder")]
+ pub fn resolve_glyphs(
+ &mut self,
+ _: &mut GlyphCache,
+ _: &mut TextureCache,
+ _: &mut GpuCache,
+ _: &mut RenderTaskCache,
+ _: &mut RenderTaskTree,
+ _: &mut TextureCacheProfileCounters,
+ ) {
+ self.remove_dead_fonts();
+ }
+
+ #[cfg(not(feature = "pathfinder"))]
pub fn resolve_glyphs(
&mut self,
glyph_cache: &mut GlyphCache,
texture_cache: &mut TextureCache,
gpu_cache: &mut GpuCache,
- _texture_cache_profile: &mut TextureCacheProfileCounters,
+ _: &mut RenderTaskCache,
+ _: &mut RenderTaskTree,
+ _: &mut TextureCacheProfileCounters,
) {
- // Pull rasterized glyphs from the queue and Update the caches.
+ // Pull rasterized glyphs from the queue and update the caches.
while self.pending_glyphs > 0 {
self.pending_glyphs -= 1;
// TODO: rather than blocking until all pending glyphs are available
// we could try_recv and steal work from the thread pool to take advantage
// of the fact that this thread is alive and we avoid the added latency
// of blocking it.
let GlyphRasterJobs { font, mut jobs } = self.glyph_rx
.recv()
.expect("BUG: Should be glyphs pending!");
// Ensure that the glyphs are always processed in the same
// order for a given text run (since iterating a hash set doesn't
- // guarantee order). This can show up as very small float inaccuacry
+ // guarantee order). This can show up as very small float inaccuracy
// differences in rasterizers due to the different coordinates
// that text runs get associated with by the texture cache allocator.
jobs.sort_by(|a, b| a.key.cmp(&b.key));
let glyph_key_cache = glyph_cache.get_glyph_key_cache_for_font_mut(font);
for GlyphRasterJob { key, result } in jobs {
- let glyph_info = result.map_or(GlyphCacheEntry::Blank, |glyph| {
- if glyph.width > 0 && glyph.height > 0 {
+ let glyph_info = match result {
+ GlyphRasterResult::LoadFailed => GlyphCacheEntry::Blank,
+ GlyphRasterResult::Bitmap(ref glyph) if glyph.width == 0 ||
+ glyph.height == 0 => {
+ GlyphCacheEntry::Blank
+ }
+ GlyphRasterResult::Bitmap(glyph) => {
assert_eq!((glyph.left.fract(), glyph.top.fract()), (0.0, 0.0));
let mut texture_cache_handle = TextureCacheHandle::new();
texture_cache.request(&mut texture_cache_handle, gpu_cache);
texture_cache.update(
&mut texture_cache_handle,
ImageDescriptor {
width: glyph.width,
height: glyph.height,
@@ -530,64 +795,86 @@ impl GlyphRasterizer {
None,
gpu_cache,
Some(glyph_key_cache.eviction_notice()),
);
GlyphCacheEntry::Cached(CachedGlyphInfo {
texture_cache_handle,
format: glyph.format,
})
- } else {
- GlyphCacheEntry::Blank
}
- });
+ };
glyph_key_cache.insert(key, glyph_info);
}
}
// Now that we are done with the critical path (rendering the glyphs),
// we can schedule removing the fonts if needed.
- if !self.fonts_to_remove.is_empty() {
- let font_contexts = Arc::clone(&self.font_contexts);
- let fonts_to_remove = mem::replace(&mut self.fonts_to_remove, Vec::new());
- self.workers.spawn(move || {
+ self.remove_dead_fonts();
+ }
+
+ fn remove_dead_fonts(&mut self) {
+ if self.fonts_to_remove.is_empty() {
+ return
+ }
+
+ let font_contexts = Arc::clone(&self.font_contexts);
+ let fonts_to_remove = mem::replace(&mut self.fonts_to_remove, Vec::new());
+ self.workers.spawn(move || {
+ for font_key in &fonts_to_remove {
+ font_contexts.lock_shared_context().delete_font(font_key);
+ }
+ for i in 0 .. font_contexts.num_worker_contexts() {
+ let mut context = font_contexts.lock_context(Some(i));
for font_key in &fonts_to_remove {
- font_contexts.lock_shared_context().delete_font(font_key);
+ context.delete_font(font_key);
}
- for i in 0 .. font_contexts.num_worker_contexts() {
- let mut context = font_contexts.lock_context(Some(i));
- for font_key in &fonts_to_remove {
- context.delete_font(font_key);
- }
- }
- });
- }
+ }
+ });
}
#[cfg(feature = "replay")]
pub fn reset(&mut self) {
//TODO: any signals need to be sent to the workers?
self.pending_glyphs = 0;
self.fonts_to_remove.clear();
}
}
-impl FontContext {
+trait AddFont {
+ fn add_font(&mut self, font_key: &FontKey, template: &FontTemplate);
+}
+
+impl AddFont for FontContext {
fn add_font(&mut self, font_key: &FontKey, template: &FontTemplate) {
match template {
&FontTemplate::Raw(ref bytes, index) => {
self.add_raw_font(&font_key, bytes.clone(), index);
}
&FontTemplate::Native(ref native_font_handle) => {
self.add_native_font(&font_key, (*native_font_handle).clone());
}
}
}
}
+#[cfg(feature = "pathfinder")]
+impl AddFont for PathfinderFontContext {
+ fn add_font(&mut self, font_key: &FontKey, template: &FontTemplate) {
+ match template {
+ &FontTemplate::Raw(ref bytes, index) => {
+ drop(self.add_font_from_memory(&font_key, bytes.clone(), index));
+ }
+ &FontTemplate::Native(ref native_font_handle) => {
+ drop(self.add_native_font(&font_key, (*native_font_handle).clone().0));
+ }
+ }
+ }
+}
+
#[derive(Clone, Hash, PartialEq, Eq, Debug, Ord, PartialOrd)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct GlyphRequest {
pub key: GlyphKey,
pub font: FontInstance,
}
@@ -595,21 +882,51 @@ impl GlyphRequest {
pub fn new(font: &FontInstance, key: &GlyphKey) -> Self {
GlyphRequest {
key: key.clone(),
font: font.clone(),
}
}
}
+#[allow(dead_code)]
struct GlyphRasterJob {
key: GlyphKey,
- result: Option<RasterizedGlyph>,
+ result: GlyphRasterResult,
+}
+
+#[allow(dead_code)]
+pub enum GlyphRasterResult {
+ LoadFailed,
+ Bitmap(RasterizedGlyph),
}
+#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub struct GpuGlyphCacheKey(pub u32);
+
+#[cfg(feature = "pathfinder")]
+fn create_pathfinder_font_context() -> Result<Box<Mutex<PathfinderFontContext>>,
+ ResourceCacheError> {
+ match PathfinderFontContext::new() {
+ Ok(context) => Ok(Box::new(Mutex::new(context))),
+ Err(_) => {
+ let msg = "Failed to create the Pathfinder font context!".to_owned();
+ Err(ResourceCacheError::new(msg))
+ }
+ }
+}
+
+#[cfg(not(feature = "pathfinder"))]
+fn create_pathfinder_font_context() -> Result<(), ResourceCacheError> {
+ Ok(())
+}
+
+#[allow(dead_code)]
struct GlyphRasterJobs {
font: FontInstance,
jobs: Vec<GlyphRasterJob>,
}
#[test]
fn rasterize_200_glyphs() {
// This test loads a font from disc, the renders 4 requests containing
@@ -625,16 +942,19 @@ fn rasterize_200_glyphs() {
register_thread_with_profiler(format!("WRWorker#{}", idx));
})
.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 render_task_cache = RenderTaskCache::new();
+ let mut render_task_tree = RenderTaskTree::new(FrameId(0));
+ let mut special_render_passes = SpecialRenderPasses::new(&DeviceIntSize::new(1366, 768));
let mut font_file =
File::open("../wrench/reftests/text/VeraBd.ttf").expect("Couldn't open font file");
let mut font_data = vec![];
font_file
.read_to_end(&mut font_data)
.expect("failed to read font file");
@@ -665,20 +985,85 @@ fn rasterize_200_glyphs() {
for i in 0 .. 4 {
glyph_rasterizer.request_glyphs(
&mut glyph_cache,
font.clone(),
&glyph_keys[(50 * i) .. (50 * (i + 1))],
&mut texture_cache,
&mut gpu_cache,
+ &mut render_task_cache,
+ &mut render_task_tree,
+ &mut special_render_passes,
);
}
glyph_rasterizer.delete_font(font_key);
glyph_rasterizer.resolve_glyphs(
&mut glyph_cache,
&mut TextureCache::new(4096),
&mut gpu_cache,
+ &mut render_task_cache,
+ &mut render_task_tree,
&mut TextureCacheProfileCounters::new(),
);
}
+
+#[cfg(feature = "pathfinder")]
+fn compute_embolden_amount(ppem: f32) -> TypedVector2D<f32, DevicePixel> {
+ TypedVector2D::new(f32::min(ppem * STEM_DARKENING_FACTOR_X, MAX_STEM_DARKENING_AMOUNT),
+ f32::min(ppem * STEM_DARKENING_FACTOR_Y, MAX_STEM_DARKENING_AMOUNT))
+}
+
+#[cfg(feature = "pathfinder")]
+fn request_render_task_from_pathfinder(glyph_key: &GlyphKey,
+ font: &FontInstance,
+ glyph_origin: &DeviceIntPoint,
+ glyph_size: &DeviceIntSize,
+ font_context: &mut PathfinderFontContext,
+ render_mode: FontRenderMode,
+ render_tasks: &mut RenderTaskTree,
+ render_passes: &mut SpecialRenderPasses)
+ -> Result<(RenderTaskId, bool), ()> {
+ let pathfinder_font_instance = pathfinder_font_renderer::FontInstance {
+ font_key: font.font_key.clone(),
+ size: font.size,
+ };
+
+ let pathfinder_subpixel_offset =
+ pathfinder_font_renderer::SubpixelOffset(glyph_key.subpixel_offset as u8);
+ let glyph_subpixel_offset: f64 = glyph_key.subpixel_offset.into();
+ let pathfinder_glyph_key = pathfinder_font_renderer::GlyphKey::new(glyph_key.index,
+ pathfinder_subpixel_offset);
+
+ // TODO(pcwalton): Fall back to CPU rendering if Pathfinder fails to collect the outline.
+ let mut mesh = PathfinderMesh::new();
+ let outline = try!(font_context.glyph_outline(&pathfinder_font_instance,
+ &pathfinder_glyph_key));
+ let tolerance = CUBIC_TO_QUADRATIC_APPROX_TOLERANCE;
+ mesh.push_stencil_segments(CubicToQuadraticTransformer::new(outline.iter(), tolerance));
+ mesh.push_stencil_normals(CubicToQuadraticTransformer::new(outline.iter(), tolerance));
+
+ // FIXME(pcwalton): Support vertical subpixel offsets.
+ // FIXME(pcwalton): Embolden amount should be 0 on macOS if "Use LCD font
+ // smoothing" is unchecked in System Preferences.
+
+ let subpixel_offset = TypedPoint2D::new(glyph_subpixel_offset as f32, 0.0);
+ let embolden_amount = compute_embolden_amount(font.size.to_f32_px());
+
+ let location = RenderTaskLocation::Dynamic(None, *glyph_size);
+ let glyph_render_task = RenderTask::new_glyph(location,
+ mesh,
+ &glyph_origin,
+ &subpixel_offset,
+ font.render_mode,
+ &embolden_amount);
+
+ let root_task_id = render_tasks.add(glyph_render_task);
+ let render_pass = match render_mode {
+ FontRenderMode::Mono | FontRenderMode::Alpha => &mut render_passes.alpha_glyph_pass,
+ FontRenderMode::Subpixel => &mut render_passes.color_glyph_pass,
+ };
+ render_pass.add_render_task(root_task_id, *glyph_size, RenderTargetKind::Color);
+
+ Ok((root_task_id, false))
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/src/gpu_glyph_renderer.rs
@@ -0,0 +1,290 @@
+/* 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/. */
+
+//! GPU glyph rasterization using Pathfinder.
+
+use api::{DeviceIntPoint, DeviceIntRect, DeviceUintSize, FontRenderMode};
+use api::{ImageFormat, TextureTarget};
+use debug_colors;
+use device::{Device, Texture, TextureFilter, VAO};
+use euclid::{Point2D, Size2D, Transform3D, TypedVector2D, Vector2D};
+use internal_types::RenderTargetInfo;
+use pathfinder_gfx_utils::ShelfBinPacker;
+use profiler::GpuProfileTag;
+use renderer::{self, ImageBufferKind, Renderer, RendererError, RendererStats};
+use renderer::{TextureSampler, VertexArrayKind};
+use shade::{LazilyCompiledShader, ShaderKind};
+use tiling::GlyphJob;
+
+// The area lookup table in uncompressed grayscale TGA format (TGA image format 3).
+static AREA_LUT_TGA_BYTES: &'static [u8] = include_bytes!("../res/area-lut.tga");
+
+const HORIZONTAL_BIN_PADDING: i32 = 3;
+
+const GPU_TAG_GLYPH_STENCIL: GpuProfileTag = GpuProfileTag {
+ label: "Glyph Stencil",
+ color: debug_colors::STEELBLUE,
+};
+const GPU_TAG_GLYPH_COVER: GpuProfileTag = GpuProfileTag {
+ label: "Glyph Cover",
+ color: debug_colors::LIGHTSTEELBLUE,
+};
+
+pub struct GpuGlyphRenderer {
+ pub area_lut_texture: Texture,
+ pub vector_stencil_vao: VAO,
+ pub vector_cover_vao: VAO,
+
+ // These are Pathfinder shaders, used for rendering vector graphics.
+ vector_stencil: LazilyCompiledShader,
+ vector_cover: LazilyCompiledShader,
+}
+
+impl GpuGlyphRenderer {
+ pub fn new(device: &mut Device, prim_vao: &VAO, precache_shaders: bool)
+ -> Result<GpuGlyphRenderer, RendererError> {
+ // Make sure the area LUT is uncompressed grayscale TGA, 8bpp.
+ debug_assert!(AREA_LUT_TGA_BYTES[2] == 3);
+ debug_assert!(AREA_LUT_TGA_BYTES[16] == 8);
+ let area_lut_width = (AREA_LUT_TGA_BYTES[12] as u32) |
+ ((AREA_LUT_TGA_BYTES[13] as u32) << 8);
+ let area_lut_height = (AREA_LUT_TGA_BYTES[14] as u32) |
+ ((AREA_LUT_TGA_BYTES[15] as u32) << 8);
+ let area_lut_pixels =
+ &AREA_LUT_TGA_BYTES[18..(18 + area_lut_width * area_lut_height) as usize];
+
+ let mut area_lut_texture = device.create_texture(TextureTarget::Default, ImageFormat::R8);
+ device.init_texture(&mut area_lut_texture,
+ area_lut_width,
+ area_lut_height,
+ TextureFilter::Linear,
+ None,
+ 1,
+ Some(area_lut_pixels));
+
+ let vector_stencil_vao =
+ device.create_vao_with_new_instances(&renderer::desc::VECTOR_STENCIL, prim_vao);
+ let vector_cover_vao = device.create_vao_with_new_instances(&renderer::desc::VECTOR_COVER,
+ prim_vao);
+
+ // Load Pathfinder vector graphics shaders.
+ let vector_stencil = try!{
+ LazilyCompiledShader::new(ShaderKind::VectorStencil,
+ "pf_vector_stencil",
+ &[ImageBufferKind::Texture2D.get_feature_string()],
+ device,
+ precache_shaders)
+ };
+ let vector_cover = try!{
+ LazilyCompiledShader::new(ShaderKind::VectorCover,
+ "pf_vector_cover",
+ &[ImageBufferKind::Texture2D.get_feature_string()],
+ device,
+ precache_shaders)
+ };
+
+ Ok(GpuGlyphRenderer {
+ area_lut_texture,
+ vector_stencil_vao,
+ vector_cover_vao,
+ vector_stencil,
+ vector_cover,
+ })
+ }
+}
+
+impl Renderer {
+ /// Renders glyphs using the vector graphics shaders (Pathfinder).
+ pub fn stencil_glyphs(&mut self,
+ glyphs: &[GlyphJob],
+ projection: &Transform3D<f32>,
+ target_size: &DeviceUintSize,
+ stats: &mut RendererStats)
+ -> Option<StenciledGlyphPage> {
+ if glyphs.is_empty() {
+ return None
+ }
+
+ let _timer = self.gpu_profile.start_timer(GPU_TAG_GLYPH_STENCIL);
+
+ // Initialize temporary framebuffer.
+ // FIXME(pcwalton): Cache this!
+ // FIXME(pcwalton): Use RF32, not RGBAF32!
+ let mut current_page = StenciledGlyphPage {
+ texture: self.device.create_texture(TextureTarget::Default, ImageFormat::RGBAF32),
+ glyphs: vec![],
+ };
+ self.device.init_texture::<f32>(&mut current_page.texture,
+ target_size.width,
+ target_size.height,
+ TextureFilter::Nearest,
+ Some(RenderTargetInfo {
+ has_depth: false,
+ }),
+ 1,
+ None);
+
+ // Allocate all target rects.
+ let mut packer = ShelfBinPacker::new(&target_size.to_i32().to_untyped(),
+ &Vector2D::new(HORIZONTAL_BIN_PADDING, 0));
+ let mut glyph_indices: Vec<_> = (0..(glyphs.len())).collect();
+ glyph_indices.sort_by(|&a, &b| {
+ glyphs[b].target_rect.size.height.cmp(&glyphs[a].target_rect.size.height)
+ });
+ for &glyph_index in &glyph_indices {
+ let glyph = &glyphs[glyph_index];
+ let x_scale = x_scale_for_render_mode(glyph.render_mode);
+ let stencil_size = Size2D::new(glyph.target_rect.size.width * x_scale,
+ glyph.target_rect.size.height);
+ match packer.add(&stencil_size) {
+ Err(_) => return None,
+ Ok(origin) => {
+ current_page.glyphs.push(VectorCoverInstanceAttrs {
+ target_rect: glyph.target_rect,
+ stencil_origin: DeviceIntPoint::from_untyped(&origin),
+ subpixel: (glyph.render_mode == FontRenderMode::Subpixel) as u16,
+ })
+ }
+ }
+ }
+
+ // Initialize path info.
+ // TODO(pcwalton): Cache this texture!
+ let mut path_info_texture = self.device.create_texture(TextureTarget::Default,
+ ImageFormat::RGBAF32);
+
+ let mut path_info_texels = Vec::with_capacity(glyphs.len() * 12);
+ for (stenciled_glyph_index, &glyph_index) in glyph_indices.iter().enumerate() {
+ let glyph = &glyphs[glyph_index];
+ let stenciled_glyph = ¤t_page.glyphs[stenciled_glyph_index];
+ let x_scale = x_scale_for_render_mode(glyph.render_mode) as f32;
+ let glyph_origin = TypedVector2D::new(-glyph.origin.x as f32 * x_scale,
+ -glyph.origin.y as f32);
+ let subpixel_offset = TypedVector2D::new(glyph.subpixel_offset.x * x_scale,
+ glyph.subpixel_offset.y);
+ let rect = stenciled_glyph.stencil_rect()
+ .to_f32()
+ .translate(&glyph_origin)
+ .translate(&subpixel_offset);
+ path_info_texels.extend_from_slice(&[
+ x_scale, 0.0, 0.0, -1.0,
+ rect.origin.x, rect.max_y(), 0.0, 0.0,
+ rect.size.width, rect.size.height,
+ glyph.embolden_amount.x,
+ glyph.embolden_amount.y,
+ ]);
+ }
+
+ self.device.init_texture(&mut path_info_texture,
+ 3,
+ glyphs.len() as u32,
+ TextureFilter::Nearest,
+ None,
+ 1,
+ Some(&path_info_texels));
+
+ self.gpu_glyph_renderer.vector_stencil.bind(&mut self.device,
+ projection,
+ &mut self.renderer_errors);
+
+ self.device.bind_draw_target(Some((¤t_page.texture, 0)), Some(*target_size));
+ self.device.clear_target(Some([0.0, 0.0, 0.0, 0.0]), None, None);
+
+ self.device.set_blend(true);
+ self.device.set_blend_mode_subpixel_pass1();
+
+ let mut instance_data = vec![];
+ for (path_id, &glyph_id) in glyph_indices.iter().enumerate() {
+ let glyph = &glyphs[glyph_id];
+ instance_data.extend(glyph.mesh
+ .stencil_segments
+ .iter()
+ .zip(glyph.mesh.stencil_normals.iter())
+ .map(|(segment, normals)| {
+ VectorStencilInstanceAttrs {
+ from_position: segment.from,
+ ctrl_position: segment.ctrl,
+ to_position: segment.to,
+ from_normal: normals.from,
+ ctrl_normal: normals.ctrl,
+ to_normal: normals.to,
+ path_id: path_id as u16,
+ }
+ }));
+ }
+
+ self.device.bind_texture(TextureSampler::color(0),
+ &self.gpu_glyph_renderer.area_lut_texture);
+ self.device.bind_texture(TextureSampler::color(1), &path_info_texture);
+ self.draw_instanced_batch_with_previously_bound_textures(&instance_data,
+ VertexArrayKind::VectorStencil,
+ stats);
+
+ self.device.delete_texture(path_info_texture);
+
+ Some(current_page)
+ }
+
+ /// Blits glyphs from the stencil texture to the texture cache.
+ ///
+ /// Deletes the stencil texture at the end.
+ /// FIXME(pcwalton): This is bad. Cache it somehow.
+ pub fn cover_glyphs(&mut self,
+ stencil_page: StenciledGlyphPage,
+ projection: &Transform3D<f32>,
+ stats: &mut RendererStats) {
+ debug_assert!(!stencil_page.glyphs.is_empty());
+
+ let _timer = self.gpu_profile.start_timer(GPU_TAG_GLYPH_COVER);
+
+ self.gpu_glyph_renderer.vector_cover.bind(&mut self.device,
+ projection,
+ &mut self.renderer_errors);
+
+ self.device.bind_texture(TextureSampler::color(0), &stencil_page.texture);
+ self.draw_instanced_batch_with_previously_bound_textures(&stencil_page.glyphs,
+ VertexArrayKind::VectorCover,
+ stats);
+
+ self.device.delete_texture(stencil_page.texture);
+ }
+}
+
+#[derive(Clone, Copy, Debug)]
+#[repr(C)]
+struct VectorStencilInstanceAttrs {
+ from_position: Point2D<f32>,
+ ctrl_position: Point2D<f32>,
+ to_position: Point2D<f32>,
+ from_normal: Vector2D<f32>,
+ ctrl_normal: Vector2D<f32>,
+ to_normal: Vector2D<f32>,
+ path_id: u16,
+}
+
+pub struct StenciledGlyphPage {
+ texture: Texture,
+ glyphs: Vec<VectorCoverInstanceAttrs>,
+}
+
+#[derive(Clone, Copy, Debug)]
+#[repr(C)]
+struct VectorCoverInstanceAttrs {
+ target_rect: DeviceIntRect,
+ stencil_origin: DeviceIntPoint,
+ subpixel: u16,
+}
+
+impl VectorCoverInstanceAttrs {
+ fn stencil_rect(&self) -> DeviceIntRect {
+ DeviceIntRect::new(self.stencil_origin, self.target_rect.size)
+ }
+}
+
+fn x_scale_for_render_mode(render_mode: FontRenderMode) -> i32 {
+ match render_mode {
+ FontRenderMode::Subpixel => 3,
+ FontRenderMode::Mono | FontRenderMode::Alpha => 1,
+ }
+}
--- a/gfx/webrender/src/gpu_types.rs
+++ b/gfx/webrender/src/gpu_types.rs
@@ -190,17 +190,17 @@ impl From<CompositePrimitiveInstance> fo
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
+// TODO(gw): While we are converting things over, we
// need to have the instance be the same
// size as an old PrimitiveInstance. In the
// future, we can compress this vertex
// format a lot - e.g. z, render task
// addresses etc can reasonably become
// a u16 type.
#[repr(C)]
pub struct BrushInstance {
--- a/gfx/webrender/src/hit_test.rs
+++ b/gfx/webrender/src/hit_test.rs
@@ -52,24 +52,26 @@ impl HitTestClipChainDescriptor {
}
}
#[derive(Clone)]
pub struct HitTestingItem {
rect: LayerRect,
clip_rect: LayerRect,
tag: ItemTag,
+ is_backface_visible: bool,
}
impl HitTestingItem {
pub fn new(tag: ItemTag, info: &LayerPrimitiveInfo) -> HitTestingItem {
HitTestingItem {
rect: info.rect,
clip_rect: info.clip_rect,
tag: tag,
+ is_backface_visible: info.is_backface_visible,
}
}
}
#[derive(Clone)]
pub struct HitTestingRun(pub Vec<HitTestingItem>, pub ScrollNodeAndClipChain);
enum HitTestRegion {
@@ -227,16 +229,17 @@ impl HitTester {
let scroll_node = &self.nodes[scroll_node_id.0];
let pipeline_id = scroll_node.pipeline_id;
match (test.pipeline_id, pipeline_id) {
(Some(id), node_id) if node_id != id => continue,
_ => {},
}
let transform = scroll_node.world_content_transform;
+ let mut facing_backwards: Option<bool> = None; // will be computed on first use
let point_in_layer = match transform.inverse() {
Some(inverted) => inverted.transform_point2d(&point),
None => continue,
};
let mut clipped_in = false;
for item in items.iter().rev() {
if !item.rect.contains(&point_in_layer) ||
@@ -260,16 +263,23 @@ impl HitTester {
if !self.is_point_clipped_in_for_node(point, root_node_index, &mut test) {
continue;
}
let point_in_viewport = match test.node_cache[&root_node_index] {
Some(point) => point,
None => continue,
};
+ // Don't hit items with backface-visibility:hidden if they are facing the back.
+ if !item.is_backface_visible {
+ if *facing_backwards.get_or_insert_with(|| transform.is_backface_visible()) {
+ continue;
+ }
+ }
+
result.items.push(HitTestItem {
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;
--- a/gfx/webrender/src/lib.rs
+++ b/gfx/webrender/src/lib.rs
@@ -43,44 +43,50 @@ they're nestable.
#[macro_use]
extern crate bitflags;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
#[macro_use]
extern crate thread_profiler;
+#[macro_use]
+extern crate cfg_if;
#[cfg(any(feature = "debugger", feature = "capture", feature = "replay"))]
#[macro_use]
extern crate serde;
mod batch;
mod border;
mod box_shadow;
#[cfg(any(feature = "capture", feature = "replay"))]
mod capture;
mod clip;
mod clip_scroll_node;
mod clip_scroll_tree;
mod debug_colors;
+#[cfg(feature = "debug_renderer")]
mod debug_font_data;
+#[cfg(feature = "debug_renderer")]
mod debug_render;
#[cfg(feature = "debugger")]
mod debug_server;
mod device;
mod display_list_flattener;
mod ellipse;
mod frame_builder;
mod freelist;
#[cfg(any(target_os = "macos", target_os = "windows"))]
mod gamma_lut;
mod geometry;
mod glyph_cache;
mod glyph_rasterizer;
mod gpu_cache;
+#[cfg(feature = "pathfinder")]
+mod gpu_glyph_renderer;
mod gpu_types;
mod hit_test;
mod image;
mod internal_types;
mod picture;
mod prim_store;
mod print_tree;
mod profiler;
@@ -143,16 +149,24 @@ extern crate dwrote;
extern crate app_units;
extern crate bincode;
extern crate byteorder;
extern crate euclid;
extern crate fxhash;
extern crate gleam;
extern crate num_traits;
+#[cfg(feature = "pathfinder")]
+extern crate pathfinder_font_renderer;
+#[cfg(feature = "pathfinder")]
+extern crate pathfinder_gfx_utils;
+#[cfg(feature = "pathfinder")]
+extern crate pathfinder_partitioner;
+#[cfg(feature = "pathfinder")]
+extern crate pathfinder_path_utils;
extern crate plane_split;
extern crate rayon;
#[cfg(feature = "ron")]
extern crate ron;
#[cfg(feature = "debugger")]
extern crate serde_json;
extern crate smallvec;
extern crate time;
--- a/gfx/webrender/src/picture.rs
+++ b/gfx/webrender/src/picture.rs
@@ -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/. */
-use api::{FilterOp, MixBlendMode, PipelineId, PremultipliedColorF};
-use api::{DeviceIntRect, LayerRect, LayerToWorldScale};
+use api::{FilterOp, LayerVector2D, MixBlendMode, PipelineId, PremultipliedColorF};
+use api::{DeviceIntRect, LayerRect};
use box_shadow::{BLUR_SAMPLE_SCALE};
use clip_scroll_tree::ClipScrollNodeIndex;
use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureState};
-use gpu_cache::{GpuCacheHandle, GpuDataRequest};
+use gpu_cache::{GpuCacheHandle};
use prim_store::{PrimitiveIndex, PrimitiveRun, PrimitiveRunLocalRect};
use prim_store::{PrimitiveMetadata, ScrollNodeAndClipChain};
use render_task::{ClearMode, RenderTask};
use render_task::{RenderTaskId, RenderTaskLocation};
use scene::{FilterOpHelpers, SceneProperties};
use tiling::RenderTargetKind;
/*
@@ -154,50 +154,55 @@ impl PicturePrimitive {
self.real_local_rect = prim_run_rect.local_rect_in_original_parent_space;
match self.composite_mode {
Some(PictureCompositeMode::Filter(FilterOp::Blur(blur_radius))) => {
let inflate_size = (blur_radius * BLUR_SAMPLE_SCALE).ceil();
local_content_rect.inflate(inflate_size, inflate_size)
}
- Some(PictureCompositeMode::Filter(FilterOp::DropShadow(offset, blur_radius, _))) => {
- let inflate_size = blur_radius * BLUR_SAMPLE_SCALE;
+ Some(PictureCompositeMode::Filter(FilterOp::DropShadow(_, blur_radius, _))) => {
+ let inflate_size = (blur_radius * BLUR_SAMPLE_SCALE).ceil();
local_content_rect.inflate(inflate_size, inflate_size)
- .translate(&offset)
}
_ => {
local_content_rect
}
}
}
pub fn prepare_for_render(
&mut self,
prim_index: PrimitiveIndex,
prim_metadata: &mut PrimitiveMetadata,
pic_state_for_children: PictureState,
pic_state: &mut PictureState,
frame_context: &FrameBuildingContext,
frame_state: &mut FrameBuildingState,
) {
- let content_scale = LayerToWorldScale::new(1.0) * frame_context.device_pixel_scale;
let prim_screen_rect = prim_metadata
.screen_rect
.as_ref()
.expect("bug: trying to draw an off-screen picture!?");
+
+ // TODO(gw): Almost all of the Picture types below use extra_gpu_cache_data
+ // to store the same type of data. The exception is the filter
+ // with a ColorMatrix, which stores the color matrix here. It's
+ // probably worth tidying this code up to be a bit more consistent.
+ // Perhaps store the color matrix after the common data, even though
+ // it's not used by that shader.
let device_rect = match self.composite_mode {
Some(PictureCompositeMode::Filter(FilterOp::Blur(blur_radius))) => {
- // If blur radius is 0, we can skip drawing this an an
+ // If blur radius is 0, we can skip drawing this on an
// intermediate surface.
if blur_radius == 0.0 {
pic_state.tasks.extend(pic_state_for_children.tasks);
self.surface = None;
- DeviceIntRect::zero()
+ None
} else {
let blur_std_deviation = blur_radius * frame_context.device_pixel_scale.0;
let blur_range = (blur_std_deviation * BLUR_SAMPLE_SCALE).ceil() as i32;
// The clipped field is the part of the picture that is visible
// on screen. The unclipped field is the screen-space rect of
// the complete picture, if no screen / clip-chain was applied
// (this includes the extra space for blur region). To ensure
@@ -209,20 +214,17 @@ impl PicturePrimitive {
.clipped
.inflate(blur_range, blur_range)
.intersection(&prim_screen_rect.unclipped)
.unwrap();
let picture_task = RenderTask::new_picture(
RenderTaskLocation::Dynamic(None, device_rect.size),
prim_index,
- RenderTargetKind::Color,
device_rect.origin,
- PremultipliedColorF::TRANSPARENT,
- ClearMode::Transparent,
pic_state_for_children.tasks,
);
let picture_task_id = frame_state.render_tasks.add(picture_task);
let blur_render_task = RenderTask::new_blur(
blur_std_deviation,
picture_task_id,
@@ -230,155 +232,172 @@ impl PicturePrimitive {
RenderTargetKind::Color,
ClearMode::Transparent,
);
let render_task_id = frame_state.render_tasks.add(blur_render_task);
pic_state.tasks.push(render_task_id);
self.surface = Some(render_task_id);
- device_rect
+ Some(device_rect)
}
}
- Some(PictureCompositeMode::Filter(FilterOp::DropShadow(offset, blur_radius, _))) => {
- // TODO(gw): This is totally wrong and can never work with
- // transformed drop-shadow elements. Fix me!
- let rect = (prim_metadata.local_rect.translate(&-offset) * content_scale).round().to_i32();
+ Some(PictureCompositeMode::Filter(FilterOp::DropShadow(_, blur_radius, _))) => {
+ let blur_std_deviation = blur_radius * frame_context.device_pixel_scale.0;
+ let blur_range = (blur_std_deviation * BLUR_SAMPLE_SCALE).ceil() as i32;
+
+ // The clipped field is the part of the picture that is visible
+ // on screen. The unclipped field is the screen-space rect of
+ // the complete picture, if no screen / clip-chain was applied
+ // (this includes the extra space for blur region). To ensure
+ // that we draw a large enough part of the picture to get correct
+ // blur results, inflate that clipped area by the blur range, and
+ // then intersect with the total screen rect, to minimize the
+ // allocation size.
+ let device_rect = prim_screen_rect
+ .clipped
+ .inflate(blur_range, blur_range)
+ .intersection(&prim_screen_rect.unclipped)
+ .unwrap();
+
let mut picture_task = RenderTask::new_picture(
- RenderTaskLocation::Dynamic(None, rect.size),
+ RenderTaskLocation::Dynamic(None, device_rect.size),
prim_index,
- RenderTargetKind::Color,
- rect.origin,
- PremultipliedColorF::TRANSPARENT,
- ClearMode::Transparent,
+ device_rect.origin,
pic_state_for_children.tasks,
);
picture_task.mark_for_saving();
- let blur_std_deviation = blur_radius * frame_context.device_pixel_scale.0;
let picture_task_id = frame_state.render_tasks.add(picture_task);
let blur_render_task = RenderTask::new_blur(
blur_std_deviation.round(),
picture_task_id,
frame_state.render_tasks,
RenderTargetKind::Color,
ClearMode::Transparent,
);
self.secondary_render_task_id = Some(picture_task_id);
let render_task_id = frame_state.render_tasks.add(blur_render_task);
pic_state.tasks.push(render_task_id);
self.surface = Some(render_task_id);
- rect
+ Some(device_rect)
}
Some(PictureCompositeMode::MixBlend(..)) => {
let picture_task = RenderTask::new_picture(
RenderTaskLocation::Dynamic(None, prim_screen_rect.clipped.size),
prim_index,
- RenderTargetKind::Color,
prim_screen_rect.clipped.origin,
- PremultipliedColorF::TRANSPARENT,
- ClearMode::Transparent,
pic_state_for_children.tasks,
);
let readback_task_id = frame_state.render_tasks.add(
RenderTask::new_readback(prim_screen_rect.clipped)
);
self.secondary_render_task_id = Some(readback_task_id);
pic_state.tasks.push(readback_task_id);
let render_task_id = frame_state.render_tasks.add(picture_task);
pic_state.tasks.push(render_task_id);
self.surface = Some(render_task_id);
- prim_screen_rect.clipped
+ Some(prim_screen_rect.clipped)
}
Some(PictureCompositeMode::Filter(filter)) => {
// If this filter is not currently going to affect
// the picture, just collapse this picture into the
// current render task. This most commonly occurs
// when opacity == 1.0, but can also occur on other
// filters and be a significant performance win.
if filter.is_noop() {
pic_state.tasks.extend(pic_state_for_children.tasks);
self.surface = None;
- } else {
- if let FilterOp::ColorMatrix(m) = filter {
- if let Some(mut request) = frame_state.gpu_cache.request(&mut self.extra_gpu_data_handle) {
- for i in 0..5 {
- request.push([m[i*4], m[i*4+1], m[i*4+2], m[i*4+3]]);
+ None
+ } else {
+ let device_rect = match filter {
+ FilterOp::ColorMatrix(m) => {
+ if let Some(mut request) = frame_state.gpu_cache.request(&mut self.extra_gpu_data_handle) {
+ for i in 0..5 {
+ request.push([m[i*4], m[i*4+1], m[i*4+2], m[i*4+3]]);
+ }
}
+
+ None
}
- }
+ _ => {
+ Some(prim_screen_rect.clipped)
+ }
+ };
let picture_task = RenderTask::new_picture(
RenderTaskLocation::Dynamic(None, prim_screen_rect.clipped.size),
prim_index,
- RenderTargetKind::Color,
prim_screen_rect.clipped.origin,
- PremultipliedColorF::TRANSPARENT,
- ClearMode::Transparent,
pic_state_for_children.tasks,
);
let render_task_id = frame_state.render_tasks.add(picture_task);
pic_state.tasks.push(render_task_id);
self.surface = Some(render_task_id);
+
+ device_rect
}
-
- prim_screen_rect.clipped
}
Some(PictureCompositeMode::Blit) => {
let picture_task = RenderTask::new_picture(
RenderTaskLocation::Dynamic(None, prim_screen_rect.clipped.size),
prim_index,
- RenderTargetKind::Color,
prim_screen_rect.clipped.origin,
- PremultipliedColorF::TRANSPARENT,
- ClearMode::Transparent,
pic_state_for_children.tasks,
);
let render_task_id = frame_state.render_tasks.add(picture_task);
pic_state.tasks.push(render_task_id);
self.surface = Some(render_task_id);
- prim_screen_rect.clipped
+ Some(prim_screen_rect.clipped)
}
None => {
pic_state.tasks.extend(pic_state_for_children.tasks);
self.surface = None;
- DeviceIntRect::zero()
+ None
}
};
- // If scrolling or property animation has resulted in the task
- // rect being different than last time, invalidate the GPU
- // cache entry for this picture to ensure that the correct
- // task rect is provided to the image shader.
- if self.task_rect != device_rect {
- frame_state.gpu_cache.invalidate(&prim_metadata.gpu_location);
- self.task_rect = device_rect;
+ // If this picture type uses the common / general GPU data
+ // format, then write it now.
+ if let Some(device_rect) = device_rect {
+ // If scrolling or property animation has resulted in the task
+ // rect being different than last time, invalidate the GPU
+ // cache entry for this picture to ensure that the correct
+ // task rect is provided to the image shader.
+ if self.task_rect != device_rect {
+ frame_state.gpu_cache.invalidate(&self.extra_gpu_data_handle);
+ self.task_rect = device_rect;
+ }
+
+ if let Some(mut request) = frame_state.gpu_cache.request(&mut self.extra_gpu_data_handle) {
+ request.push(self.task_rect.to_f32());
+
+ // TODO(gw): It would make the shaders a bit simpler if the offset
+ // was provided as part of the brush::picture instance,
+ // rather than in the Picture data itself.
+ let (offset, color) = match self.composite_mode {
+ Some(PictureCompositeMode::Filter(FilterOp::DropShadow(offset, _, color))) => {
+ (offset, color.premultiplied())
+ }
+ _ => {
+ (LayerVector2D::zero(), PremultipliedColorF::WHITE)
+ }
+ };
+
+ request.push([offset.x, offset.y, 0.0, 0.0]);
+ request.push(color);
+ }
}
}
-
- pub fn write_gpu_blocks(&self, request: &mut GpuDataRequest) {
- request.push(self.task_rect.to_f32());
-
- let color = match self.composite_mode {
- Some(PictureCompositeMode::Filter(FilterOp::DropShadow(_, _, color))) => {
- color.premultiplied()
- }
- _ => {
- PremultipliedColorF::WHITE
- }
- };
-
- request.push(color);
- }
}
--- a/gfx/webrender/src/platform/macos/font.rs
+++ b/gfx/webrender/src/platform/macos/font.rs
@@ -6,44 +6,53 @@ use api::{ColorU, FontKey, FontRenderMod
use api::{FontInstanceFlags, FontVariation, NativeFontHandle};
use api::{GlyphKey, SubpixelDirection};
use app_units::Au;
use core_foundation::array::{CFArray, CFArrayRef};
use core_foundation::base::TCFType;
use core_foundation::dictionary::CFDictionary;
use core_foundation::number::{CFNumber, CFNumberRef};
use core_foundation::string::{CFString, CFStringRef};
-use core_graphics::base::{kCGImageAlphaNoneSkipFirst, kCGImageAlphaPremultipliedFirst};
-use core_graphics::base::kCGBitmapByteOrder32Little;
+use core_graphics::base::{kCGImageAlphaNoneSkipFirst, kCGBitmapByteOrder32Little};
+#[cfg(not(feature = "pathfinder"))]
+use core_graphics::base::kCGImageAlphaPremultipliedFirst;
use core_graphics::color_space::CGColorSpace;
-use core_graphics::context::{CGContext, CGTextDrawingMode};
+use core_graphics::context::CGContext;
+#[cfg(not(feature = "pathfinder"))]
+use core_graphics::context::CGTextDrawingMode;
use core_graphics::data_provider::CGDataProvider;
use core_graphics::font::{CGFont, CGGlyph};
-use core_graphics::geometry::{CGAffineTransform, CGPoint, CGRect, CGSize};
+use core_graphics::geometry::{CGAffineTransform, CGPoint, CGSize};
+#[cfg(not(feature = "pathfinder"))]
+use core_graphics::geometry::CGRect;
use core_text;
use core_text::font::{CTFont, CTFontRef};
use core_text::font_descriptor::{kCTFontDefaultOrientation, kCTFontColorGlyphsTrait};
use gamma_lut::{ColorLut, GammaLut};
-use glyph_rasterizer::{FontInstance, FontTransform, GlyphFormat, RasterizedGlyph};
+use glyph_rasterizer::{FontInstance, FontTransform};
+#[cfg(not(feature = "pathfinder"))]
+use glyph_rasterizer::{GlyphFormat, GlyphRasterResult, RasterizedGlyph};
use internal_types::{FastHashMap, ResourceCacheError};
use std::collections::hash_map::Entry;
use std::sync::Arc;
pub struct FontContext {
cg_fonts: FastHashMap<FontKey, CGFont>,
ct_fonts: FastHashMap<(FontKey, Au, Vec<FontVariation>), CTFont>,
+ #[allow(dead_code)]
gamma_lut: GammaLut,
}
// core text is safe to use on multiple threads and non-shareable resources are
// all hidden inside their font context.
unsafe impl Send for FontContext {}
struct GlyphMetrics {
rasterized_left: i32,
+ #[allow(dead_code)]
rasterized_descent: i32,
rasterized_ascent: i32,
rasterized_width: u32,
rasterized_height: u32,
advance: f32,
}
// According to the Skia source code, there's no public API to
@@ -406,16 +415,17 @@ impl FontContext {
height: metrics.rasterized_height as u32,
advance: metrics.advance,
})
}
})
}
// Assumes the pixels here are linear values from CG
+ #[cfg(not(feature = "pathfinder"))]
fn gamma_correct_pixels(
&self,
pixels: &mut Vec<u8>,
render_mode: FontRenderMode,
color: ColorU,
) {
// Then convert back to gamma corrected values.
match render_mode {
@@ -473,26 +483,23 @@ impl FontContext {
font.color.quantized_ceil()
} else {
font.color.quantized_floor()
};
}
}
}
- pub fn rasterize_glyph(
- &mut self,
- font: &FontInstance,
- key: &GlyphKey,
- ) -> Option<RasterizedGlyph> {
+ #[cfg(not(feature = "pathfinder"))]
+ pub fn rasterize_glyph(&mut self, font: &FontInstance, key: &GlyphKey) -> GlyphRasterResult {
let (x_scale, y_scale) = font.transform.compute_scale().unwrap_or((1.0, 1.0));
let size = font.size.scale_by(y_scale as f32);
let ct_font = match self.get_ct_font(font.font_key, size, &font.variations) {
Some(font) => font,
- None => return None,
+ None => return GlyphRasterResult::LoadFailed,
};
let bitmap = is_bitmap_font(&ct_font);
let (mut shape, (x_offset, y_offset)) = if bitmap {
(FontTransform::identity(), (0.0, 0.0))
} else {
(font.transform.invert_scale(y_scale, y_scale), font.get_subpx_offset(key))
};
@@ -528,17 +535,17 @@ impl FontContext {
&ct_font,
transform.as_ref(),
glyph,
x_offset,
y_offset,
extra_strikes as f64 * pixel_step,
);
if metrics.rasterized_width == 0 || metrics.rasterized_height == 0 {
- return None;
+ return GlyphRasterResult::LoadFailed
}
// The result of this function, in all render modes, is going to be a
// BGRA surface with white text on transparency using premultiplied
// alpha. For subpixel text, the RGB values will be the mask value for
// the individual components. For bitmap glyphs, the RGB values will be
// the (premultiplied) color of the pixel. For Alpha and Mono, each
// pixel will have R==G==B==A at the end of this function.
@@ -708,17 +715,17 @@ impl FontContext {
self.gamma_correct_pixels(
&mut rasterized_pixels,
font.render_mode,
font.color,
);
}
}
- Some(RasterizedGlyph {
+ GlyphRasterResult::Bitmap(RasterizedGlyph {
left: metrics.rasterized_left as f32,
top: metrics.rasterized_ascent as f32,
width: metrics.rasterized_width,
height: metrics.rasterized_height,
scale: if bitmap { y_scale.recip() as f32 } else { 1.0 },
format: if bitmap { GlyphFormat::ColorBitmap } else { font.get_glyph_format() },
bytes: rasterized_pixels,
})
--- a/gfx/webrender/src/platform/unix/font.rs
+++ b/gfx/webrender/src/platform/unix/font.rs
@@ -12,17 +12,18 @@ use freetype::freetype::{FT_F26Dot6, FT_
use freetype::freetype::{FT_GlyphSlot, FT_LcdFilter, FT_New_Face, FT_New_Memory_Face};
use freetype::freetype::{FT_Init_FreeType, FT_Load_Glyph, FT_Render_Glyph};
use freetype::freetype::{FT_Library, FT_Outline_Get_CBox, FT_Set_Char_Size, FT_Select_Size};
use freetype::freetype::{FT_Fixed, FT_Matrix, FT_Set_Transform};
use freetype::freetype::{FT_LOAD_COLOR, FT_LOAD_DEFAULT, FT_LOAD_FORCE_AUTOHINT};
use freetype::freetype::{FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH, FT_LOAD_NO_AUTOHINT};
use freetype::freetype::{FT_LOAD_NO_BITMAP, FT_LOAD_NO_HINTING, FT_LOAD_VERTICAL_LAYOUT};
use freetype::freetype::{FT_FACE_FLAG_SCALABLE, FT_FACE_FLAG_FIXED_SIZES};
-use glyph_rasterizer::{FontInstance, GlyphFormat, RasterizedGlyph};
+use freetype::succeeded;
+use glyph_rasterizer::{FontInstance, GlyphFormat, GlyphRasterResult, RasterizedGlyph};
use internal_types::{FastHashMap, ResourceCacheError};
use std::{cmp, mem, ptr, slice};
use std::cmp::max;
use std::ffi::CString;
use std::sync::Arc;
// These constants are not present in the freetype
// bindings due to bindgen not handling the way
@@ -147,26 +148,26 @@ impl FontContext {
// Thus, the only reasonable way to guess padding is to unconditonally add it if
// subpixel AA is used.
let lcd_extra_pixels = 1;
let result = unsafe {
FT_Init_FreeType(&mut lib)
};
- if result.succeeded() {
+ if succeeded(result) {
Ok(FontContext {
lib,
faces: FastHashMap::default(),
lcd_extra_pixels,
})
} else {
// TODO(gw): Provide detailed error values.
Err(ResourceCacheError::new(
- format!("Failed to initialize FreeType - {}", result.0)
+ format!("Failed to initialize FreeType - {}", result)
))
}
}
pub fn has_font(&self, font_key: &FontKey) -> bool {
self.faces.contains_key(font_key)
}
@@ -177,17 +178,17 @@ impl FontContext {
FT_New_Memory_Face(
self.lib,
bytes.as_ptr(),
bytes.len() as FT_Long,
index as FT_Long,
&mut face,
)
};
- if result.succeeded() && !face.is_null() {
+ if succeeded(result) && !face.is_null() {
self.faces.insert(
*font_key,
Face {
face,
_bytes: Some(bytes),
},
);
} else {
@@ -204,17 +205,17 @@ impl FontContext {
let result = unsafe {
FT_New_Face(
self.lib,
pathname.as_ptr(),
native_font_handle.index as FT_Long,
&mut face,
)
};
- if result.succeeded() && !face.is_null() {
+ if succeeded(result) && !face.is_null() {
self.faces.insert(
*font_key,
Face {
face,
_bytes: None,
},
);
} else {
@@ -222,17 +223,17 @@ impl FontContext {
debug!("font={:?}, path={:?}", font_key, pathname);
}
}
}
pub fn delete_font(&mut self, font_key: &FontKey) {
if let Some(face) = self.faces.remove(font_key) {
let result = unsafe { FT_Done_Face(face.face) };
- assert!(result.succeeded());
+ assert!(succeeded(result));
}
}
fn load_glyph(&self, font: &FontInstance, glyph: &GlyphKey) -> Option<FT_GlyphSlot> {
debug_assert!(self.faces.contains_key(&font.font_key));
let face = self.faces.get(&font.font_key).unwrap();
let mut load_flags = FT_LOAD_DEFAULT;
@@ -311,21 +312,21 @@ impl FontContext {
(req_size * x_scale * 64.0 + 0.5) as FT_F26Dot6,
(req_size * y_scale * 64.0 + 0.5) as FT_F26Dot6,
0,
0,
)
}
};
- if result.succeeded() {
+ if succeeded(result) {
result = unsafe { FT_Load_Glyph(face.face, glyph.index as FT_UInt, load_flags as FT_Int32) };
};
- if result.succeeded() {
+ if succeeded(result) {
let slot = unsafe { (*face.face).glyph };
assert!(slot != ptr::null_mut());
if font.flags.contains(FontInstanceFlags::SYNTHETIC_BOLD) {
unsafe { FT_GlyphSlot_Embolden(slot) };
}
let format = unsafe { (*slot).format };
@@ -562,68 +563,65 @@ impl FontContext {
}
let render_mode = match (font.render_mode, font.subpx_dir) {
(FontRenderMode::Mono, _) => FT_Render_Mode::FT_RENDER_MODE_MONO,
(FontRenderMode::Alpha, _) => FT_Render_Mode::FT_RENDER_MODE_NORMAL,
(FontRenderMode::Subpixel, SubpixelDirection::Vertical) => FT_Render_Mode::FT_RENDER_MODE_LCD_V,
(FontRenderMode::Subpixel, _) => FT_Render_Mode::FT_RENDER_MODE_LCD,
};
let result = unsafe { FT_Render_Glyph(slot, render_mode) };
- if !result.succeeded() {
+ if !succeeded(result) {
error!("Unable to rasterize");
debug!(
"{:?} with {:?}, {:?}",
key,
render_mode,
result
);
false
} else {
true
}
}
- pub fn rasterize_glyph(
- &mut self,
- font: &FontInstance,
- key: &GlyphKey,
- ) -> Option<RasterizedGlyph> {
+ #[cfg(not(feature = "pathfinder"))]
+ pub fn rasterize_glyph(&mut self, font: &FontInstance, key: &GlyphKey) -> GlyphRasterResult {
let slot = match self.load_glyph(font, key) {
Some(slot) => slot,
- None => return None,
+ None => return GlyphRasterResult::LoadFailed,
};
// Get dimensions of the glyph, to see if we need to rasterize it.
let dimensions = match self.get_glyph_dimensions_impl(slot, font, key, false) {
Some(val) => val,
- None => return None,
+ None => return GlyphRasterResult::LoadFailed,
};
let GlyphDimensions { mut left, mut top, width, height, .. } = dimensions;
// For spaces and other non-printable characters, early out.
if width == 0 || height == 0 {
- return None;
+ return GlyphRasterResult::LoadFailed;
}
let format = unsafe { (*slot).format };
let mut scale = 1.0;
match format {
FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP => {
let y_size = unsafe { (*(*(*slot).face).size).metrics.y_ppem };
scale = font.size.to_f32_px() / y_size as f32;
}
FT_Glyph_Format::FT_GLYPH_FORMAT_OUTLINE => {
if !self.rasterize_glyph_outline(slot, font, key) {
- return None;
+ return GlyphRasterResult::LoadFailed;
}
}
_ => {
error!("Unsupported format");
debug!("format={:?}", format);
- return None;
+ return GlyphRasterResult::LoadFailed;
}
};
debug!(
"Rasterizing {:?} as {:?} with dimensions {:?}",
key,
font.render_mode,
dimensions
@@ -766,17 +764,17 @@ impl FontContext {
let glyph_format = match (pixel_mode, format) {
(FT_Pixel_Mode::FT_PIXEL_MODE_LCD, _) |
(FT_Pixel_Mode::FT_PIXEL_MODE_LCD_V, _) => font.get_subpixel_glyph_format(),
(FT_Pixel_Mode::FT_PIXEL_MODE_BGRA, _) => GlyphFormat::ColorBitmap,
(_, FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP) => GlyphFormat::Bitmap,
_ => font.get_alpha_glyph_format(),
};
- Some(RasterizedGlyph {
+ GlyphRasterResult::Bitmap(RasterizedGlyph {
left: left as f32,
top: top as f32,
width: actual_width as u32,
height: actual_height as u32,
scale,
format: glyph_format,
bytes: final_buffer,
})
--- a/gfx/webrender/src/platform/windows/font.rs
+++ b/gfx/webrender/src/platform/windows/font.rs
@@ -1,17 +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/. */
use api::{FontInstanceFlags, FontKey, FontRenderMode};
use api::{ColorU, GlyphDimensions, GlyphKey, SubpixelDirection};
use dwrote;
use gamma_lut::{ColorLut, GammaLut};
-use glyph_rasterizer::{FontInstance, FontTransform, GlyphFormat, RasterizedGlyph};
+use glyph_rasterizer::{FontInstance, FontTransform, GlyphFormat};
+use glyph_rasterizer::{GlyphRasterResult, RasterizedGlyph};
use internal_types::{FastHashMap, ResourceCacheError};
use std::collections::hash_map::Entry;
use std::sync::Arc;
lazy_static! {
static ref DEFAULT_FONT_DESCRIPTOR: dwrote::FontDescriptor = dwrote::FontDescriptor {
family_name: "Arial".to_owned(),
weight: dwrote::FontWeight::Regular,
@@ -356,21 +357,18 @@ impl FontContext {
font.color = font.color.luminance_color().quantize();
}
FontRenderMode::Subpixel => {
font.color = font.color.quantize();
}
}
}
- pub fn rasterize_glyph(
- &mut self,
- font: &FontInstance,
- key: &GlyphKey,
- ) -> Option<RasterizedGlyph> {
+ #[cfg(not(feature = "pathfinder"))]
+ pub fn rasterize_glyph(&mut self, font: &FontInstance, key: &GlyphKey) -> GlyphRasterResult {
let (.., y_scale) = font.transform.compute_scale().unwrap_or((1.0, 1.0));
let size = (font.size.to_f64_px() * y_scale) as f32;
let bitmaps = is_bitmap_font(font);
let (mut shape, (x_offset, y_offset)) = if bitmaps {
(FontTransform::identity(), (0.0, 0.0))
} else {
(font.transform.invert_scale(y_scale, y_scale), font.get_subpx_offset(key))
};
@@ -404,17 +402,17 @@ impl FontContext {
let bounds = analysis.get_alpha_texture_bounds(texture_type);
let width = (bounds.right - bounds.left) as u32;
let height = (bounds.bottom - bounds.top) as u32;
// Alpha texture bounds can sometimes return an empty rect
// Such as for spaces
if width == 0 || height == 0 {
- return None;
+ return GlyphRasterResult::LoadFailed;
}
let pixels = analysis.create_alpha_texture(texture_type, bounds);
let mut bgra_pixels = self.convert_to_bgra(&pixels, font.render_mode, bitmaps);
let lut_correction = match font.render_mode {
FontRenderMode::Mono => &self.gdi_gamma_lut,
FontRenderMode::Alpha | FontRenderMode::Subpixel => {
@@ -422,17 +420,17 @@ impl FontContext {
&self.gdi_gamma_lut
} else {
&self.gamma_lut
}
}
};
lut_correction.preblend(&mut bgra_pixels, font.color);
- Some(RasterizedGlyph {
+ GlyphRasterResult::Bitmap(RasterizedGlyph {
left: bounds.left as f32,
top: -bounds.top as f32,
width,
height,
scale: if bitmaps { y_scale.recip() as f32 } else { 1.0 },
format: if bitmaps { GlyphFormat::Bitmap } else { font.get_glyph_format() },
bytes: bgra_pixels,
})
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -2,33 +2,34 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{AlphaType, BorderRadius, BoxShadowClipMode, BuiltDisplayList, ClipMode, ColorF, ComplexClipRegion};
use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, Epoch, ExtendMode, FontRenderMode};
use api::{FilterOp, GlyphInstance, GlyphKey, GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag};
use api::{LayerPoint, LayerRect, LayerSize, LayerToWorldTransform, LayerVector2D};
use api::{PipelineId, PremultipliedColorF, Shadow, YuvColorSpace, YuvFormat};
+use batch::BrushImageSourceKind;
use border::{BorderCornerInstance, BorderEdgeKind};
use box_shadow::BLUR_SAMPLE_SCALE;
use clip_scroll_tree::{ClipChainIndex, ClipScrollNodeIndex, CoordinateSystemId};
use clip_scroll_node::ClipScrollNode;
use clip::{ClipChain, ClipChainNode, ClipChainNodeIter, ClipChainNodeRef, ClipSource};
use clip::{ClipSourcesHandle, ClipWorkItem};
use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureContext, PictureState};
use frame_builder::PrimitiveRunContext;
use glyph_rasterizer::{FontInstance, FontTransform};
use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest,
ToGpuBlocks};
use gpu_types::{ClipChainRectIndex};
use picture::{PictureCompositeMode, PicturePrimitive};
-use render_task::{BlitSource, RenderTask, RenderTaskCacheKey, RenderTaskCacheKeyKind};
-use render_task::RenderTaskId;
+use render_task::{BlitSource, RenderTask, RenderTaskCacheKey};
+use render_task::{RenderTaskCacheKeyKind, RenderTaskId};
use renderer::{MAX_VERTEX_TEXTURE_WIDTH};
-use resource_cache::{CacheItem, ImageProperties, ImageRequest, ResourceCache};
+use resource_cache::{CacheItem, ImageProperties, ImageRequest};
use segment::SegmentBuilder;
use std::{mem, usize};
use std::sync::Arc;
use util::{MatrixHelpers, WorldToLayerFastTransform, calculate_screen_bounding_rect};
use util::{pack_as_float, recycle_vec};
const MIN_BRUSH_SPLIT_AREA: f32 = 256.0 * 256.0;
@@ -196,16 +197,22 @@ pub struct PrimitiveMetadata {
#[derive(Debug)]
pub enum BrushKind {
Solid {
color: ColorF,
},
Clear,
Picture {
pic_index: PictureIndex,
+ // What kind of texels to sample from the
+ // picture (e.g color or alpha mask).
+ source_kind: BrushImageSourceKind,
+ // A local space offset to apply when drawing
+ // this picture.
+ local_offset: LayerVector2D,
},
Image {
request: ImageRequest,
current_epoch: Epoch,
alpha_type: AlphaType,
},
YuvImage {
yuv_key: [ImageKey; 3],
@@ -232,22 +239,25 @@ pub enum BrushKind {
end_point: LayerPoint,
}
}
impl BrushKind {
fn supports_segments(&self) -> bool {
match *self {
BrushKind::Solid { .. } |
- BrushKind::Picture { .. } |
BrushKind::Image { .. } |
BrushKind::YuvImage { .. } |
BrushKind::RadialGradient { .. } |
BrushKind::LinearGradient { .. } => true,
+ // TODO(gw): Allow batch.rs to add segment instances
+ // for Picture primitives.
+ BrushKind::Picture { .. } => false,
+
BrushKind::Clear => false,
}
}
}
bitflags! {
/// Each bit of the edge AA mask is:
/// 0, when the edge of the primitive needs to be considered for AA
@@ -312,41 +322,40 @@ impl BrushPrimitive {
segment_desc: Option<BrushSegmentDescriptor>,
) -> BrushPrimitive {
BrushPrimitive {
kind,
segment_desc,
}
}
- pub fn new_picture(pic_index: PictureIndex) -> BrushPrimitive {
+ pub fn new_picture(
+ pic_index: PictureIndex,
+ source_kind: BrushImageSourceKind,
+ local_offset: LayerVector2D,
+ ) -> BrushPrimitive {
BrushPrimitive {
kind: BrushKind::Picture {
pic_index,
+ source_kind,
+ local_offset,
},
segment_desc: None,
}
}
fn write_gpu_blocks(
&self,
request: &mut GpuDataRequest,
- pictures: &[PicturePrimitive],
) {
// has to match VECS_PER_SPECIFIC_BRUSH
match self.kind {
- BrushKind::Picture { pic_index } => {
- pictures[pic_index.0].write_gpu_blocks(request);
- }
- BrushKind::YuvImage { .. } => {
- }
- BrushKind::Image { .. } => {
- request.push([0.0; 4]);
- request.push(PremultipliedColorF::WHITE);
- }
+ BrushKind::Picture { .. } |
+ BrushKind::YuvImage { .. } |
+ BrushKind::Image { .. } => {}
BrushKind::Solid { color } => {
request.push(color.premultiplied());
}
BrushKind::Clear => {
// Opaque black with operator dest out
request.push(PremultipliedColorF::BLACK);
}
BrushKind::LinearGradient { start_point, end_point, extend_mode, .. } => {
@@ -645,21 +654,20 @@ impl TextRunPrimitiveCpu {
font.transform = FontTransform::from(&transform).quantize();
}
}
font
}
fn prepare_for_render(
&mut self,
- resource_cache: &mut ResourceCache,
device_pixel_scale: DevicePixelScale,
transform: Option<LayerToWorldTransform>,
display_list: &BuiltDisplayList,
- gpu_cache: &mut GpuCache,
+ frame_building_state: &mut FrameBuildingState,
) {
let font = self.get_font(device_pixel_scale, transform);
// Cache the glyph positions, if not in the cache already.
// TODO(gw): In the future, remove `glyph_instances`
// completely, and just reference the glyphs
// directly from the display list.
if self.glyph_keys.is_empty() {
@@ -688,17 +696,22 @@ impl TextRunPrimitiveCpu {
// Ensure the last block is added in the case
// of an odd number of glyphs.
if (self.glyph_keys.len() & 1) != 0 {
self.glyph_gpu_blocks.push(gpu_block.into());
}
}
- resource_cache.request_glyphs(font, &self.glyph_keys, gpu_cache);
+ frame_building_state.resource_cache
+ .request_glyphs(font,
+ &self.glyph_keys,
+ frame_building_state.gpu_cache,
+ frame_building_state.render_tasks,
+ frame_building_state.special_render_passes);
}
fn write_gpu_blocks(&self, request: &mut GpuDataRequest) {
request.push(ColorF::from(self.font.color).premultiplied());
// this is the only case where we need to provide plain color to GPU
let bg_color = ColorF::from(self.font.bg_color);
request.push([bg_color.r, bg_color.g, bg_color.b, 1.0]);
request.push([
@@ -1163,21 +1176,20 @@ impl PrimitiveStore {
let metadata = &mut self.cpu_metadata[prim_index.0];
match metadata.prim_kind {
PrimitiveKind::Border => {}
PrimitiveKind::TextRun => {
let text = &mut self.cpu_text_runs[metadata.cpu_prim_index.0];
// The transform only makes sense for screen space rasterization
let transform = Some(prim_run_context.scroll_node.world_content_transform.into());
text.prepare_for_render(
- frame_state.resource_cache,
frame_context.device_pixel_scale,
transform,
pic_context.display_list,
- frame_state.gpu_cache,
+ frame_state,
);
}
PrimitiveKind::Image => {
let image_cpu = &mut self.cpu_images[metadata.cpu_prim_index.0];
let image_properties = frame_state
.resource_cache
.get_image_properties(image_cpu.key.request.key);
@@ -1228,16 +1240,17 @@ impl PrimitiveStore {
// Request a pre-rendered image task.
*item = frame_state.resource_cache.request_render_task(
RenderTaskCacheKey {
size,
kind: RenderTaskCacheKeyKind::Image(key),
},
frame_state.gpu_cache,
frame_state.render_tasks,
+ None,
|render_tasks| {
// We need to render the image cache this frame,
// so will need access to the source texture.
request_source_image = true;
// Create a task to blit from the texture cache to
// a normal transient render task surface. This will
// copy only the sub-rect, if specified.
@@ -1342,26 +1355,52 @@ impl PrimitiveStore {
pic_context.display_list,
);
gradient_builder.build(
reverse_stops,
&mut request,
);
}
}
- BrushKind::Picture { pic_index } => {
- self.pictures[pic_index.0]
- .prepare_for_render(
- prim_index,
- metadata,
- pic_state_for_children,
- pic_state,
- frame_context,
- frame_state,
- );
+ BrushKind::Picture { pic_index, source_kind, .. } => {
+ let pic = &mut self.pictures[pic_index.0];
+ // If this picture is referenced by multiple brushes,
+ // we only want to prepare it once per frame. It
+ // should be prepared for the main color pass.
+ // TODO(gw): Make this a bit more explicit - perhaps
+ // we could mark which brush::picture is
+ // the owner of the picture, vs the shadow
+ // which is just referencing it.
+ match source_kind {
+ BrushImageSourceKind::Color => {
+ pic.prepare_for_render(
+ prim_index,
+ metadata,
+ pic_state_for_children,
+ pic_state,
+ frame_context,
+ frame_state,
+ );
+ }
+ BrushImageSourceKind::ColorAlphaMask => {
+ // Since we will always visit the shadow
+ // brush first, use this to clear out the
+ // render tasks from the previous frame.
+ // This ensures that if the primary brush
+ // is found to be non-visible, then we
+ // won't try to draw the drop-shadow either.
+ // This isn't quite correct - it can result
+ // in clipping artifacts if the image is
+ // off-screen, but the drop-shadow is
+ // partially visible - we can fix this edge
+ // case as a follow up.
+ pic.surface = None;
+ pic.secondary_render_task_id = None;
+ }
+ }
}
BrushKind::Solid { .. } |
BrushKind::Clear => {}
}
}
}
// Mark this GPU resource as required for this frame.
@@ -1380,17 +1419,17 @@ impl PrimitiveStore {
image.write_gpu_blocks(request);
}
PrimitiveKind::TextRun => {
let text = &self.cpu_text_runs[metadata.cpu_prim_index.0];
text.write_gpu_blocks(&mut request);
}
PrimitiveKind::Brush => {
let brush = &self.cpu_brushes[metadata.cpu_prim_index.0];
- brush.write_gpu_blocks(&mut request, &self.pictures);
+ brush.write_gpu_blocks(&mut request);
match brush.segment_desc {
Some(ref segment_desc) => {
for segment in &segment_desc.segments {
// has to match VECS_PER_SEGMENT
request.write_segment(segment.local_rect);
}
}
None => {
@@ -1792,17 +1831,17 @@ impl PrimitiveStore {
};
// If we have dependencies, we need to prepare them first, in order
// to know the actual rect of this primitive.
// For example, scrolling may affect the location of an item in
// local space, which may force us to render this item on a larger
// picture target, if being composited.
if let PrimitiveKind::Brush = prim_kind {
- if let BrushKind::Picture { pic_index } = self.cpu_brushes[cpu_prim_index.0].kind {
+ if let BrushKind::Picture { pic_index, local_offset, .. } = self.cpu_brushes[cpu_prim_index.0].kind {
let pic_context_for_children = {
let pic = &mut self.pictures[pic_index.0];
if !pic.resolve_scene_properties(frame_context.scene_properties) {
return None;
}
may_need_clip_mask = pic.composite_mode.is_some();
@@ -1847,17 +1886,21 @@ impl PrimitiveStore {
frame_state,
);
// Restore the dependencies (borrow check dance)
let pic = &mut self.pictures[pic_index.0];
pic.runs = pic_context_for_children.prim_runs;
let metadata = &mut self.cpu_metadata[prim_index.0];
- metadata.local_rect = pic.update_local_rect(result);
+ // Store local rect of the picture for this brush,
+ // also applying any local offset for the instance.
+ metadata.local_rect = pic
+ .update_local_rect(result)
+ .translate(&local_offset);
}
}
let (local_rect, unclipped_device_rect) = {
let metadata = &mut self.cpu_metadata[prim_index.0];
if metadata.local_rect.size.width <= 0.0 ||
metadata.local_rect.size.height <= 0.0 {
//warn!("invalid primitive rect {:?}", metadata.local_rect);
--- a/gfx/webrender/src/profiler.rs
+++ b/gfx/webrender/src/profiler.rs
@@ -1,36 +1,48 @@
/* 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, ColorU};
-use debug_render::DebugRenderer;
-use euclid::{Point2D, Rect, Size2D, vec2};
-use query::{GpuSampler, GpuTimer, NamedTag};
+use api::ColorF;
+use query::{GpuTimer, NamedTag};
use std::collections::vec_deque::VecDeque;
-use internal_types::FastHashMap;
-use renderer::MAX_VERTEX_TEXTURE_WIDTH;
-use std::{f32, mem};
+use std::f32;
use time::precise_time_ns;
-const GRAPH_WIDTH: f32 = 1024.0;
-const GRAPH_HEIGHT: f32 = 320.0;
-const GRAPH_PADDING: f32 = 8.0;
-const GRAPH_FRAME_HEIGHT: f32 = 16.0;
-const PROFILE_PADDING: f32 = 10.0;
+cfg_if! {
+ if #[cfg(feature = "debug_renderer")] {
+ use api::ColorU;
+ use debug_render::DebugRenderer;
+ use euclid::{Point2D, Rect, Size2D, vec2};
+ use query::GpuSampler;
+ use internal_types::FastHashMap;
+ use renderer::MAX_VERTEX_TEXTURE_WIDTH;
+ use std::mem;
+ }
+}
+
+cfg_if! {
+ if #[cfg(feature = "debug_renderer")] {
+ const GRAPH_WIDTH: f32 = 1024.0;
+ const GRAPH_HEIGHT: f32 = 320.0;
+ const GRAPH_PADDING: f32 = 8.0;
+ const GRAPH_FRAME_HEIGHT: f32 = 16.0;
+ const PROFILE_PADDING: f32 = 10.0;
+ }
+}
const ONE_SECOND_NS: u64 = 1000000000;
#[derive(Debug, Clone)]
pub struct GpuProfileTag {
pub label: &'static str,
pub color: ColorF,
}
-
+
impl NamedTag for GpuProfileTag {
fn get_label(&self) -> &str {
self.label
}
}
trait ProfileCounter {
fn description(&self) -> &'static str;
@@ -80,21 +92,23 @@ impl ProfileCounter for IntProfileCounte
self.description
}
fn value(&self) -> String {
format!("{}", self.value)
}
}
+#[cfg(feature = "debug_renderer")]
pub struct FloatProfileCounter {
description: &'static str,
value: f32,
}
+#[cfg(feature = "debug_renderer")]
impl ProfileCounter for FloatProfileCounter {
fn description(&self) -> &'static str {
self.description
}
fn value(&self) -> String {
format!("{:.2}", self.value)
}
@@ -484,33 +498,36 @@ impl RendererProfileTimers {
struct GraphStats {
min_value: f32,
mean_value: f32,
max_value: f32,
}
struct ProfileGraph {
+ #[cfg(feature = "debug_renderer")]
max_samples: usize,
values: VecDeque<f32>,
short_description: &'static str,
}
impl ProfileGraph {
+ #[cfg(feature = "debug_renderer")]
fn new(
max_samples: usize,
short_description: &'static str,
) -> Self {
ProfileGraph {
max_samples,
values: VecDeque::new(),
short_description,
}
}
+ #[cfg(feature = "debug_renderer")]
fn push(&mut self, ns: u64) {
let ms = ns as f64 / 1000000.0;
if self.values.len() == self.max_samples {
self.values.pop_back();
}
self.values.push_front(ms as f32);
}
@@ -529,16 +546,17 @@ impl ProfileGraph {
if !self.values.is_empty() {
stats.mean_value = stats.mean_value / self.values.len() as f32;
}
stats
}
+ #[cfg(feature = "debug_renderer")]
fn draw_graph(
&self,
x: f32,
y: f32,
description: &'static str,
debug_renderer: &mut DebugRenderer,
) -> Rect<f32> {
let size = Size2D::new(600.0, 120.0);
@@ -628,25 +646,28 @@ impl ProfileCounter for ProfileGraph {
self.short_description
}
fn value(&self) -> String {
format!("{:.2}ms", self.stats().mean_value)
}
}
+#[cfg(feature = "debug_renderer")]
struct GpuFrame {
total_time: u64,
samples: Vec<GpuTimer<GpuProfileTag>>,
}
+#[cfg(feature = "debug_renderer")]
struct GpuFrameCollection {
frames: VecDeque<GpuFrame>,
}
+#[cfg(feature = "debug_renderer")]
impl GpuFrameCollection {
fn new() -> Self {
GpuFrameCollection {
frames: VecDeque::new(),
}
}
fn push(&mut self, total_time: u64, samples: Vec<GpuTimer<GpuProfileTag>>) {
@@ -655,16 +676,17 @@ impl GpuFrameCollection {
}
self.frames.push_front(GpuFrame {
total_time,
samples,
});
}
}
+#[cfg(feature = "debug_renderer")]
impl GpuFrameCollection {
fn draw(&self, x: f32, y: f32, debug_renderer: &mut DebugRenderer) -> Rect<f32> {
let graph_rect = Rect::new(
Point2D::new(x, y),
Size2D::new(GRAPH_WIDTH, GRAPH_HEIGHT),
);
let bounding_rect = graph_rect.inflate(GRAPH_PADDING, GRAPH_PADDING);
@@ -745,33 +767,37 @@ impl GpuFrameCollection {
ColorU::new(255, 255, 0, 255),
);
}
bounding_rect
}
}
+#[cfg(feature = "debug_renderer")]
struct DrawState {
x_left: f32,
y_left: f32,
x_right: f32,
y_right: f32,
}
+#[cfg(feature = "debug_renderer")]
pub struct Profiler {
draw_state: DrawState,
backend_time: ProfileGraph,
compositor_time: ProfileGraph,
gpu_time: ProfileGraph,
gpu_frames: GpuFrameCollection,
ipc_time: ProfileGraph,
}
+#[cfg(feature = "debug_renderer")]
impl Profiler {
+
pub fn new() -> Self {
Profiler {
draw_state: DrawState {
x_left: 0.0,
y_left: 0.0,
x_right: 0.0,
y_right: 0.0,
},
--- a/gfx/webrender/src/query.rs
+++ b/gfx/webrender/src/query.rs
@@ -1,13 +1,14 @@
/* 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 gleam::gl;
+#[cfg(feature = "debug_renderer")]
use std::mem;
use std::rc::Rc;
use device::FrameId;
pub trait NamedTag {
fn get_label(&self) -> &str;
@@ -49,16 +50,17 @@ impl<T> QuerySet<T> {
assert_eq!(self.pending, 0);
self.set.get(self.data.len()).cloned().map(|query_id| {
self.data.push(value);
self.pending = query_id;
query_id
})
}
+ #[cfg(feature = "debug_renderer")]
fn take<F: Fn(&mut T, gl::GLuint)>(&mut self, fun: F) -> Vec<T> {
let mut data = mem::replace(&mut self.data, Vec::new());
for (value, &query) in data.iter_mut().zip(self.set.iter()) {
fun(value, query)
}
data
}
}
@@ -152,16 +154,17 @@ impl<T: NamedTag> GpuFrameProfile<T> {
if let Some(query) = self.samplers.add(GpuSampler { tag, count: 0 }) {
self.gl.begin_query(gl::SAMPLES_PASSED, query);
}
GpuSampleQuery
}
+ #[cfg(feature = "debug_renderer")]
fn build_samples(&mut self) -> (FrameId, Vec<GpuTimer<T>>, Vec<GpuSampler<T>>) {
debug_assert!(!self.inside_frame);
let gl = &self.gl;
(
self.frame_id,
self.timers.take(|timer, query| {
timer.time_ns = gl.get_query_object_ui64v(query, gl::QUERY_RESULT)
@@ -228,16 +231,17 @@ impl<T> GpuProfiler<T> {
pub fn disable_samplers(&mut self) {
for frame in &mut self.frames {
frame.disable_samplers();
}
}
}
impl<T: NamedTag> GpuProfiler<T> {
+ #[cfg(feature = "debug_renderer")]
pub fn build_samples(&mut self) -> (FrameId, Vec<GpuTimer<T>>, Vec<GpuSampler<T>>) {
self.frames[self.next_frame].build_samples()
}
pub fn begin_frame(&mut self, frame_id: FrameId) {
self.frames[self.next_frame].begin_frame(frame_id);
}
--- a/gfx/webrender/src/render_task.rs
+++ b/gfx/webrender/src/render_task.rs
@@ -1,30 +1,38 @@
/* 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, ImageDescriptor, ImageFormat};
-use api::{DeviceSize, PremultipliedColorF};
+use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceSize, ImageDescriptor, ImageFormat};
+#[cfg(feature = "pathfinder")]
+use api::FontRenderMode;
use box_shadow::{BoxShadowCacheKey};
use clip::{ClipSource, ClipStore, ClipWorkItem};
use clip_scroll_tree::CoordinateSystemId;
use device::TextureFilter;
+#[cfg(feature = "pathfinder")]
+use euclid::{TypedPoint2D, TypedVector2D};
+use glyph_rasterizer::GpuGlyphCacheKey;
use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
use gpu_types::{ImageSource, RasterizationSpace};
use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture};
+#[cfg(feature = "pathfinder")]
+use pathfinder_partitioner::mesh::Mesh;
use prim_store::{PrimitiveIndex, ImageCacheKey};
#[cfg(feature = "debugger")]
use print_tree::{PrintTreePrinter};
use render_backend::FrameId;
use resource_cache::{CacheItem, ResourceCache};
use std::{cmp, ops, usize, f32, i32};
use texture_cache::{TextureCache, TextureCacheHandle};
use tiling::{RenderPass, RenderTargetIndex};
use tiling::{RenderTargetKind};
+#[cfg(feature = "pathfinder")]
+use webrender_api::DevicePixel;
const FLOATS_PER_RENDER_TASK_INFO: usize = 8;
pub const MAX_BLUR_STD_DEVIATION: f32 = 4.0;
pub const MIN_DOWNSCALING_RT_SIZE: i32 = 128;
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
@@ -71,17 +79,17 @@ impl RenderTaskTree {
self.max_depth(*child, depth, max_depth);
}
}
pub fn assign_to_passes(
&self,
id: RenderTaskId,
pass_index: usize,
- passes: &mut Vec<RenderPass>,
+ passes: &mut [RenderPass],
) {
debug_assert_eq!(self.frame_id, id.1);
let task = &self.tasks[id.0 as usize];
for child in &task.children {
self.assign_to_passes(*child, pass_index - 1, passes);
}
@@ -168,19 +176,17 @@ pub struct ClipRegionTask {
pub clip_data_address: GpuCacheAddress,
}
#[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct PictureTask {
pub prim_index: PrimitiveIndex,
- pub target_kind: RenderTargetKind,
pub content_origin: DeviceIntPoint,
- pub color: PremultipliedColorF,
pub uv_rect_handle: GpuCacheHandle,
}
#[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct BlurTask {
pub blur_std_deviation: f32,
@@ -191,16 +197,35 @@ pub struct BlurTask {
impl BlurTask {
#[cfg(feature = "debugger")]
fn print_with<T: PrintTreePrinter>(&self, pt: &mut T) {
pt.add_item(format!("std deviation: {}", self.blur_std_deviation));
pt.add_item(format!("target: {:?}", self.target_kind));
}
}
+#[cfg(feature = "pathfinder")]
+#[derive(Debug)]
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub struct GlyphTask {
+ /// After job building, this becomes `None`.
+ pub mesh: Option<Mesh>,
+ pub origin: DeviceIntPoint,
+ pub subpixel_offset: TypedPoint2D<f32, DevicePixel>,
+ pub render_mode: FontRenderMode,
+ pub embolden_amount: TypedVector2D<f32, DevicePixel>,
+}
+
+#[cfg(not(feature = "pathfinder"))]
+#[derive(Debug)]
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub struct GlyphTask;
+
// Where the source data for a blit task can be found.
#[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum BlitSource {
Image {
key: ImageCacheKey,
},
@@ -227,16 +252,18 @@ pub struct RenderTaskData {
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum RenderTaskKind {
Picture(PictureTask),
CacheMask(CacheMaskTask),
ClipRegion(ClipRegionTask),
VerticalBlur(BlurTask),
HorizontalBlur(BlurTask),
+ #[allow(dead_code)]
+ Glyph(GlyphTask),
Readback(DeviceIntRect),
Scaling(RenderTargetKind),
Blit(BlitTask),
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
@@ -259,33 +286,28 @@ pub struct RenderTask {
pub clear_mode: ClearMode,
pub saved_index: Option<SavedTargetIndex>,
}
impl RenderTask {
pub fn new_picture(
location: RenderTaskLocation,
prim_index: PrimitiveIndex,
- target_kind: RenderTargetKind,
content_origin: DeviceIntPoint,
- color: PremultipliedColorF,
- clear_mode: ClearMode,
children: Vec<RenderTaskId>,
) -> Self {
RenderTask {
children,
location,
kind: RenderTaskKind::Picture(PictureTask {
prim_index,
- target_kind,
content_origin,
- color,
uv_rect_handle: GpuCacheHandle::new(),
}),
- clear_mode,
+ clear_mode: ClearMode::Transparent,
saved_index: None,
}
}
pub fn new_readback(screen_rect: DeviceIntRect) -> Self {
RenderTask {
children: Vec::new(),
location: RenderTaskLocation::Dynamic(None, screen_rect.size),
@@ -357,16 +379,17 @@ impl RenderTask {
// sized box-shadow rect.
info.cache_item = resource_cache.request_render_task(
RenderTaskCacheKey {
size: cache_size,
kind: RenderTaskCacheKeyKind::BoxShadow(cache_key),
},
gpu_cache,
render_tasks,
+ None,
|render_tasks| {
// Draw the rounded rect.
let mask_task = RenderTask::new_rounded_rect_mask(
cache_size,
clip_data_address,
);
let mask_task_id = render_tasks.add(mask_task);
@@ -512,16 +535,39 @@ impl RenderTask {
clear_mode: match target_kind {
RenderTargetKind::Color => ClearMode::Transparent,
RenderTargetKind::Alpha => ClearMode::One,
},
saved_index: None,
}
}
+ #[cfg(feature = "pathfinder")]
+ pub fn new_glyph(location: RenderTaskLocation,
+ mesh: Mesh,
+ origin: &DeviceIntPoint,
+ subpixel_offset: &TypedPoint2D<f32, DevicePixel>,
+ render_mode: FontRenderMode,
+ embolden_amount: &TypedVector2D<f32, DevicePixel>)
+ -> Self {
+ RenderTask {
+ children: vec![],
+ location: location,
+ kind: RenderTaskKind::Glyph(GlyphTask {
+ mesh: Some(mesh),
+ origin: *origin,
+ subpixel_offset: *subpixel_offset,
+ render_mode: render_mode,
+ embolden_amount: *embolden_amount,
+ }),
+ clear_mode: ClearMode::Transparent,
+ 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
// required to match both the GPU structures declared
// in prim_shared.glsl, and also the uses in submit_batch()
// in renderer.rs.
@@ -555,16 +601,19 @@ impl RenderTask {
RenderTaskKind::VerticalBlur(ref task) |
RenderTaskKind::HorizontalBlur(ref task) => {
[
task.blur_std_deviation,
0.0,
0.0,
]
}
+ RenderTaskKind::Glyph(_) => {
+ [1.0, 0.0, 0.0]
+ }
RenderTaskKind::Readback(..) |
RenderTaskKind::Scaling(..) |
RenderTaskKind::Blit(..) => {
[0.0; 3]
}
};
let (target_rect, target_index) = self.get_target_rect();
@@ -591,17 +640,18 @@ impl RenderTask {
RenderTaskKind::VerticalBlur(ref info) |
RenderTaskKind::HorizontalBlur(ref info) => {
&info.uv_rect_handle
}
RenderTaskKind::ClipRegion(..) |
RenderTaskKind::Readback(..) |
RenderTaskKind::Scaling(..) |
RenderTaskKind::Blit(..) |
- RenderTaskKind::CacheMask(..) => {
+ RenderTaskKind::CacheMask(..) |
+ RenderTaskKind::Glyph(..) => {
panic!("texture handle not supported for this task kind");
}
}
}
pub fn get_dynamic_size(&self) -> DeviceIntSize {
match self.location {
RenderTaskLocation::Fixed(..) => DeviceIntSize::zero(),
@@ -650,22 +700,26 @@ impl RenderTask {
RenderTargetKind::Alpha
}
RenderTaskKind::VerticalBlur(ref task_info) |
RenderTaskKind::HorizontalBlur(ref task_info) => {
task_info.target_kind
}
+ RenderTaskKind::Glyph(..) => {
+ RenderTargetKind::Color
+ }
+
RenderTaskKind::Scaling(target_kind) => {
target_kind
}
- RenderTaskKind::Picture(ref task_info) => {
- task_info.target_kind
+ RenderTaskKind::Picture(..) => {
+ RenderTargetKind::Color
}
RenderTaskKind::Blit(..) => {
RenderTargetKind::Color
}
}
}
@@ -678,17 +732,18 @@ impl RenderTask {
pub fn is_shared(&self) -> bool {
match self.kind {
RenderTaskKind::Picture(..) |
RenderTaskKind::VerticalBlur(..) |
RenderTaskKind::Readback(..) |
RenderTaskKind::HorizontalBlur(..) |
RenderTaskKind::Scaling(..) |
RenderTaskKind::ClipRegion(..) |
- RenderTaskKind::Blit(..) => false,
+ RenderTaskKind::Blit(..) |
+ RenderTaskKind::Glyph(..) => false,
// TODO(gw): For now, we've disabled the shared clip mask
// optimization. It's of dubious value in the
// future once we start to cache clip tasks anyway.
// I have left shared texture support here though,
// just in case we want it in the future.
RenderTaskKind::CacheMask(..) => false,
}
@@ -707,17 +762,18 @@ impl RenderTask {
}
RenderTaskKind::Picture(ref mut info) => {
&mut info.uv_rect_handle
}
RenderTaskKind::Readback(..) |
RenderTaskKind::Scaling(..) |
RenderTaskKind::Blit(..) |
RenderTaskKind::ClipRegion(..) |
- RenderTaskKind::CacheMask(..) => {
+ RenderTaskKind::CacheMask(..) |
+ RenderTaskKind::Glyph(..) => {
return;
}
};
if let Some(mut request) = gpu_cache.request(cache_handle) {
let image_source = ImageSource {
p0: target_rect.origin.to_f32(),
p1: target_rect.bottom_right().to_f32(),
@@ -728,17 +784,16 @@ impl RenderTask {
}
}
#[cfg(feature = "debugger")]
pub fn print_with<T: PrintTreePrinter>(&self, pt: &mut T, tree: &RenderTaskTree) -> bool {
match self.kind {
RenderTaskKind::Picture(ref task) => {
pt.new_level(format!("Picture of {:?}", task.prim_index));
- pt.add_item(format!("kind: {:?}", task.target_kind));
}
RenderTaskKind::CacheMask(ref task) => {
pt.new_level(format!("CacheMask with {} clips", task.clips.len()));
pt.add_item(format!("rect: {:?}", task.actual_rect));
}
RenderTaskKind::ClipRegion(..) => {
pt.new_level("ClipRegion".to_owned());
}
@@ -757,16 +812,19 @@ impl RenderTask {
RenderTaskKind::Scaling(ref kind) => {
pt.new_level("Scaling".to_owned());
pt.add_item(format!("kind: {:?}", kind));
}
RenderTaskKind::Blit(ref task) => {
pt.new_level("Blit".to_owned());
pt.add_item(format!("source: {:?}", task.source));
}
+ RenderTaskKind::Glyph(..) => {
+ pt.new_level("Glyph".to_owned());
+ }
}
pt.add_item(format!("clear to: {:?}", self.clear_mode));
for &child_id in &self.children {
if tree[child_id].print_with(pt, tree) {
pt.add_item(format!("self: {:?}", child_id))
}
@@ -785,25 +843,27 @@ impl RenderTask {
}
RenderTaskLocation::TextureCache(..) => {
panic!("Unable to mark a permanently cached task for saving!");
}
}
}
}
-#[derive(Debug, Hash, PartialEq, Eq)]
+#[derive(Clone, Debug, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum RenderTaskCacheKeyKind {
BoxShadow(BoxShadowCacheKey),
Image(ImageCacheKey),
+ #[allow(dead_code)]
+ Glyph(GpuGlyphCacheKey),
}
-#[derive(Debug, Hash, PartialEq, Eq)]
+#[derive(Clone, Debug, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct RenderTaskCacheKey {
pub size: DeviceIntSize,
pub kind: RenderTaskCacheKeyKind,
}
#[cfg_attr(feature = "capture", derive(Serialize))]
@@ -854,31 +914,33 @@ impl RenderTaskCache {
}
pub fn request_render_task<F>(
&mut self,
key: RenderTaskCacheKey,
texture_cache: &mut TextureCache,
gpu_cache: &mut GpuCache,
render_tasks: &mut RenderTaskTree,
+ user_data: Option<[f32; 3]>,
mut f: F,
- ) -> CacheItem where F: FnMut(&mut RenderTaskTree) -> (RenderTaskId, bool) {
+ ) -> Result<CacheItem, ()>
+ where F: FnMut(&mut RenderTaskTree) -> Result<(RenderTaskId, bool), ()> {
// Get the texture cache handle for this cache key,
// or create one.
let cache_entry = self.entries
.entry(key)
.or_insert(RenderTaskCacheEntry {
handle: TextureCacheHandle::new(),
});
- // Check if this texture cache handle is valie.
+ // Check if this texture cache handle is valid.
if texture_cache.request(&mut cache_entry.handle, gpu_cache) {
// Invoke user closure to get render task chain
// to draw this into the texture cache.
- let (render_task_id, is_opaque) = f(render_tasks);
+ let (render_task_id, is_opaque) = try!(f(render_tasks));
let render_task = &mut render_tasks[render_task_id];
// Select the right texture page to allocate from.
let image_format = match render_task.target_kind() {
RenderTargetKind::Color => ImageFormat::BGRA8,
RenderTargetKind::Alpha => ImageFormat::R8,
};
@@ -904,17 +966,17 @@ impl RenderTaskCache {
// Allocate space in the texture cache, but don't supply
// and CPU-side data to be uploaded.
texture_cache.update(
&mut cache_entry.handle,
descriptor,
TextureFilter::Linear,
None,
- [0.0; 3],
+ user_data.unwrap_or([0.0; 3]),
None,
gpu_cache,
None,
);
// Get the allocation details in the texture cache, and store
// this in the render task. The renderer will draw this
// task into the appropriate layer and rect of the texture
@@ -926,18 +988,37 @@ impl RenderTaskCache {
texture_id,
texture_layer,
uv_rect.to_i32()
);
}
// Finally, return the texture cache handle that we know
// is now up to date.
+ Ok(texture_cache.get(&cache_entry.handle))
+ }
+
+ #[allow(dead_code)]
+ pub fn get_cache_item_for_render_task(&self,
+ texture_cache: &TextureCache,
+ key: &RenderTaskCacheKey)
+ -> CacheItem {
+ // Get the texture cache handle for this cache key.
+ let cache_entry = self.entries.get(key).unwrap();
texture_cache.get(&cache_entry.handle)
}
+
+ #[allow(dead_code)]
+ pub fn cache_item_is_allocated_for_render_task(&self,
+ texture_cache: &TextureCache,
+ key: &RenderTaskCacheKey)
+ -> bool {
+ let cache_entry = self.entries.get(key).unwrap();
+ texture_cache.is_allocated(&cache_entry.handle)
+ }
}
// TODO(gw): Rounding the content rect here to device pixels is not
// technically correct. Ideally we should ceil() here, and ensure that
// the extra part pixel in the case of fractional sizes is correctly
// handled. For now, just use rounding which passes the existing
// Gecko tests.
// Note: zero-square tasks are prohibited in WR task tree, so
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -4,81 +4,94 @@
//! The webrender API.
//!
//! The `webrender::renderer` module provides the interface to webrender, which
//! is accessible through [`Renderer`][renderer]
//!
//! [renderer]: struct.Renderer.html
-use api::{BlobImageRenderer, ColorF, ColorU, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
+use api::{BlobImageRenderer, ColorF, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, DocumentId, Epoch, ExternalImageId};
use api::{ExternalImageType, FontRenderMode, ImageFormat, PipelineId};
use api::{RenderApiSender, RenderNotifier, TexelRect, TextureTarget};
use api::{channel};
-#[cfg(not(feature = "debugger"))]
-use api::ApiMsg;
use api::DebugCommand;
-#[cfg(not(feature = "debugger"))]
-use api::channel::MsgSender;
use api::channel::PayloadReceiverHelperMethods;
use batch::{BatchKey, BatchKind, BatchTextures, BrushBatchKind, TransformBatchKind};
#[cfg(any(feature = "capture", feature = "replay"))]
use capture::{CaptureConfig, ExternalCaptureImage, PlainExternalImage};
use debug_colors;
-use debug_render::DebugRenderer;
-#[cfg(feature = "debugger")]
-use debug_server::{self, DebugServer};
use device::{DepthFunction, Device, FrameId, Program, UploadMethod, Texture, PBO};
use device::{ExternalTexture, FBOId, TextureSlot};
use device::{FileWatcherHandler, ShaderError, TextureFilter,
VertexUsageHint, VAO, VBO, CustomVAO};
use device::{ProgramCache, ReadPixelsFormat};
use euclid::{rect, Transform3D};
use frame_builder::FrameBuilderConfig;
use gleam::gl;
use glyph_rasterizer::{GlyphFormat, GlyphRasterizer};
use gpu_cache::{GpuBlockData, GpuCacheUpdate, GpuCacheUpdateList};
+#[cfg(feature = "pathfinder")]
+use gpu_glyph_renderer::GpuGlyphRenderer;
use gpu_types::PrimitiveInstance;
use internal_types::{SourceTexture, ORTHO_FAR_PLANE, ORTHO_NEAR_PLANE, ResourceCacheError};
use internal_types::{CacheTextureId, DebugOutput, FastHashMap, RenderedDocument, ResultMsg};
use internal_types::{TextureUpdateList, TextureUpdateOp, TextureUpdateSource};
use internal_types::{RenderTargetInfo, SavedTargetIndex};
use prim_store::DeferredResolve;
-use profiler::{BackendProfileCounters, FrameProfileCounters, Profiler};
-use profiler::{GpuProfileTag, RendererProfileCounters, RendererProfileTimers};
-use query::{GpuProfiler, GpuTimer};
+use profiler::{BackendProfileCounters, FrameProfileCounters,
+ GpuProfileTag, RendererProfileCounters, RendererProfileTimers};
+use query::GpuProfiler;
use rayon::{ThreadPool, ThreadPoolBuilder};
use record::ApiRecordingReceiver;
use render_backend::RenderBackend;
use scene_builder::SceneBuilder;
use shade::Shaders;
use render_task::{RenderTask, RenderTaskKind, RenderTaskTree};
use resource_cache::ResourceCache;
-#[cfg(feature = "debugger")]
-use serde_json;
use std;
use std::cmp;
use std::collections::VecDeque;
use std::collections::hash_map::Entry;
use std::f32;
use std::mem;
use std::path::PathBuf;
use std::rc::Rc;
use std::sync::Arc;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::thread;
use texture_cache::TextureCache;
use thread_profiler::{register_thread_with_profiler, write_profile};
use tiling::{AlphaRenderTarget, ColorRenderTarget};
use tiling::{BlitJob, BlitJobSource, RenderPass, RenderPassKind, RenderTargetList};
use tiling::{Frame, RenderTarget, ScalingInfo, TextureCacheRenderTarget};
+#[cfg(not(feature = "pathfinder"))]
+use tiling::GlyphJob;
use time::precise_time_ns;
+cfg_if! {
+ if #[cfg(feature = "debugger")] {
+ use serde_json;
+ use debug_server::{self, DebugServer};
+ } else {
+ use api::ApiMsg;
+ use api::channel::MsgSender;
+ }
+}
+
+cfg_if! {
+ if #[cfg(feature = "debug_renderer")] {
+ use api::ColorU;
+ use debug_render::DebugRenderer;
+ use profiler::Profiler;
+ use query::GpuTimer;
+ }
+}
pub const MAX_VERTEX_TEXTURE_WIDTH: usize = 1024;
/// 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.
@@ -123,20 +136,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_HW_COMPOSITE: GpuProfileTag = GpuProfileTag {
- label: "HwComposite",
- color: debug_colors::DODGERBLUE,
-};
const GPU_TAG_PRIM_SPLIT_COMPOSITE: GpuProfileTag = GpuProfileTag {
label: "SplitComposite",
color: debug_colors::DARKBLUE,
};
const GPU_TAG_PRIM_TEXT_RUN: GpuProfileTag = GpuProfileTag {
label: "TextRun",
color: debug_colors::BLUE,
};
@@ -195,17 +204,16 @@ impl TransformBatchKind {
}
}
}
impl BatchKind {
#[cfg(feature = "debugger")]
fn debug_name(&self) -> &'static str {
match *self {
- BatchKind::HardwareComposite => "HardwareComposite",
BatchKind::SplitComposite => "SplitComposite",
BatchKind::Brush(kind) => {
match kind {
BrushBatchKind::Solid => "Brush (Solid)",
BrushBatchKind::Image(..) => "Brush (Image)",
BrushBatchKind::Blend => "Brush (Blend)",
BrushBatchKind::MixBlend { .. } => "Brush (Composite)",
BrushBatchKind::YuvImage(..) => "Brush (YuvImage)",
@@ -214,17 +222,16 @@ impl BatchKind {
}
}
BatchKind::Transformable(_, batch_kind) => batch_kind.debug_name(),
}
}
fn sampler_tag(&self) -> GpuProfileTag {
match *self {
- BatchKind::HardwareComposite => GPU_TAG_PRIM_HW_COMPOSITE,
BatchKind::SplitComposite => GPU_TAG_PRIM_SPLIT_COMPOSITE,
BatchKind::Brush(kind) => {
match kind {
BrushBatchKind::Solid => GPU_TAG_BRUSH_SOLID,
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,
@@ -300,17 +307,17 @@ pub(crate) enum TextureSampler {
// A special sampler that is bound to the A8 output of
// the *first* pass. Items rendered in this target are
// available as inputs to tasks in any subsequent pass.
SharedCacheA8,
LocalClipRects
}
impl TextureSampler {
- fn color(n: usize) -> TextureSampler {
+ pub(crate) fn color(n: usize) -> TextureSampler {
match n {
0 => TextureSampler::Color0,
1 => TextureSampler::Color1,
2 => TextureSampler::Color2,
_ => {
panic!("There are only 3 color samplers.");
}
}
@@ -435,23 +442,109 @@ pub(crate) mod desc {
VertexAttribute {
name: "aValue",
count: 4,
kind: VertexAttributeKind::F32,
},
],
instance_attributes: &[],
};
+
+ pub const VECTOR_STENCIL: VertexDescriptor = VertexDescriptor {
+ vertex_attributes: &[
+ VertexAttribute {
+ name: "aPosition",
+ count: 2,
+ kind: VertexAttributeKind::F32,
+ },
+ ],
+ instance_attributes: &[
+ VertexAttribute {
+ name: "aFromPosition",
+ count: 2,
+ kind: VertexAttributeKind::F32,
+ },
+ VertexAttribute {
+ name: "aCtrlPosition",
+ count: 2,
+ kind: VertexAttributeKind::F32,
+ },
+ VertexAttribute {
+ name: "aToPosition",
+ count: 2,
+ kind: VertexAttributeKind::F32,
+ },
+ VertexAttribute {
+ name: "aFromNormal",
+ count: 2,
+ kind: VertexAttributeKind::F32,
+ },
+ VertexAttribute {
+ name: "aCtrlNormal",
+ count: 2,
+ kind: VertexAttributeKind::F32,
+ },
+ VertexAttribute {
+ name: "aToNormal",
+ count: 2,
+ kind: VertexAttributeKind::F32,
+ },
+ VertexAttribute {
+ name: "aPathID",
+ count: 1,
+ kind: VertexAttributeKind::U16,
+ },
+ VertexAttribute {
+ name: "aPad",
+ count: 1,
+ kind: VertexAttributeKind::U16,
+ },
+ ],
+ };
+
+ pub const VECTOR_COVER: VertexDescriptor = VertexDescriptor {
+ vertex_attributes: &[
+ VertexAttribute {
+ name: "aPosition",
+ count: 2,
+ kind: VertexAttributeKind::F32,
+ },
+ ],
+ instance_attributes: &[
+ VertexAttribute {
+ name: "aTargetRect",
+ count: 4,
+ kind: VertexAttributeKind::I32,
+ },
+ VertexAttribute {
+ name: "aStencilOrigin",
+ count: 2,
+ kind: VertexAttributeKind::I32,
+ },
+ VertexAttribute {
+ name: "aSubpixel",
+ count: 1,
+ kind: VertexAttributeKind::U16,
+ },
+ VertexAttribute {
+ name: "aPad",
+ count: 1,
+ kind: VertexAttributeKind::U16,
+ },
+ ],
+ };
}
#[derive(Debug, Copy, Clone)]
pub(crate) enum VertexArrayKind {
Primitive,
Blur,
Clip,
+ VectorStencil,
+ VectorCover,
}
#[derive(Clone, Debug, PartialEq)]
pub enum GraphicsApi {
OpenGL,
}
#[derive(Clone, Debug)]
@@ -491,16 +584,17 @@ pub enum RendererKind {
#[derive(Debug)]
pub struct GpuProfile {
pub frame_id: FrameId,
pub paint_time_ns: u64,
}
impl GpuProfile {
+ #[cfg(feature = "debug_renderer")]
fn new<T>(frame_id: FrameId, timers: &[GpuTimer<T>]) -> GpuProfile {
let mut paint_time_ns = 0;
for timer in timers {
paint_time_ns += timer.time_ns;
}
GpuProfile {
frame_id,
paint_time_ns,
@@ -527,16 +621,29 @@ impl CpuProfile {
frame_id,
backend_time_ns,
composite_time_ns,
draw_calls,
}
}
}
+#[cfg(not(feature = "pathfinder"))]
+pub struct GpuGlyphRenderer;
+
+#[cfg(not(feature = "pathfinder"))]
+impl GpuGlyphRenderer {
+ fn new(_: &mut Device, _: &VAO, _: bool) -> Result<GpuGlyphRenderer, RendererError> {
+ Ok(GpuGlyphRenderer)
+ }
+}
+
+#[cfg(not(feature = "pathfinder"))]
+struct StenciledGlyphPage;
+
struct ActiveTexture {
texture: Texture,
saved_index: Option<SavedTargetIndex>,
is_shared: bool,
}
struct SourceTextureResolver {
/// A vector for fast resolves of texture cache IDs to
@@ -571,17 +678,17 @@ struct SourceTextureResolver {
/// 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(
+ device.init_texture::<u8>(
&mut dummy_cache_texture,
1,
1,
TextureFilter::Linear,
None,
1,
None,
);
@@ -855,17 +962,17 @@ impl CacheTexture {
let old_size = self.texture.get_dimensions();
let new_size = DeviceUintSize::new(MAX_VERTEX_TEXTURE_WIDTH as _, max_height);
match self.bus {
CacheBus::PixelBuffer { ref mut rows, .. } => {
if max_height > old_size.height {
// Create a f32 texture that can be used for the vertex shader
// to fetch data from.
- device.init_texture(
+ device.init_texture::<u8>(
&mut self.texture,
new_size.width,
new_size.height,
TextureFilter::Nearest,
None,
1,
None,
);
@@ -889,17 +996,17 @@ impl CacheTexture {
device.allocate_vbo(buf_position, total_block_count, VertexUsageHint::Stream);
device.allocate_vbo(buf_value, total_block_count, VertexUsageHint::Stream);
}
if new_size.height > old_size.height || GPU_CACHE_RESIZE_TEST {
if old_size.height > 0 {
device.resize_renderable_texture(&mut self.texture, new_size);
} else {
- device.init_texture(
+ device.init_texture::<u8>(
&mut self.texture,
new_size.width,
new_size.height,
TextureFilter::Nearest,
Some(RenderTargetInfo {
has_depth: false,
}),
1,
@@ -1071,17 +1178,17 @@ impl VertexDataTexture {
let needed_height = (data.len() / items_per_row) as u32;
// Determine if the texture needs to be resized.
let texture_size = self.texture.get_dimensions();
if needed_height > texture_size.height {
let new_height = (needed_height + 127) & !127;
- device.init_texture(
+ device.init_texture::<u8>(
&mut self.texture,
width,
new_height,
TextureFilter::Nearest,
None,
1,
None,
);
@@ -1121,43 +1228,70 @@ struct FrameOutput {
#[derive(PartialEq)]
struct TargetSelector {
size: DeviceUintSize,
num_layers: usize,
format: ImageFormat,
}
+#[cfg(feature = "debug_renderer")]
+struct LazyInitializedDebugRenderer {
+ debug_renderer: Option<DebugRenderer>,
+}
+
+#[cfg(feature = "debug_renderer")]
+impl LazyInitializedDebugRenderer {
+ pub fn new() -> Self {
+ Self {
+ debug_renderer: None,
+ }
+ }
+
+ pub fn get_mut<'a>(&'a mut self, device: &mut Device) -> &'a mut DebugRenderer {
+ self.debug_renderer.get_or_insert_with(|| DebugRenderer::new(device))
+ }
+
+ pub fn deinit(self, device: &mut Device) {
+ if let Some(debug_renderer) = self.debug_renderer {
+ debug_renderer.deinit(device);
+ }
+ }
+}
/// The renderer is responsible for submitting to the GPU the work prepared by the
/// RenderBackend.
pub struct Renderer {
result_rx: Receiver<ResultMsg>,
debug_server: DebugServer,
- device: Device,
+ pub device: Device,
pending_texture_updates: Vec<TextureUpdateList>,
pending_gpu_cache_updates: Vec<GpuCacheUpdateList>,
pending_shader_updates: Vec<PathBuf>,
active_documents: Vec<(DocumentId, RenderedDocument)>,
shaders: Shaders,
+ pub gpu_glyph_renderer: GpuGlyphRenderer,
+
max_texture_size: u32,
max_recorded_profiles: usize,
clear_color: Option<ColorF>,
enable_clear_scissor: bool,
- debug: DebugRenderer,
+ #[cfg(feature = "debug_renderer")]
+ debug: LazyInitializedDebugRenderer,
debug_flags: DebugFlags,
backend_profile_counters: BackendProfileCounters,
profile_counters: RendererProfileCounters,
+ #[cfg(feature = "debug_renderer")]
profiler: Profiler,
last_time: u64,
- gpu_profile: GpuProfiler<GpuProfileTag>,
+ pub gpu_profile: GpuProfiler<GpuProfileTag>,
prim_vao: VAO,
blur_vao: VAO,
clip_vao: VAO,
node_data_texture: VertexDataTexture,
local_clip_rects_texture: VertexDataTexture,
render_task_texture: VertexDataTexture,
gpu_cache_texture: CacheTexture,
@@ -1182,17 +1316,17 @@ pub struct Renderer {
/// Optional trait object that allows the client
/// application to provide a texture handle to
/// copy the WR output to.
output_image_handler: Option<Box<OutputImageHandler>>,
// Currently allocated FBOs for output frames.
output_targets: FastHashMap<u32, FrameOutput>,
- renderer_errors: Vec<RendererError>,
+ pub renderer_errors: Vec<RendererError>,
/// List of profile results from previous frames. Can be retrieved
/// via get_frame_profiles().
cpu_profiles: VecDeque<CpuProfile>,
gpu_profiles: VecDeque<GpuProfile>,
#[cfg(feature = "capture")]
read_fbo: FBOId,
@@ -1380,18 +1514,16 @@ impl Renderer {
Some(&dither_matrix),
);
Some(texture)
} else {
None
};
- let debug_renderer = DebugRenderer::new(&mut device);
-
let x0 = 0.0;
let y0 = 0.0;
let x1 = 1.0;
let y1 = 1.0;
let quad_indices: [u16; 6] = [0, 1, 2, 2, 1, 3];
let quad_vertices = [
PackedVertex { pos: [x0, y0] },
@@ -1400,19 +1532,22 @@ impl Renderer {
PackedVertex { pos: [x1, y1] },
];
let prim_vao = device.create_vao(&desc::PRIM_INSTANCES);
device.bind_vao(&prim_vao);
device.update_vao_indices(&prim_vao, &quad_indices, VertexUsageHint::Static);
device.update_vao_main_vertices(&prim_vao, &quad_vertices, VertexUsageHint::Static);
+ let gpu_glyph_renderer = try!(GpuGlyphRenderer::new(&mut device,
+ &prim_vao,
+ options.precache_shaders));
+
let blur_vao = device.create_vao_with_new_instances(&desc::BLUR, &prim_vao);
let clip_vao = device.create_vao_with_new_instances(&desc::CLIP, &prim_vao);
-
let texture_cache_upload_pbo = device.create_pbo();
let texture_resolver = SourceTextureResolver::new(&mut device);
let node_data_texture = VertexDataTexture::new(&mut device);
let local_clip_rects_texture = VertexDataTexture::new(&mut device);
let render_task_texture = VertexDataTexture::new(&mut device);
@@ -1533,27 +1668,30 @@ impl Renderer {
result_rx,
debug_server,
device,
active_documents: Vec::new(),
pending_texture_updates: Vec::new(),
pending_gpu_cache_updates: Vec::new(),
pending_shader_updates: Vec::new(),
shaders,
- debug: debug_renderer,
+ #[cfg(feature = "debug_renderer")]
+ debug: LazyInitializedDebugRenderer::new(),
debug_flags,
backend_profile_counters: BackendProfileCounters::new(),
profile_counters: RendererProfileCounters::new(),
+ #[cfg(feature = "debug_renderer")]
profiler: Profiler::new(),
max_texture_size: max_device_size,
max_recorded_profiles: options.max_recorded_profiles,
clear_color: options.clear_color,
enable_clear_scissor: options.enable_clear_scissor,
last_time: 0,
gpu_profile,
+ gpu_glyph_renderer,
prim_vao,
blur_vao,
clip_vao,
node_data_texture,
local_clip_rects_texture,
render_task_texture,
pipeline_info: PipelineInfo::default(),
dither_matrix_texture,
@@ -2018,16 +2156,17 @@ impl Renderer {
self.last_time = precise_time_ns();
return Ok(RendererStats::empty());
}
let mut stats = RendererStats::empty();
let mut frame_profiles = Vec::new();
let mut profile_timers = RendererProfileTimers::new();
+ #[cfg(feature = "debug_renderer")]
let profile_samplers = {
let _gm = self.gpu_profile.start_marker("build samples");
// Block CPU waiting for last frame's GPU profiles to arrive.
// In general this shouldn't block unless heavily GPU limited.
let (gpu_frame_id, timers, samplers) = self.gpu_profile.build_samples();
if self.max_recorded_profiles > 0 {
while self.gpu_profiles.len() >= self.max_recorded_profiles {
@@ -2132,41 +2271,48 @@ impl Renderer {
cpu_frame_id,
self.backend_profile_counters.total_time.get(),
profile_timers.cpu_time.get(),
self.profile_counters.draw_calls.get(),
);
self.cpu_profiles.push_back(cpu_profile);
}
- if self.debug_flags.contains(DebugFlags::PROFILER_DBG) {
- if let Some(framebuffer_size) = framebuffer_size {
- //TODO: take device/pixel ratio into equation?
- let screen_fraction = 1.0 / framebuffer_size.to_f32().area();
- self.profiler.draw_profile(
- &frame_profiles,
- &self.backend_profile_counters,
- &self.profile_counters,
- &mut profile_timers,
- &profile_samplers,
- screen_fraction,
- &mut self.debug,
- self.debug_flags.contains(DebugFlags::COMPACT_PROFILER),
- );
+ #[cfg(feature = "debug_renderer")]
+ {
+ if self.debug_flags.contains(DebugFlags::PROFILER_DBG) {
+ if let Some(framebuffer_size) = framebuffer_size {
+ //TODO: take device/pixel ratio into equation?
+ let screen_fraction = 1.0 / framebuffer_size.to_f32().area();
+ self.profiler.draw_profile(
+ &frame_profiles,
+ &self.backend_profile_counters,
+ &self.profile_counters,
+ &mut profile_timers,
+ &profile_samplers,
+ screen_fraction,
+ self.debug.get_mut(&mut self.device),
+ self.debug_flags.contains(DebugFlags::COMPACT_PROFILER),
+ );
+ }
}
}
self.backend_profile_counters.reset();
self.profile_counters.reset();
self.profile_counters.frame_counter.inc();
profile_timers.cpu_time.profile(|| {
let _gm = self.gpu_profile.start_marker("end frame");
self.gpu_profile.end_frame();
- self.debug.render(&mut self.device, framebuffer_size);
+ #[cfg(feature = "debug_renderer")]
+ {
+ self.debug.get_mut(&mut self.device)
+ .render(&mut self.device, framebuffer_size);
+ }
self.device.end_frame();
});
self.last_time = current_time;
if self.renderer_errors.is_empty() {
Ok(stats)
} else {
Err(mem::replace(&mut self.renderer_errors, Vec::new()))
@@ -2180,17 +2326,17 @@ impl Renderer {
}
fn update_gpu_cache(&mut self) {
let _gm = self.gpu_profile.start_marker("gpu cache update");
// For an artificial stress test of GPU cache resizing,
// always pass an extra update list with at least one block in it.
let gpu_cache_height = self.gpu_cache_texture.get_height();
- if gpu_cache_height != 0 && GPU_CACHE_RESIZE_TEST {
+ if gpu_cache_height != 0 && GPU_CACHE_RESIZE_TEST {
self.pending_gpu_cache_updates.push(GpuCacheUpdateList {
frame_id: FrameId::new(0),
height: gpu_cache_height,
blocks: vec![[1f32; 4].into()],
updates: Vec::new(),
});
}
@@ -2201,17 +2347,17 @@ impl Renderer {
(count + list.blocks.len(), cmp::max(height, list.height))
});
if max_requested_height > self.max_texture_size && !self.gpu_cache_overflow {
self.gpu_cache_overflow = true;
self.renderer_errors.push(RendererError::MaxTextureSize);
}
- //Note: if we decide to switch to scatter-style GPU cache update
+ // Note: if we decide to switch to scatter-style GPU cache update
// permanently, we can have this code nicer with `BufferUploader` kind
// of helper, similarly to how `TextureUploader` API is used.
self.gpu_cache_texture.prepare_for_updates(
&mut self.device,
updated_blocks,
max_requested_height,
);
@@ -2267,17 +2413,17 @@ impl Renderer {
self.texture_resolver.cache_texture_map.push(texture);
}
let texture =
&mut self.texture_resolver.cache_texture_map[cache_texture_index];
assert_eq!(texture.get_format(), format);
// Ensure no PBO is bound when creating the texture storage,
// or GL will attempt to read data from there.
- self.device.init_texture(
+ self.device.init_texture::<u8>(
texture,
width,
height,
filter,
render_target,
layer_count,
None,
);
@@ -2319,32 +2465,34 @@ impl Renderer {
let bpp = texture.get_format().bytes_per_pixel();
let width = stride.unwrap_or(rect.size.width * bpp);
let total_size = width * rect.size.height;
// WR haven't support RGBAF32 format in texture_cache, so
// we use u8 type here.
let dummy_data: Vec<u8> = vec![255; total_size as usize];
uploader.upload(rect, layer_index, stride, &dummy_data);
}
- _ => panic!("No external buffer found"),
+ ExternalImageSource::NativeTexture(eid) => {
+ panic!("Unexpected external texture {:?} for the texture cache update of {:?}", eid, id);
+ }
};
handler.unlock(id, channel_index);
}
}
}
TextureUpdateOp::Free => {
let texture = &mut self.texture_resolver.cache_texture_map[update.id.0];
self.device.free_texture_storage(texture);
}
}
}
}
}
- fn draw_instanced_batch<T>(
+ pub(crate) fn draw_instanced_batch<T>(
&mut self,
data: &[T],
vertex_array_kind: VertexArrayKind,
textures: &BatchTextures,
stats: &mut RendererStats,
) {
for i in 0 .. textures.colors.len() {
self.texture_resolver.bind(
@@ -2354,21 +2502,30 @@ impl Renderer {
);
}
// TODO: this probably isn't the best place for this.
if let Some(ref texture) = self.dither_matrix_texture {
self.device.bind_texture(TextureSampler::Dither, texture);
}
- let vao = match vertex_array_kind {
- VertexArrayKind::Primitive => &self.prim_vao,
- VertexArrayKind::Clip => &self.clip_vao,
- VertexArrayKind::Blur => &self.blur_vao,
- };
+ self.draw_instanced_batch_with_previously_bound_textures(data, vertex_array_kind, stats)
+ }
+
+ pub(crate) fn draw_instanced_batch_with_previously_bound_textures<T>(
+ &mut self,
+ data: &[T],
+ vertex_array_kind: VertexArrayKind,
+ stats: &mut RendererStats,
+ ) {
+ let vao = get_vao(vertex_array_kind,
+ &self.prim_vao,
+ &self.clip_vao,
+ &self.blur_vao,
+ &self.gpu_glyph_renderer);
self.device.bind_vao(vao);
let batched = !self.debug_flags.contains(DebugFlags::DISABLE_BATCHING);
if batched {
self.device
.update_vao_instances(vao, data, VertexUsageHint::Stream);
@@ -3127,38 +3284,52 @@ impl Renderer {
fn draw_texture_cache_target(
&mut self,
texture: &SourceTexture,
layer: i32,
target: &TextureCacheRenderTarget,
render_tasks: &RenderTaskTree,
stats: &mut RendererStats,
) {
- let projection = {
+ let (target_size, projection) = {
let texture = self.texture_resolver
.resolve(texture)
.expect("BUG: invalid target texture");
let target_size = texture.get_dimensions();
-
- self.device
- .bind_draw_target(Some((texture, layer)), Some(target_size));
- self.device.disable_depth();
- self.device.disable_depth_write();
- self.device.set_blend(false);
-
- Transform3D::ortho(
+ let projection = Transform3D::ortho(
0.0,
target_size.width as f32,
0.0,
target_size.height as f32,
ORTHO_NEAR_PLANE,
ORTHO_FAR_PLANE,
- )
+ );
+ (target_size, projection)
};
+ self.device.disable_depth();
+ self.device.disable_depth_write();
+
+ self.device.set_blend(false);
+
+ // Handle any Pathfinder glyphs.
+ let stencil_page = self.stencil_glyphs(&target.glyphs, &projection, &target_size, stats);
+
+ {
+ let texture = self.texture_resolver
+ .resolve(texture)
+ .expect("BUG: invalid target texture");
+ self.device
+ .bind_draw_target(Some((texture, layer)), Some(target_size));
+ }
+
+ self.device.disable_depth();
+ self.device.disable_depth_write();
+ self.device.set_blend(false);
+
// Handle any blits to this texture from child tasks.
self.handle_blits(&target.blits, render_tasks);
// Draw any blurs for this target.
if !target.horizontal_blurs.is_empty() {
let _timer = self.gpu_profile.start_timer(GPU_TAG_BLUR);
self.shaders.cs_blur_a8
@@ -3166,18 +3337,39 @@ impl Renderer {
self.draw_instanced_batch(
&target.horizontal_blurs,
VertexArrayKind::Blur,
&BatchTextures::no_texture(),
stats,
);
}
+
+ // Blit any Pathfinder glyphs to the cache texture.
+ if let Some(stencil_page) = stencil_page {
+ self.cover_glyphs(stencil_page, &projection, stats);
+ }
}
+ #[cfg(not(feature = "pathfinder"))]
+ fn stencil_glyphs(&mut self,
+ _: &[GlyphJob],
+ _: &Transform3D<f32>,
+ _: &DeviceUintSize,
+ _: &mut RendererStats)
+ -> Option<StenciledGlyphPage> {
+ None
+ }
+
+ #[cfg(not(feature = "pathfinder"))]
+ fn cover_glyphs(&mut self,
+ _: StenciledGlyphPage,
+ _: &Transform3D<f32>,
+ _: &mut RendererStats) {}
+
fn update_deferred_resolves(&mut self, deferred_resolves: &[DeferredResolve]) -> Option<GpuCacheUpdateList> {
// The first thing we do is run through any pending deferred
// resolves, and use a callback to get the UV rect for this
// custom item. Then we patch the resource_rects structure
// here before it's uploaded to the GPU.
if deferred_resolves.is_empty() {
return None;
}
@@ -3302,17 +3494,17 @@ impl Renderer {
}
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(
+ self.device.init_texture::<u8>(
&mut texture,
list.max_size.width,
list.max_size.height,
TextureFilter::Linear,
Some(RenderTargetInfo {
has_depth: list.needs_depth(),
}),
list.targets.len() as _,
@@ -3504,33 +3696,36 @@ impl Renderer {
);
}
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);
}
+
+ #[cfg(feature = "debug_renderer")]
self.draw_epoch_debug();
// Garbage collect any frame outputs that weren't used this frame.
let device = &mut self.device;
self.output_targets
.retain(|_, target| if target.last_access != frame_id {
device.delete_fbo(target.fbo_id);
false
} else {
true
});
frame.has_been_rendered = true;
}
+ #[cfg(feature = "debug_renderer")]
pub fn debug_renderer<'b>(&'b mut self) -> &'b mut DebugRenderer {
- &mut self.debug
+ self.debug.get_mut(&mut self.device)
}
pub fn get_debug_flags(&self) -> DebugFlags {
self.debug_flags
}
pub fn set_debug_flags(&mut self, flags: DebugFlags) {
if let Some(enabled) = flag_changed(self.debug_flags, flags, DebugFlags::GPU_TIME_QUERIES) {
@@ -3651,38 +3846,41 @@ impl Renderer {
let dest_rect = rect(x, y, size, size);
self.device.blit_render_target(src_rect, dest_rect);
i += 1;
}
}
}
+ #[cfg(feature = "debug_renderer")]
fn draw_epoch_debug(&mut self) {
if !self.debug_flags.contains(DebugFlags::EPOCHS) {
return;
}
- let dy = self.debug.line_height();
+ let debug_renderer = self.debug.get_mut(&mut self.device);
+
+ let dy = debug_renderer.line_height();
let x0: f32 = 30.0;
let y0: f32 = 30.0;
let mut y = y0;
let mut text_width = 0.0;
for (pipeline, epoch) in &self.pipeline_info.epochs {
y += dy;
- let w = self.debug.add_text(
+ let w = debug_renderer.add_text(
x0, y,
&format!("{:?}: {:?}", pipeline, epoch),
ColorU::new(255, 255, 0, 255),
).size.width;
text_width = f32::max(text_width, w);
}
let margin = 10.0;
- self.debug.add_quad(
+ debug_renderer.add_quad(
&x0 - margin,
y0 - margin,
x0 + text_width + margin,
y + margin,
ColorU::new(25, 25, 25, 200),
ColorU::new(51, 51, 51, 200),
);
}
@@ -3724,17 +3922,22 @@ impl Renderer {
self.node_data_texture.deinit(&mut self.device);
self.local_clip_rects_texture.deinit(&mut self.device);
self.render_task_texture.deinit(&mut self.device);
self.device.delete_pbo(self.texture_cache_upload_pbo);
self.texture_resolver.deinit(&mut self.device);
self.device.delete_vao(self.prim_vao);
self.device.delete_vao(self.clip_vao);
self.device.delete_vao(self.blur_vao);
- self.debug.deinit(&mut self.device);
+
+ #[cfg(feature = "debug_renderer")]
+ {
+ self.debug.deinit(&mut self.device);
+ }
+
for (_, target) in self.output_targets {
self.device.delete_fbo(target.fbo_id);
}
self.shaders.deinit(&mut self.device);
#[cfg(feature = "capture")]
self.device.delete_fbo(self.read_fbo);
#[cfg(feature = "replay")]
for (_, ext) in self.owned_external_images {
@@ -4098,18 +4301,17 @@ impl Renderer {
if let Some(bytes) = data {
fs::File::create(config.root.join(&short_path))
.expect(&format!("Unable to create {}", short_path))
.write_all(&bytes)
.unwrap();
}
let plain = PlainExternalImage {
data: short_path,
- id: def.external.id,
- channel_index: def.external.channel_index,
+ external: def.external,
uv: ext_image.uv,
};
config.serialize(&plain, &def.short_path);
}
for def in &deferred_images {
handler.unlock(def.external.id, def.external.channel_index);
}
}
@@ -4173,19 +4375,19 @@ impl Renderer {
let mut buffer = Vec::new();
File::open(root.join(e.key()))
.expect(&format!("Unable to open {}", e.key()))
.read_to_end(&mut buffer)
.unwrap();
e.insert(Arc::new(buffer)).clone()
}
};
- let key = (plain_ext.id, plain_ext.channel_index);
+ let ext = plain_ext.external;
let value = (CapturedExternalImageData::Buffer(data), plain_ext.uv);
- image_handler.data.insert(key, value);
+ image_handler.data.insert((ext.id, ext.channel_index), value);
}
if let Some(renderer) = CaptureConfig::deserialize::<PlainRenderer, _>(&root, "renderer") {
info!("loading cached textures");
self.device.begin_frame();
for texture in self.texture_resolver.cache_texture_map.drain(..) {
self.device.delete_texture(texture);
@@ -4261,8 +4463,41 @@ impl Renderer {
self.device.end_frame();
}
self.output_image_handler = Some(Box::new(()) as Box<_>);
self.external_image_handler = Some(Box::new(image_handler) as Box<_>);
info!("done.");
}
}
+
+// FIXME(pcwalton): We should really gather up all the VAOs into a separate structure so that they
+// don't have to be passed in as parameters here.
+#[cfg(feature = "pathfinder")]
+fn get_vao<'a>(vertex_array_kind: VertexArrayKind,
+ prim_vao: &'a VAO,
+ clip_vao: &'a VAO,
+ blur_vao: &'a VAO,
+ gpu_glyph_renderer: &'a GpuGlyphRenderer)
+ -> &'a VAO {
+ match vertex_array_kind {
+ VertexArrayKind::Primitive => prim_vao,
+ VertexArrayKind::Clip => clip_vao,
+ VertexArrayKind::Blur => blur_vao,
+ VertexArrayKind::VectorStencil => &gpu_glyph_renderer.vector_stencil_vao,
+ VertexArrayKind::VectorCover => &gpu_glyph_renderer.vector_cover_vao,
+ }
+}
+
+#[cfg(not(feature = "pathfinder"))]
+fn get_vao<'a>(vertex_array_kind: VertexArrayKind,
+ prim_vao: &'a VAO,
+ clip_vao: &'a VAO,
+ blur_vao: &'a VAO,
+ _: &'a GpuGlyphRenderer)
+ -> &'a VAO {
+ match vertex_array_kind {
+ VertexArrayKind::Primitive => prim_vao,
+ VertexArrayKind::Clip => clip_vao,
+ VertexArrayKind::Blur => blur_vao,
+ VertexArrayKind::VectorStencil | VertexArrayKind::VectorCover => unreachable!(),
+ }
+}
--- a/gfx/webrender/src/resource_cache.rs
+++ b/gfx/webrender/src/resource_cache.rs
@@ -14,33 +14,35 @@ use api::{TileOffset, TileSize};
use app_units::Au;
#[cfg(feature = "capture")]
use capture::ExternalCaptureImage;
#[cfg(feature = "replay")]
use capture::PlainExternalImage;
#[cfg(any(feature = "replay", feature = "png"))]
use capture::CaptureConfig;
use device::TextureFilter;
-use glyph_cache::{GlyphCache, GlyphCacheEntry};
+use glyph_cache::GlyphCache;
+#[cfg(not(feature = "pathfinder"))]
+use glyph_cache::GlyphCacheEntry;
use glyph_rasterizer::{FontInstance, GlyphFormat, GlyphRasterizer, GlyphRequest};
use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
use internal_types::{FastHashMap, FastHashSet, SourceTexture, TextureUpdateList};
use profiler::{ResourceProfileCounters, TextureCacheProfileCounters};
use render_backend::FrameId;
use render_task::{RenderTaskCache, RenderTaskCacheKey, RenderTaskId, RenderTaskTree};
use std::collections::hash_map::Entry::{self, Occupied, Vacant};
use std::cmp;
use std::fmt::Debug;
use std::hash::Hash;
use std::mem;
#[cfg(any(feature = "capture", feature = "replay"))]
use std::path::PathBuf;
use std::sync::{Arc, RwLock};
use texture_cache::{TextureCache, TextureCacheHandle};
-
+use tiling::SpecialRenderPasses;
const DEFAULT_TILE_SIZE: TileSize = 512;
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct GlyphFetchResult {
pub index_in_text_run: i32,
pub uv_rect_address: GpuCacheAddress,
@@ -315,25 +317,27 @@ impl ResourceCache {
// handle will be returned. Otherwise, the user supplied
// closure will be invoked to generate the render task
// chain that is required to draw this task.
pub fn request_render_task<F>(
&mut self,
key: RenderTaskCacheKey,
gpu_cache: &mut GpuCache,
render_tasks: &mut RenderTaskTree,
- f: F,
+ user_data: Option<[f32; 3]>,
+ mut f: F,
) -> CacheItem where F: FnMut(&mut RenderTaskTree) -> (RenderTaskId, bool) {
self.cached_render_tasks.request_render_task(
key,
&mut self.texture_cache,
gpu_cache,
render_tasks,
- f
- )
+ user_data,
+ |render_task_tree| Ok(f(render_task_tree))
+ ).expect("Failed to request a render task from the resource cache!")
}
pub fn update_resources(
&mut self,
updates: ResourceUpdates,
profile_counters: &mut ResourceProfileCounters,
) {
// TODO, there is potential for optimization here, by processing updates in
@@ -641,69 +645,128 @@ impl ResourceCache {
}
}
pub fn request_glyphs(
&mut self,
mut font: FontInstance,
glyph_keys: &[GlyphKey],
gpu_cache: &mut GpuCache,
+ render_task_tree: &mut RenderTaskTree,
+ render_passes: &mut SpecialRenderPasses,
) {
debug_assert_eq!(self.state, State::AddResources);
self.glyph_rasterizer.prepare_font(&mut font);
self.glyph_rasterizer.request_glyphs(
&mut self.cached_glyphs,
font,
glyph_keys,
&mut self.texture_cache,
gpu_cache,
+ &mut self.cached_render_tasks,
+ render_task_tree,
+ render_passes,
);
}
pub fn pending_updates(&mut self) -> TextureUpdateList {
self.texture_cache.pending_updates()
}
+ #[cfg(feature = "pathfinder")]
pub fn fetch_glyphs<F>(
&self,
mut font: FontInstance,
glyph_keys: &[GlyphKey],
fetch_buffer: &mut Vec<GlyphFetchResult>,
- gpu_cache: &GpuCache,
+ gpu_cache: &mut GpuCache,
+ mut f: F,
+ ) where
+ F: FnMut(SourceTexture, GlyphFormat, &[GlyphFetchResult]),
+ {
+ debug_assert_eq!(self.state, State::QueryResources);
+
+ self.glyph_rasterizer.prepare_font(&mut font);
+
+ let mut current_texture_id = SourceTexture::Invalid;
+ let mut current_glyph_format = GlyphFormat::Subpixel;
+ debug_assert!(fetch_buffer.is_empty());
+
+ for (loop_index, key) in glyph_keys.iter().enumerate() {
+ let (cache_item, glyph_format) =
+ match self.glyph_rasterizer.get_cache_item_for_glyph(key,
+ &font,
+ &self.cached_glyphs,
+ &self.texture_cache,
+ &self.cached_render_tasks) {
+ None => continue,
+ Some(result) => result,
+ };
+ if current_texture_id != cache_item.texture_id ||
+ current_glyph_format != glyph_format {
+ if !fetch_buffer.is_empty() {
+ f(current_texture_id, current_glyph_format, fetch_buffer);
+ fetch_buffer.clear();
+ }
+ current_texture_id = cache_item.texture_id;
+ current_glyph_format = glyph_format;
+ }
+ fetch_buffer.push(GlyphFetchResult {
+ index_in_text_run: loop_index as i32,
+ uv_rect_address: gpu_cache.get_address(&cache_item.uv_rect_handle),
+ });
+ }
+
+ if !fetch_buffer.is_empty() {
+ f(current_texture_id, current_glyph_format, fetch_buffer);
+ fetch_buffer.clear();
+ }
+ }
+
+ #[cfg(not(feature = "pathfinder"))]
+ pub fn fetch_glyphs<F>(
+ &self,
+ mut font: FontInstance,
+ glyph_keys: &[GlyphKey],
+ fetch_buffer: &mut Vec<GlyphFetchResult>,
+ gpu_cache: &mut GpuCache,
mut f: F,
) where
F: FnMut(SourceTexture, GlyphFormat, &[GlyphFetchResult]),
{
debug_assert_eq!(self.state, State::QueryResources);
self.glyph_rasterizer.prepare_font(&mut font);
let glyph_key_cache = self.cached_glyphs.get_glyph_key_cache_for_font(&font);
let mut current_texture_id = SourceTexture::Invalid;
let mut current_glyph_format = GlyphFormat::Subpixel;
debug_assert!(fetch_buffer.is_empty());
for (loop_index, key) in glyph_keys.iter().enumerate() {
- if let GlyphCacheEntry::Cached(ref glyph) = *glyph_key_cache.get(key) {
- let cache_item = self.texture_cache.get(&glyph.texture_cache_handle);
- if current_texture_id != cache_item.texture_id ||
- current_glyph_format != glyph.format {
- if !fetch_buffer.is_empty() {
- f(current_texture_id, current_glyph_format, fetch_buffer);
- fetch_buffer.clear();
- }
- current_texture_id = cache_item.texture_id;
- current_glyph_format = glyph.format;
+ let (cache_item, glyph_format) = match *glyph_key_cache.get(key) {
+ GlyphCacheEntry::Cached(ref glyph) => {
+ (self.texture_cache.get(&glyph.texture_cache_handle), glyph.format)
}
- fetch_buffer.push(GlyphFetchResult {
- index_in_text_run: loop_index as i32,
- uv_rect_address: gpu_cache.get_address(&cache_item.uv_rect_handle),
- });
+ GlyphCacheEntry::Blank | GlyphCacheEntry::Pending => continue,
+ };
+ if current_texture_id != cache_item.texture_id ||
+ current_glyph_format != glyph_format {
+ if !fetch_buffer.is_empty() {
+ f(current_texture_id, current_glyph_format, fetch_buffer);
+ fetch_buffer.clear();
+ }
+ current_texture_id = cache_item.texture_id;
+ current_glyph_format = glyph_format;
}
+ fetch_buffer.push(GlyphFetchResult {
+ index_in_text_run: loop_index as i32,
+ uv_rect_address: gpu_cache.get_address(&cache_item.uv_rect_handle),
+ });
}
if !fetch_buffer.is_empty() {
f(current_texture_id, current_glyph_format, fetch_buffer);
fetch_buffer.clear();
}
}
@@ -790,35 +853,38 @@ impl ResourceCache {
})
.collect()
}
pub fn begin_frame(&mut self, frame_id: FrameId) {
debug_assert_eq!(self.state, State::Idle);
self.state = State::AddResources;
self.texture_cache.begin_frame(frame_id);
- self.cached_glyphs.begin_frame(&mut self.texture_cache);
+ self.cached_glyphs.begin_frame(&mut self.texture_cache, &self.cached_render_tasks);
self.cached_render_tasks.begin_frame(&mut self.texture_cache);
self.current_frame_id = frame_id;
}
pub fn block_until_all_resources_added(
&mut self,
gpu_cache: &mut GpuCache,
+ render_tasks: &mut RenderTaskTree,
texture_cache_profile: &mut TextureCacheProfileCounters,
) {
profile_scope!("block_until_all_resources_added");
debug_assert_eq!(self.state, State::AddResources);
self.state = State::QueryResources;
self.glyph_rasterizer.resolve_glyphs(
&mut self.cached_glyphs,
&mut self.texture_cache,
gpu_cache,
+ &mut self.cached_render_tasks,
+ render_tasks,
texture_cache_profile,
);
// Apply any updates of new / updated images (incl. blobs) to the texture cache.
self.update_texture_cache(gpu_cache);
self.texture_cache.end_frame(texture_cache_profile);
}
@@ -1317,21 +1383,17 @@ impl ResourceCache {
res.font_templates.insert(key, template);
}
info!("\timage templates...");
let mut external_images = Vec::new();
for (key, template) in resources.image_templates {
let data = match CaptureConfig::deserialize::<PlainExternalImage, _>(root, &template.data) {
Some(plain) => {
- let ext_data = ExternalImageData {
- id: plain.id,
- channel_index: plain.channel_index,
- image_type: ExternalImageType::Buffer,
- };
+ let ext_data = plain.external;
external_images.push(plain);
ImageData::External(ext_data)
}
None => {
let arc = match raw_map.entry(template.data) {
Entry::Occupied(e) => {
e.get().clone()
}
--- a/gfx/webrender/src/scene_builder.rs
+++ b/gfx/webrender/src/scene_builder.rs
@@ -31,17 +31,17 @@ pub enum SceneBuilderResult {
document_id: DocumentId,
built_scene: Option<BuiltScene>,
resource_updates: ResourceUpdates,
frame_ops: Vec<FrameMsg>,
render: bool,
},
}
-/// Contains the the render backend data needed to build a scene.
+/// Contains the render backend data needed to build a scene.
pub struct SceneRequest {
pub scene: Scene,
pub view: DocumentView,
pub font_instances: FontInstanceMap,
pub tiled_image_map: TiledImageMap,
pub output_pipelines: FastHashSet<PipelineId>,
pub removed_pipelines: Vec<PipelineId>,
}
--- a/gfx/webrender/src/shade.rs
+++ b/gfx/webrender/src/shade.rs
@@ -18,17 +18,17 @@ use renderer::{
};
use util::TransformedRectKind;
use gleam::gl::GlType;
use time::precise_time_ns;
impl ImageBufferKind {
- fn get_feature_string(&self) -> &'static str {
+ pub(crate) fn get_feature_string(&self) -> &'static str {
match *self {
ImageBufferKind::Texture2D => "TEXTURE_2D",
ImageBufferKind::Texture2DArray => "",
ImageBufferKind::TextureRect => "TEXTURE_RECT",
ImageBufferKind::TextureExternal => "TEXTURE_EXTERNAL",
}
}
@@ -49,33 +49,37 @@ pub const IMAGE_BUFFER_KINDS: [ImageBuff
ImageBufferKind::TextureExternal,
ImageBufferKind::Texture2DArray,
];
const TRANSFORM_FEATURE: &str = "TRANSFORM";
const ALPHA_FEATURE: &str = "ALPHA_PASS";
const DITHERING_FEATURE: &str = "DITHERING";
-enum ShaderKind {
+pub(crate) enum ShaderKind {
Primitive,
Cache(VertexArrayKind),
ClipCache,
Brush,
Text,
+ #[allow(dead_code)]
+ VectorStencil,
+ #[allow(dead_code)]
+ VectorCover,
}
pub struct LazilyCompiledShader {
program: Option<Program>,
name: &'static str,
kind: ShaderKind,
features: Vec<&'static str>,
}
impl LazilyCompiledShader {
- fn new(
+ pub(crate) fn new(
kind: ShaderKind,
name: &'static str,
features: &[&'static str],
device: &mut Device,
precache: bool,
) -> Result<Self, ShaderError> {
let mut shader = LazilyCompiledShader {
program: None,
@@ -129,16 +133,28 @@ impl LazilyCompiledShader {
VertexArrayKind::Primitive)
}
ShaderKind::Cache(format) => {
create_prim_shader(self.name,
device,
&self.features,
format)
}
+ ShaderKind::VectorStencil => {
+ create_prim_shader(self.name,
+ device,
+ &self.features,
+ VertexArrayKind::VectorStencil)
+ }
+ ShaderKind::VectorCover => {
+ create_prim_shader(self.name,
+ device,
+ &self.features,
+ VertexArrayKind::VectorCover)
+ }
ShaderKind::ClipCache => {
create_clip_shader(self.name, device)
}
};
self.program = Some(program?);
}
Ok(self.program.as_ref().unwrap())
@@ -349,16 +365,18 @@ fn create_prim_shader(
}
debug!("PrimShader {}", name);
let vertex_descriptor = match vertex_format {
VertexArrayKind::Primitive => desc::PRIM_INSTANCES,
VertexArrayKind::Blur => desc::BLUR,
VertexArrayKind::Clip => desc::CLIP,
+ VertexArrayKind::VectorStencil => desc::VECTOR_STENCIL,
+ VertexArrayKind::VectorCover => desc::VECTOR_COVER,
};
let program = device.create_program(name, &prefix, &vertex_descriptor);
if let Ok(ref program) = program {
device.bind_shader_samplers(
program,
&[
@@ -442,17 +460,16 @@ pub struct Shaders {
// output, and the cache_image shader blits the results of
// a cache shader (e.g. blur) to the screen.
pub ps_text_run: TextShader,
pub ps_text_run_dual_source: TextShader,
ps_image: Vec<Option<PrimitiveShader>>,
ps_border_corner: PrimitiveShader,
ps_border_edge: PrimitiveShader,
- ps_hw_composite: LazilyCompiledShader,
ps_split_composite: LazilyCompiledShader,
}
impl Shaders {
pub fn new(
device: &mut Device,
gl_type: GlType,
options: &RendererOptions,
@@ -652,24 +669,16 @@ impl Shaders {
let ps_border_edge = PrimitiveShader::new(
"ps_border_edge",
device,
&[],
options.precache_shaders,
)?;
- let ps_hw_composite = LazilyCompiledShader::new(
- ShaderKind::Primitive,
- "ps_hardware_composite",
- &[],
- device,
- options.precache_shaders,
- )?;
-
let ps_split_composite = LazilyCompiledShader::new(
ShaderKind::Primitive,
"ps_split_composite",
&[],
device,
options.precache_shaders,
)?;
@@ -688,35 +697,31 @@ impl Shaders {
cs_clip_border,
cs_clip_image,
cs_clip_line,
ps_text_run,
ps_text_run_dual_source,
ps_image,
ps_border_corner,
ps_border_edge,
- ps_hw_composite,
ps_split_composite,
})
}
fn get_yuv_shader_index(
buffer_kind: ImageBufferKind,
format: YuvFormat,
color_space: YuvColorSpace,
) -> usize {
((buffer_kind as usize) * YUV_FORMATS.len() + (format as usize)) * YUV_COLOR_SPACES.len() +
(color_space as usize)
}
pub fn get(&mut self, key: &BatchKey) -> &mut LazilyCompiledShader {
match key.kind {
- BatchKind::HardwareComposite => {
- &mut self.ps_hw_composite
- }
BatchKind::SplitComposite => {
&mut self.ps_split_composite
}
BatchKind::Brush(brush_kind) => {
let brush_shader = match brush_kind {
BrushBatchKind::Solid => {
&mut self.brush_solid
}
@@ -796,12 +801,11 @@ impl Shaders {
}
for shader in self.brush_yuv_image {
if let Some(shader) = shader {
shader.deinit(device);
}
}
self.ps_border_corner.deinit(device);
self.ps_border_edge.deinit(device);
- self.ps_hw_composite.deinit(device);
self.ps_split_composite.deinit(device);
}
}
--- a/gfx/webrender/src/spring.rs
+++ b/gfx/webrender/src/spring.rs
@@ -99,12 +99,12 @@ fn next(cur: f32, prev: f32, dest: f32,
// Calculate new velocity after adding acceleration. Scale to framerate.
let nextv = vel + acc;
// Calculate next position by integrating velocity. Scale to framerate.
let next = cur + nextv;
next
}
-/// Given numbers, calcluate if a spring is at rest.
+/// Given numbers, calculate if a spring is at rest.
fn is_resting(cur: f32, prev: f32, dest: f32) -> bool {
(cur - prev).abs() < EPSILON && (cur - dest).abs() < EPSILON
}
--- a/gfx/webrender/src/texture_cache.rs
+++ b/gfx/webrender/src/texture_cache.rs
@@ -167,17 +167,17 @@ type WeakCacheEntryHandle = WeakFreeList
// A texture cache handle is a weak reference to a cache entry.
// If the handle has not been inserted into the cache yet, the
// value will be None. Even when the value is Some(), the location
// may not actually be valid if it has been evicted by the cache.
// In this case, the cache handle needs to re-upload this item
// to the texture cache (see request() below).
#[derive(Debug)]
-#[cfg_attr(feature = "capture", derive(Clone, Serialize))]
+#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct TextureCacheHandle {
entry: Option<WeakCacheEntryHandle>,
}
impl TextureCacheHandle {
pub fn new() -> Self {
TextureCacheHandle { entry: None }
@@ -492,17 +492,17 @@ impl TextureCache {
handle.entry.as_ref().map_or(false, |handle| {
self.entries.get_opt(handle).is_some()
})
}
// Retrieve the details of an item in the cache. This is used
// during batch creation to provide the resource rect address
// to the shaders and texture ID to the batching logic.
- // This function will asssert in debug modes if the caller
+ // This function will assert in debug modes if the caller
// tries to get a handle that was not requested this frame.
pub fn get(&self, handle: &TextureCacheHandle) -> CacheItem {
match handle.entry {
Some(ref handle) => {
let entry = self.entries
.get_opt(handle)
.expect("BUG: was dropped from cache or not updated!");
debug_assert_eq!(entry.last_access, self.frame_id);
--- a/gfx/webrender/src/tiling.rs
+++ b/gfx/webrender/src/tiling.rs
@@ -4,47 +4,53 @@
use api::{ColorF, DeviceIntPoint, DeviceIntRect, DeviceIntSize, DevicePixelScale, DeviceUintPoint};
use api::{DeviceUintRect, DeviceUintSize, DocumentLayer, FilterOp, ImageFormat, LayerRect};
use api::{MixBlendMode, PipelineId};
use batch::{AlphaBatchBuilder, AlphaBatchContainer, ClipBatcher, resolve_image};
use clip::{ClipStore};
use clip_scroll_tree::{ClipScrollTree, ClipScrollNodeIndex};
use device::{FrameId, Texture};
+#[cfg(feature = "pathfinder")]
+use euclid::{TypedPoint2D, TypedVector2D};
use gpu_cache::{GpuCache};
use gpu_types::{BlurDirection, BlurInstance};
use gpu_types::{ClipScrollNodeData, ZBufferIdGenerator};
use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture};
+#[cfg(feature = "pathfinder")]
+use pathfinder_partitioner::mesh::Mesh;
use prim_store::{CachedGradient, PrimitiveIndex, PrimitiveKind, PrimitiveStore};
use prim_store::{BrushKind, DeferredResolve};
use profiler::FrameProfileCounters;
-use render_task::{BlitSource, RenderTaskId, RenderTaskKind};
-use render_task::{BlurTask, ClearMode, RenderTaskLocation, RenderTaskTree};
+use render_task::{BlitSource, RenderTaskAddress, RenderTaskId, RenderTaskKind};
+use render_task::{BlurTask, ClearMode, GlyphTask, RenderTaskLocation, RenderTaskTree};
use resource_cache::ResourceCache;
use std::{cmp, usize, f32, i32};
use texture_allocator::GuillotineAllocator;
+#[cfg(feature = "pathfinder")]
+use webrender_api::{DevicePixel, FontRenderMode};
const MIN_TARGET_SIZE: u32 = 2048;
#[derive(Debug)]
pub struct ScrollbarPrimitive {
pub scroll_frame_index: ClipScrollNodeIndex,
pub prim_index: PrimitiveIndex,
pub frame_rect: LayerRect,
}
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct RenderTargetIndex(pub usize);
-pub struct RenderTargetContext<'a> {
+pub struct RenderTargetContext<'a, 'rc> {
pub device_pixel_scale: DevicePixelScale,
pub prim_store: &'a PrimitiveStore,
- pub resource_cache: &'a ResourceCache,
+ pub resource_cache: &'rc mut ResourceCache,
pub clip_scroll_tree: &'a ClipScrollTree,
pub use_dual_source_blending: bool,
pub node_data: &'a [ClipScrollNodeData],
pub cached_gradients: &'a [CachedGradient],
}
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
@@ -89,17 +95,17 @@ impl TextureAllocator {
pub trait RenderTarget {
fn new(
size: Option<DeviceUintSize>,
screen_size: DeviceIntSize,
) -> Self;
fn allocate(&mut self, size: DeviceUintSize) -> Option<DeviceUintPoint>;
fn build(
&mut self,
- _ctx: &RenderTargetContext,
+ _ctx: &mut RenderTargetContext,
_gpu_cache: &mut GpuCache,
_render_tasks: &mut RenderTaskTree,
_deferred_resolves: &mut Vec<DeferredResolve>,
) {
}
// TODO(gw): It's a bit odd that we need the deferred resolves and mutable
// GPU cache here. They are typically used by the build step
// above. They are used for the blit jobs to allow resolve_image
@@ -151,17 +157,17 @@ impl<T: RenderTarget> RenderTargetList<T
targets: Vec::new(),
saved_index: None,
is_shared: false,
}
}
fn build(
&mut self,
- ctx: &RenderTargetContext,
+ ctx: &mut 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;
@@ -256,16 +262,33 @@ pub enum BlitJobSource {
// Information required to do a blit from a source to a target.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct BlitJob {
pub source: BlitJobSource,
pub target_rect: DeviceIntRect,
}
+#[cfg(feature = "pathfinder")]
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub struct GlyphJob {
+ pub mesh: Mesh,
+ pub target_rect: DeviceIntRect,
+ pub origin: DeviceIntPoint,
+ pub subpixel_offset: TypedPoint2D<f32, DevicePixel>,
+ pub render_mode: FontRenderMode,
+ pub embolden_amount: TypedVector2D<f32, DevicePixel>,
+}
+
+#[cfg(not(feature = "pathfinder"))]
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub struct GlyphJob;
+
/// A render target represents a number of rendering operations on a surface.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct ColorRenderTarget {
pub alpha_batch_containers: Vec<AlphaBatchContainer>,
// List of blur operations to apply for this render target.
pub vertical_blurs: Vec<BlurInstance>,
pub horizontal_blurs: Vec<BlurInstance>,
@@ -302,33 +325,33 @@ impl RenderTarget for ColorRenderTarget
outputs: Vec::new(),
alpha_tasks: Vec::new(),
screen_size,
}
}
fn build(
&mut self,
- ctx: &RenderTargetContext,
+ ctx: &mut RenderTargetContext,
gpu_cache: &mut GpuCache,
render_tasks: &mut RenderTaskTree,
deferred_resolves: &mut Vec<DeferredResolve>,
) {
let mut merged_batches = AlphaBatchContainer::new(None);
let mut z_generator = ZBufferIdGenerator::new();
for task_id in &self.alpha_tasks {
let task = &render_tasks[*task_id];
match task.kind {
RenderTaskKind::Picture(ref pic_task) => {
let brush_index = ctx.prim_store.cpu_metadata[pic_task.prim_index.0].cpu_prim_index;
let brush = &ctx.prim_store.cpu_brushes[brush_index.0];
match brush.kind {
- BrushKind::Picture { pic_index } => {
+ BrushKind::Picture { pic_index, .. } => {
let pic = &ctx.prim_store.pictures[pic_index.0];
let (target_rect, _) = task.get_target_rect();
let mut batch_builder = AlphaBatchBuilder::new(self.screen_size, target_rect);
batch_builder.add_pic_to_batch(
pic,
*task_id,
@@ -367,29 +390,27 @@ impl RenderTarget for ColorRenderTarget
deferred_resolves: &mut Vec<DeferredResolve>,
) {
let task = &render_tasks[task_id];
match task.kind {
RenderTaskKind::VerticalBlur(ref info) => {
info.add_instances(
&mut self.vertical_blurs,
- task_id,
- task.children[0],
BlurDirection::Vertical,
- render_tasks,
+ render_tasks.get_task_address(task_id),
+ render_tasks.get_task_address(task.children[0]),
);
}
RenderTaskKind::HorizontalBlur(ref info) => {
info.add_instances(
&mut self.horizontal_blurs,
- task_id,
- task.children[0],
BlurDirection::Horizontal,
- render_tasks,
+ render_tasks.get_task_address(task_id),
+ render_tasks.get_task_address(task.children[0]),
);
}
RenderTaskKind::Picture(ref task_info) => {
let prim_metadata = ctx.prim_store.get_metadata(task_info.prim_index);
match prim_metadata.prim_kind {
PrimitiveKind::Brush => {
let brush = &ctx.prim_store.cpu_brushes[prim_metadata.cpu_prim_index.0];
let pic = &ctx.prim_store.pictures[brush.get_picture_index().0];
@@ -410,16 +431,20 @@ impl RenderTarget for ColorRenderTarget
unreachable!()
}
}
}
RenderTaskKind::ClipRegion(..) |
RenderTaskKind::CacheMask(..) => {
panic!("Should not be added to color target!");
}
+ RenderTaskKind::Glyph(..) => {
+ // FIXME(pcwalton): Support color glyphs.
+ panic!("Glyphs should not be added to color target!");
+ }
RenderTaskKind::Readback(device_rect) => {
self.readbacks.push(device_rect);
}
RenderTaskKind::Scaling(..) => {
self.scalings.push(ScalingInfo {
src_task_id: task.children[0],
dest_task_id: task_id,
});
@@ -534,35 +559,34 @@ impl RenderTarget for AlphaRenderTarget
ClearMode::Transparent => {
panic!("bug: invalid clear mode for alpha task");
}
}
match task.kind {
RenderTaskKind::Readback(..) |
RenderTaskKind::Picture(..) |
- RenderTaskKind::Blit(..) => {
+ RenderTaskKind::Blit(..) |
+ RenderTaskKind::Glyph(..) => {
panic!("BUG: should not be added to alpha target!");
}
RenderTaskKind::VerticalBlur(ref info) => {
info.add_instances(
&mut self.vertical_blurs,
- task_id,
- task.children[0],
BlurDirection::Vertical,
- render_tasks,
+ render_tasks.get_task_address(task_id),
+ render_tasks.get_task_address(task.children[0]),
);
}
RenderTaskKind::HorizontalBlur(ref info) => {
info.add_instances(
&mut self.horizontal_blurs,
- task_id,
- task.children[0],
BlurDirection::Horizontal,
- render_tasks,
+ render_tasks.get_task_address(task_id),
+ render_tasks.get_task_address(task.children[0]),
);
}
RenderTaskKind::CacheMask(ref task_info) => {
let task_address = render_tasks.get_task_address(task_id);
self.clip_batcher.add(
task_address,
&task_info.clips,
task_info.coordinate_system_id,
@@ -596,74 +620,98 @@ impl RenderTarget for AlphaRenderTarget
}
}
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct TextureCacheRenderTarget {
pub horizontal_blurs: Vec<BlurInstance>,
pub blits: Vec<BlitJob>,
+ pub glyphs: Vec<GlyphJob>,
}
impl TextureCacheRenderTarget {
fn new(
_size: Option<DeviceUintSize>,
_screen_size: DeviceIntSize,
) -> Self {
TextureCacheRenderTarget {
- horizontal_blurs: Vec::new(),
- blits: Vec::new(),
+ horizontal_blurs: vec![],
+ blits: vec![],
+ glyphs: vec![],
}
}
fn add_task(
&mut self,
task_id: RenderTaskId,
- render_tasks: &RenderTaskTree,
+ render_tasks: &mut RenderTaskTree,
) {
- let task = &render_tasks[task_id];
+ let task_address = render_tasks.get_task_address(task_id);
+ let src_task_address = render_tasks[task_id].children.get(0).map(|src_task_id| {
+ render_tasks.get_task_address(*src_task_id)
+ });
+
+ let task = &mut render_tasks[task_id];
+ let target_rect = task.get_target_rect();
match task.kind {
RenderTaskKind::HorizontalBlur(ref info) => {
info.add_instances(
&mut self.horizontal_blurs,
- task_id,
- task.children[0],
BlurDirection::Horizontal,
- render_tasks,
+ task_address,
+ src_task_address.unwrap(),
);
}
RenderTaskKind::Blit(ref task_info) => {
match task_info.source {
BlitSource::Image { .. } => {
// reading/writing from the texture cache at the same time
// is undefined behavior.
panic!("bug: a single blit cannot be to/from texture cache");
}
BlitSource::RenderTask { task_id } => {
// Add a blit job to copy from an existing render
// task to this target.
- let (target_rect, _) = task.get_target_rect();
self.blits.push(BlitJob {
source: BlitJobSource::RenderTask(task_id),
- target_rect,
+ target_rect: target_rect.0,
});
}
}
}
+ RenderTaskKind::Glyph(ref mut task_info) => {
+ self.add_glyph_task(task_info, target_rect.0)
+ }
RenderTaskKind::VerticalBlur(..) |
RenderTaskKind::Picture(..) |
RenderTaskKind::ClipRegion(..) |
RenderTaskKind::CacheMask(..) |
RenderTaskKind::Readback(..) |
RenderTaskKind::Scaling(..) => {
panic!("BUG: unexpected task kind for texture cache target");
}
}
}
+
+ #[cfg(feature = "pathfinder")]
+ fn add_glyph_task(&mut self, task_info: &mut GlyphTask, target_rect: DeviceIntRect) {
+ self.glyphs.push(GlyphJob {
+ mesh: task_info.mesh.take().unwrap(),
+ target_rect: target_rect,
+ origin: task_info.origin,
+ subpixel_offset: task_info.subpixel_offset,
+ render_mode: task_info.render_mode,
+ embolden_amount: task_info.embolden_amount,
+ })
+ }
+
+ #[cfg(not(feature = "pathfinder"))]
+ fn add_glyph_task(&mut self, _: &mut GlyphTask, _: DeviceIntRect) {}
}
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum RenderPassKind {
MainFramebuffer(ColorRenderTarget),
OffScreen {
alpha: RenderTargetList<AlphaRenderTarget>,
@@ -719,17 +767,17 @@ impl RenderPass {
max_size.height = cmp::max(max_size.height, size.height as u32);
}
self.tasks.push(task_id);
}
pub fn build(
&mut self,
- ctx: &RenderTargetContext,
+ ctx: &mut RenderTargetContext,
gpu_cache: &mut GpuCache,
render_tasks: &mut RenderTaskTree,
deferred_resolves: &mut Vec<DeferredResolve>,
clip_store: &ClipStore,
) {
profile_scope!("RenderPass::build");
match self.kind {
@@ -922,22 +970,35 @@ impl Frame {
self.has_texture_cache_tasks && !self.has_been_rendered
}
}
impl BlurTask {
fn add_instances(
&self,
instances: &mut Vec<BlurInstance>,
- task_id: RenderTaskId,
- source_task_id: RenderTaskId,
blur_direction: BlurDirection,
- render_tasks: &RenderTaskTree,
+ task_address: RenderTaskAddress,
+ src_task_address: RenderTaskAddress,
) {
let instance = BlurInstance {
- task_address: render_tasks.get_task_address(task_id),
- src_task_address: render_tasks.get_task_address(source_task_id),
+ task_address,
+ src_task_address,
blur_direction,
};
instances.push(instance);
}
}
+
+pub struct SpecialRenderPasses {
+ pub alpha_glyph_pass: RenderPass,
+ pub color_glyph_pass: RenderPass,
+}
+
+impl SpecialRenderPasses {
+ pub fn new(screen_size: &DeviceIntSize) -> SpecialRenderPasses {
+ SpecialRenderPasses {
+ alpha_glyph_pass: RenderPass::new_off_screen(*screen_size),
+ color_glyph_pass: RenderPass::new_off_screen(*screen_size),
+ }
+ }
+}
--- a/gfx/webrender/tests/angle_shader_validation.rs
+++ b/gfx/webrender/tests/angle_shader_validation.rs
@@ -54,20 +54,16 @@ const SHADERS: &[Shader] = &[
name: "ps_border_corner",
features: PRIM_FEATURES,
},
Shader {
name: "ps_border_edge",
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 {
@@ -75,29 +71,29 @@ const SHADERS: &[Shader] = &[
features: PRIM_FEATURES,
},
// Brush shaders
Shader {
name: "brush_yuv_image",
features: &["", "YUV_NV12", "YUV_PLANAR", "YUV_INTERLEAVED", "YUV_NV12,TEXTURE_RECT"],
},
Shader {
- name: "brush_mask",
+ name: "brush_solid",
features: &[],
},
Shader {
- name: "brush_solid",
- features: &[],
+ name: "brush_image",
+ features: &["", "ALPHA_PASS"],
},
Shader {
name: "brush_blend",
features: &[],
},
Shader {
- name: "brush_composite",
+ name: "brush_mix_blend",
features: &[],
},
Shader {
name: "brush_radial_gradient",
features: &[ "DITHERING" ],
},
Shader {
name: "brush_linear_gradient",
--- a/gfx/webrender_api/Cargo.toml
+++ b/gfx/webrender_api/Cargo.toml
@@ -1,11 +1,11 @@
[package]
name = "webrender_api"
-version = "0.57.0"
+version = "0.57.2"
authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
license = "MPL-2.0"
repository = "https://github.com/servo/webrender"
[features]
nightly = ["euclid/unstable", "serde/unstable"]
ipc = ["ipc-channel"]
serialize = []
@@ -15,16 +15,17 @@ deserialize = []
app_units = "0.6"
bincode = "1.0"
bitflags = "1.0"
byteorder = "1.2.1"
ipc-channel = {version = "0.10.0", optional = true}
euclid = { version = "0.17", features = ["serde"] }
serde = { version = "=1.0.35", features = ["rc"] }
serde_derive = { version = "=1.0.35", features = ["deserialize_in_place"] }
+serde_bytes = "0.10"
time = "0.1"
[target.'cfg(target_os = "macos")'.dependencies]
core-foundation = "0.5"
core-graphics = "0.13"
[target.'cfg(target_os = "windows")'.dependencies]
dwrote = "0.4.1"
--- a/gfx/webrender_api/src/api.rs
+++ b/gfx/webrender_api/src/api.rs
@@ -1,12 +1,14 @@
/* 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/. */
+extern crate serde_bytes;
+
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, ColorF, DeviceIntPoint, DeviceUintRect};
@@ -400,17 +402,21 @@ pub struct UpdateImage {
pub key: ImageKey,
pub descriptor: ImageDescriptor,
pub data: ImageData,
pub dirty_rect: Option<DeviceUintRect>,
}
#[derive(Clone, Deserialize, Serialize)]
pub enum AddFont {
- Raw(FontKey, Vec<u8>, u32),
+ Raw(
+ FontKey,
+ #[serde(with = "serde_bytes")] Vec<u8>,
+ u32
+ ),
Native(FontKey, NativeFontHandle),
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct HitTestItem {
/// The pipeline that the display item that was hit belongs to.
pub pipeline: PipelineId,
@@ -686,17 +692,17 @@ pub struct ResourceId(pub u32);
pub struct ExternalEvent {
raw: usize,
}
unsafe impl Send for ExternalEvent {}
impl ExternalEvent {
pub fn from_raw(raw: usize) -> Self {
- ExternalEvent { raw: raw }
+ ExternalEvent { raw }
}
/// Consumes self to make it obvious that the event should be forwarded only once.
pub fn unwrap(self) -> usize {
self.raw
}
}
#[derive(Clone, Deserialize, Serialize)]
@@ -735,17 +741,17 @@ impl RenderApiSender {
} else {
panic!("CloneApi message response was dropped while Webrender was still alive: {}", e);
}
}
};
RenderApi {
api_sender: self.api_sender.clone(),
payload_sender: self.payload_sender.clone(),
- namespace_id: namespace_id,
+ namespace_id,
next_id: Cell::new(ResourceId(0)),
}
}
}
pub struct RenderApi {
api_sender: MsgSender<ApiMsg>,
payload_sender: PayloadSender,
--- a/gfx/webrender_api/src/channel_mpsc.rs
+++ b/gfx/webrender_api/src/channel_mpsc.rs
@@ -50,22 +50,22 @@ pub struct MsgSender<T> {
impl<T> MsgSender<T> {
pub fn send(&self, data: T) -> Result<(), Error> {
self.tx.send(data).map_err(|_| Error::new(ErrorKind::Other, "cannot send on closed channel"))
}
}
pub fn payload_channel() -> Result<(PayloadSender, PayloadReceiver), Error> {
let (tx, rx) = mpsc::channel();
- Ok((PayloadSender { tx: tx }, PayloadReceiver { rx: rx }))
+ Ok((PayloadSender { tx }, PayloadReceiver { rx }))
}
pub fn msg_channel<T>() -> Result<(MsgSender<T>, MsgReceiver<T>), Error> {
let (tx, rx) = mpsc::channel();
- Ok((MsgSender { tx: tx }, MsgReceiver { rx: rx }))
+ Ok((MsgSender { tx }, MsgReceiver { rx }))
}
///
/// These serialize methods are needed to satisfy the compiler
/// which uses these implementations for IPC, and also for the
/// recording tool. The recording tool only outputs messages
/// that don't contain Senders or Receivers, so in theory
/// these should never be called in the in-process config.
--- a/gfx/webrender_api/src/display_item.rs
+++ b/gfx/webrender_api/src/display_item.rs
@@ -622,17 +622,17 @@ impl From<LayoutRect> for LocalClip {
LocalClip::Rect(rect)
}
}
impl LocalClip {
pub fn clip_rect(&self) -> &LayoutRect {
match *self {
LocalClip::Rect(ref rect) => rect,
- LocalClip::RoundedRect(ref rect, _) => &rect,
+ LocalClip::RoundedRect(ref rect, _) => rect,
}
}
pub fn create_with_offset(&self, offset: &LayoutVector2D) -> LocalClip {
match *self {
LocalClip::Rect(rect) => LocalClip::from(rect.translate(offset)),
LocalClip::RoundedRect(rect, complex) => LocalClip::RoundedRect(
rect.translate(offset),
--- a/gfx/webrender_api/src/display_list.rs
+++ b/gfx/webrender_api/src/display_list.rs
@@ -190,17 +190,17 @@ fn skip_slice<T: for<'de> Deserialize<'d
impl<'a> BuiltDisplayListIter<'a> {
pub fn new(list: &'a BuiltDisplayList) -> Self {
Self::new_with_list_and_data(list, list.item_slice())
}
pub fn new_with_list_and_data(list: &'a BuiltDisplayList, data: &'a [u8]) -> Self {
BuiltDisplayListIter {
list,
- data: &data,
+ data,
cur_item: DisplayItem {
// Dummy data, will be overwritten by `next`
item: SpecificDisplayItem::PopStackingContext,
clip_and_scroll:
ClipAndScrollInfo::simple(ClipId::root_scroll_node(PipelineId::dummy())),
info: LayoutPrimitiveInfo::new(LayoutRect::zero()),
},
cur_stops: ItemRange::default(),
@@ -231,17 +231,17 @@ impl<'a> BuiltDisplayListIter<'a> {
}
// Don't let these bleed into another item
self.cur_stops = ItemRange::default();
self.cur_complex_clip = (ItemRange::default(), 0);
self.cur_clip_chain_items = ItemRange::default();
loop {
- if self.data.len() == 0 {
+ if self.data.is_empty() {
return None;
}
{
let reader = bincode::IoReader::new(UnsafeReader::new(&mut self.data));
bincode::deserialize_in_place(reader, &mut self.cur_item)
.expect("MEH: malicious process?");
}
@@ -329,18 +329,18 @@ impl<'a, 'b> DisplayItemRef<'a, 'b> {
pub fn rect(&self) -> LayoutRect {
self.iter.cur_item.info.rect
}
pub fn get_layer_primitive_info(&self, offset: &LayoutVector2D) -> LayerPrimitiveInfo {
let info = self.iter.cur_item.info;
LayerPrimitiveInfo {
- rect: info.rect.translate(&offset),
- clip_rect: info.clip_rect.translate(&offset),
+ rect: info.rect.translate(offset),
+ clip_rect: info.clip_rect.translate(offset),
is_backface_visible: info.is_backface_visible,
tag: info.tag,
}
}
pub fn clip_rect(&self) -> &LayoutRect {
&self.iter.cur_item.info.clip_rect
}
@@ -384,17 +384,17 @@ impl<'a, 'b> DisplayItemRef<'a, 'b> {
// Creates a new iterator where this element's iterator is, to hack around borrowck.
pub fn sub_iter(&self) -> BuiltDisplayListIter<'a> {
BuiltDisplayListIter::new_with_list_and_data(self.iter.list, self.iter.data)
}
}
impl<'de, 'a, T: Deserialize<'de>> AuxIter<'a, T> {
pub fn new(mut data: &'a [u8]) -> Self {
- let size: usize = if data.len() == 0 {
+ let size: usize = if data.is_empty() {
0 // Accept empty ItemRanges pointing anywhere
} else {
bincode::deserialize_from(&mut UnsafeReader::new(&mut data)).expect("MEH: malicious input?")
};
AuxIter {
data,
size,
@@ -737,17 +737,17 @@ struct UnsafeReader<'a: 'b, 'b> {
}
impl<'a, 'b> UnsafeReader<'a, 'b> {
#[inline(always)]
fn new(buf: &'b mut &'a [u8]) -> UnsafeReader<'a, 'b> {
unsafe {
let end = buf.as_ptr().offset(buf.len() as isize);
let start = buf.as_ptr();
- UnsafeReader { start: start, end, slice: buf }
+ UnsafeReader { start, end, slice: buf }
}
}
// This read implementation is significantly faster than the standard &[u8] one.
//
// First, it only supports reading exactly buf.len() bytes. This ensures that
// the argument to memcpy is always buf.len() and will allow a constant buf.len()
// to be propagated through to memcpy which LLVM will turn into explicit loads and
@@ -870,17 +870,17 @@ impl DisplayListBuilder {
let state = self.save_state.take().expect("No save to restore DisplayListBuilder from");
self.clip_stack.truncate(state.clip_stack_len);
self.data.truncate(state.dl_len);
self.next_clip_id = state.next_clip_id;
self.next_clip_chain_id = state.next_clip_chain_id;
}
- /// Discards the builder's save (indicating the attempted operation was sucessful).
+ /// Discards the builder's save (indicating the attempted operation was successful).
pub fn clear_save(&mut self) {
self.save_state.take().expect("No save to clear in DisplayListBuilder");
}
pub fn print_display_list(&mut self) {
let mut temp = BuiltDisplayList::default();
mem::swap(&mut temp.data, &mut self.data);
@@ -1171,18 +1171,18 @@ impl DisplayListBuilder {
let (start_offset, end_offset) =
DisplayListBuilder::normalize_stops(&mut stops, extend_mode);
self.push_stops(&stops);
RadialGradient {
center,
radius,
- start_offset: start_offset,
- end_offset: end_offset,
+ start_offset,
+ end_offset,
extend_mode,
}
}
pub fn push_border(
&mut self,
info: &LayoutPrimitiveInfo,
widths: BorderWidths,
@@ -1218,17 +1218,17 @@ impl DisplayListBuilder {
}
/// Pushes a linear gradient to be displayed.
///
/// The gradient itself is described in the
/// `gradient` parameter. It is drawn on
/// a "tile" with the dimensions from `tile_size`.
/// These tiles are now repeated to the right and
- /// to the bottom infinitly. If `tile_spacing`
+ /// to the bottom infinitely. If `tile_spacing`
/// is not zero spacers with the given dimensions
/// are inserted between the tiles as seams.
///
/// The origin of the tiles is given in `info.rect.origin`.
/// If the gradient should only be displayed once limit
/// the `info.rect.size` to a single tile.
/// The gradient is only visible within the local clip.
pub fn push_gradient(
@@ -1420,17 +1420,17 @@ impl DisplayListBuilder {
) -> ClipId
where
I: IntoIterator<Item = ComplexClipRegion>,
I::IntoIter: ExactSizeIterator + Clone,
{
let id = self.generate_clip_id();
let item = SpecificDisplayItem::Clip(ClipDisplayItem {
id,
- image_mask: image_mask,
+ image_mask,
});
let info = LayoutPrimitiveInfo::new(clip_rect);
let scrollinfo = ClipAndScrollInfo::simple(parent);
self.push_item_with_clip_scroll_info(item, &info, scrollinfo);
self.push_iter(complex_clips);
id
@@ -1468,17 +1468,17 @@ impl DisplayListBuilder {
}
pub fn pop_clip_id(&mut self) {
self.clip_stack.pop();
if let Some(save_state) = self.save_state.as_ref() {
assert!(self.clip_stack.len() >= save_state.clip_stack_len,
"Cannot pop clips that were pushed before the DisplayListBuilder save.");
}
- assert!(self.clip_stack.len() > 0);
+ assert!(!self.clip_stack.is_empty());
}
pub fn push_iframe(&mut self, info: &LayoutPrimitiveInfo, pipeline_id: PipelineId) {
let item = SpecificDisplayItem::Iframe(IframeDisplayItem {
clip_id: self.generate_clip_id(),
pipeline_id,
});
self.push_item(item, info);
--- a/gfx/webrender_api/src/font.rs
+++ b/gfx/webrender_api/src/font.rs
@@ -98,17 +98,17 @@ pub enum FontRenderMode {
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug, Deserialize, Serialize, Ord, PartialOrd)]
pub enum SubpixelDirection {
None = 0,
Horizontal,
Vertical,
}
impl FontRenderMode {
- // Skia quantizes subpixel offets into 1/4 increments.
+ // Skia quantizes subpixel offsets into 1/4 increments.
// Given the absolute position, return the quantized increment
fn subpixel_quantize_offset(&self, pos: f32) -> SubpixelOffset {
// Following the conventions of Gecko and Skia, we want
// to quantize the subpixel position, such that abs(pos) gives:
// [0.0, 0.125) -> Zero
// [0.125, 0.375) -> Quarter
// [0.375, 0.625) -> Half
// [0.625, 0.875) -> ThreeQuarters,
--- a/gfx/webrender_api/src/image.rs
+++ b/gfx/webrender_api/src/image.rs
@@ -1,12 +1,14 @@
/* 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/. */
+extern crate serde_bytes;
+
use font::{FontInstanceKey, FontKey, FontTemplate};
use std::sync::Arc;
use {DevicePoint, DeviceUintRect, IdNamespace, TileOffset, TileSize};
#[repr(C)]
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct ImageKey(pub IdNamespace, pub u32);
@@ -105,38 +107,53 @@ impl ImageDescriptor {
pub fn compute_total_size(&self) -> u32 {
self.compute_stride() * self.height
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum ImageData {
- Raw(Arc<Vec<u8>>),
- Blob(BlobImageData),
+ Raw(#[serde(with = "serde_image_data_raw")] Arc<Vec<u8>>),
+ Blob(#[serde(with = "serde_bytes")] BlobImageData),
External(ExternalImageData),
}
+mod serde_image_data_raw {
+ extern crate serde_bytes;
+
+ use std::sync::Arc;
+ use serde::{Deserializer, Serializer};
+
+ pub fn serialize<'a, S: Serializer>(bytes: &'a Arc<Vec<u8>>, serializer: S) -> Result<S::Ok, S::Error> {
+ serde_bytes::serialize(bytes.as_slice(), serializer)
+ }
+
+ pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Arc<Vec<u8>>, D::Error> {
+ serde_bytes::deserialize(deserializer).map(Arc::new)
+ }
+}
+
impl ImageData {
pub fn new(bytes: Vec<u8>) -> Self {
ImageData::Raw(Arc::new(bytes))
}
pub fn new_shared(bytes: Arc<Vec<u8>>) -> Self {
ImageData::Raw(bytes)
}
pub fn new_blob_image(commands: Vec<u8>) -> Self {
ImageData::Blob(commands)
}
#[inline]
pub fn is_blob(&self) -> bool {
- match self {
- &ImageData::Blob(_) => true,
+ match *self {
+ ImageData::Blob(_) => true,
_ => false,
}
}
#[inline]
pub fn uses_texture_cache(&self) -> bool {
match *self {
ImageData::External(ref ext_data) => match ext_data.image_type {
--- a/gfx/webrender_bindings/Cargo.toml
+++ b/gfx/webrender_bindings/Cargo.toml
@@ -9,17 +9,17 @@ rayon = "1"
thread_profiler = "0.1.1"
euclid = { version = "0.17", features = ["serde"] }
app_units = "0.6"
gleam = "0.4.20"
log = "0.4"
[dependencies.webrender]
path = "../webrender"
-version = "0.57.0"
+version = "0.57.2"
default-features = false
features = ["capture"]
[target.'cfg(target_os = "windows")'.dependencies]
dwrote = "0.4.1"
[target.'cfg(target_os = "macos")'.dependencies]
core-foundation = "0.5"
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-22493328352ba432a7cd89491d81bfaa19bc1bce
+941bf5ac998061689a1bcd18d417f1f315e41ae6
--- a/gfx/wrench/Cargo.toml
+++ b/gfx/wrench/Cargo.toml
@@ -7,17 +7,17 @@ license = "MPL-2.0"
[dependencies]
base64 = "0.6"
bincode = "1.0"
byteorder = "1.0"
env_logger = { version = "0.5", optional = true }
euclid = "0.17"
gleam = "0.4"
-glutin = "0.12"
+glutin = "0.13"
app_units = "0.6"
image = "0.18"
clap = { version = "2", features = ["yaml"] }
lazy_static = "1"
log = "0.4"
yaml-rust = { git = "https://github.com/vvuk/yaml-rust", features = ["preserve_order"] }
serde_json = "1.0"
ron = "0.1.5"
--- a/gfx/wrench/src/args.yaml
+++ b/gfx/wrench/src/args.yaml
@@ -105,17 +105,17 @@ subcommands:
help: The input YAML file
required: true
index: 1
- replay:
about: replay binary recording
args:
- api:
long: api
- help: Reissue Api messsages for each frame
+ help: Reissue Api messages for each frame
- skip-uploads:
long: skip-uploads
help: Skip re-uploads while reissuing Api messages (BROKEN)
- play:
long: play
help: Play entire recording through, then quit (useful with --save)
- INPUT:
help: The input binary file or directory
--- a/gfx/wrench/src/blob.rs
+++ b/gfx/wrench/src/blob.rs
@@ -4,17 +4,17 @@
// A very basic BlobImageRenderer that can only render a checkerboard pattern.
use std::collections::HashMap;
use std::sync::Arc;
use std::sync::Mutex;
use webrender::api::*;
-// Serialize/deserialze the blob.
+// Serialize/deserialize the blob.
pub fn serialize_blob(color: ColorU) -> Vec<u8> {
vec![color.r, color.g, color.b, color.a]
}
fn deserialize_blob(blob: &[u8]) -> Result<ColorU, ()> {
let mut iter = blob.iter();
return match (iter.next(), iter.next(), iter.next(), iter.next()) {
@@ -50,32 +50,32 @@ fn render_blob(
let y2 = y + descriptor.offset.y as u32;
// Render a simple checkerboard pattern
let checker = if (x2 % 20 >= 10) != (y2 % 20 >= 10) {
1
} else {
0
};
- // ..nested in the per-tile cherkerboard pattern
+ // ..nested in the per-tile checkerboard pattern
let tc = if tile_checker { 0 } else { (1 - checker) * 40 };
match descriptor.format {
ImageFormat::BGRA8 => {
texels.push(color.b * checker + tc);
texels.push(color.g * checker + tc);
texels.push(color.r * checker + tc);
texels.push(color.a * checker + tc);
}
ImageFormat::R8 => {
texels.push(color.a * checker + tc);
}
_ => {
return Err(BlobImageError::Other(
- format!("Usupported image format {:?}", descriptor.format),
+ format!("Unsupported image format {:?}", descriptor.format),
));
}
}
}
}
Ok(RasterizedBlobImage {
data: texels,