Bug 1449562 - Update webrender to commit 941bf5ac998061689a1bcd18d417f1f315e41ae6. r=jrmuizel

MozReview-Commit-ID: 88ia1A1Dyhq

--HG--
extra : rebase_source : db4fc1e00eed8bdb20f0316e47d79d27bc28e322
This commit is contained in:
Kartikaya Gupta 2018-04-04 15:21:50 -04:00
parent 10601884d2
commit 7f1e850080
59 changed files with 2384 additions and 821 deletions

View File

@ -1,6 +1,6 @@
[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"
@ -9,10 +9,12 @@ 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"
@ -23,7 +25,7 @@ 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"}
@ -38,15 +40,34 @@ 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"

View File

@ -138,7 +138,7 @@ Imagine for a second a world in which you have *three alpha values per pixel*, o
- 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) {

View File

@ -24,8 +24,8 @@ use webrender::api::{self, DisplayListBuilder, DocumentId, PipelineId, RenderApi
// 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]
@ -73,7 +73,7 @@ fn render_blob(
} 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 {
@ -88,7 +88,7 @@ fn render_blob(
}
_ => {
return Err(api::BlobImageError::Other(
format!("Usupported image format"),
format!("Unsupported image format"),
));
}
}
@ -178,7 +178,7 @@ impl api::BlobImageRenderer for CheckerboardRenderer {
// 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);
}

Binary file not shown.

View File

@ -9,6 +9,7 @@ void brush_vs(
int prim_address,
RectWithSize local_rect,
ivec3 user_data,
mat4 transform,
PictureTask pic_task
);
@ -146,6 +147,7 @@ void main(void) {
brush.prim_address + VECS_PER_BRUSH_PRIM,
brush_prim.local_rect,
brush.user_data,
scroll_node.transform,
pic_task
);
}

View File

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#define VECS_PER_SPECIFIC_BRUSH 2
#define VECS_PER_SPECIFIC_BRUSH 0
#define FORCE_NO_PERSPECTIVE
#include shared,prim_shared,brush
@ -22,6 +22,7 @@ void brush_vs(
int prim_address,
RectWithSize local_rect,
ivec3 user_data,
mat4 transform,
PictureTask pic_task
) {
PictureTask src_task = fetch_picture_task(user_data.x);

View File

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#define VECS_PER_SPECIFIC_BRUSH 2
#define VECS_PER_SPECIFIC_BRUSH 0
#include shared,prim_shared,brush
@ -29,21 +29,37 @@ flat varying vec4 vColor;
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
@ -73,23 +89,51 @@ void brush_vs(
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
@ -110,16 +154,17 @@ void brush_vs(
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;

View File

@ -35,6 +35,7 @@ void brush_vs(
int prim_address,
RectWithSize local_rect,
ivec3 user_data,
mat4 transform,
PictureTask pic_task
) {
Gradient gradient = fetch_gradient(prim_address);

View File

@ -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

View File

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#define VECS_PER_SPECIFIC_BRUSH 2
#define VECS_PER_SPECIFIC_BRUSH 0
#include shared,prim_shared,brush
@ -17,6 +17,7 @@ void brush_vs(
int prim_address,
RectWithSize local_rect,
ivec3 user_data,
mat4 transform,
PictureTask pic_task
) {
vec2 texture_size = vec2(textureSize(sCacheRGBA8, 0));

View File

@ -36,6 +36,7 @@ void brush_vs(
int prim_address,
RectWithSize local_rect,
ivec3 user_data,
mat4 transform,
PictureTask pic_task
) {
RadialGradient gradient = fetch_radial_gradient(prim_address);

View File

@ -28,6 +28,7 @@ void brush_vs(
int prim_address,
RectWithSize local_rect,
ivec3 user_data,
mat4 transform,
PictureTask pic_task
) {
SolidBrush prim = fetch_solid_primitive(prim_address);

View File

@ -75,6 +75,7 @@ void brush_vs(
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;

View File

@ -58,7 +58,7 @@ float rounded_rect(vec2 pos,
// 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,

View File

@ -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

View File

@ -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

View File

@ -489,7 +489,7 @@ vec4 get_node_pos(vec2 pos, ClipScrollNode node) {
// 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,
@ -500,8 +500,8 @@ vec2 compute_snap_offset(vec2 local_pos,
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);
@ -535,7 +535,11 @@ VertexInfo write_vertex(RectWithSize instance_rect,
vec2 clamped_local_pos = clamp_rect(local_pos, local_clip_rect);
/// Compute the snapping offset.
vec2 snap_offset = compute_snap_offset(clamped_local_pos, 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);

View File

@ -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

View File

@ -50,7 +50,11 @@ VertexInfo write_text_vertex(vec2 clamped_local_pos,
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);

View File

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{AlphaType, 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};
@ -72,7 +72,6 @@ pub enum BrushBatchKind {
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum BatchKind {
HardwareComposite,
SplitComposite,
Transformable(TransformedRectKind, TransformBatchKind),
Brush(BrushBatchKind),
@ -639,52 +638,46 @@ impl AlphaBatchBuilder {
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.
debug_assert!(picture.surface.is_some());
// If this picture is participating in a 3D rendering context,
// then don't add it to any batches here. Instead, create a polygon
// for it and add it to the current plane splitter.
if picture.is_in_3d_context {
// Push into parent plane splitter.
let real_xf = &ctx.clip_scroll_tree
.nodes[picture.reference_frame_index.0]
.world_content_transform
.into();
let polygon = make_polygon(
picture.real_local_rect,
&real_xf,
prim_index.0,
);
let 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);
splitter.add(polygon);
return;
}
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]
@ -703,84 +696,81 @@ impl AlphaBatchBuilder {
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);
None => {
true
}
}
}
FilterOp::DropShadow(..) => {
if let Some(cache_task_id) = picture.surface {
let kind = BatchKind::Brush(
BrushBatchKind::Image(ImageBufferKind::Texture2DArray),
);
let uv_rect_address = render_tasks[cache_task_id]
.get_texture_handle()
.as_int(gpu_cache);
let instance = BrushInstance {
picture_address: task_address,
prim_address: prim_cache_address,
clip_chain_rect_index,
scroll_id,
clip_task_address,
z,
segment_index: 0,
edge_flags: EdgeAaSegmentMask::empty(),
brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
user_data: [
uv_rect_address,
BrushImageSourceKind::ColorAlphaMask as i32,
// TODO(gw): This is totally wrong, but the drop-shadow code itself
// is completely wrong, and doesn't work correctly with
// transformed Picture sources. I'm leaving this as is for
// now, and will fix drop-shadows properly, as a follow up.
RasterizationSpace::Local as i32,
],
};
{
let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
batch.push(PrimitiveInstance::from(instance));
let (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 instance = CompositePrimitiveInstance::new(
task_address,
secondary_task_address,
RenderTaskAddress(0),
rect.origin.x,
rect.origin.y,
z,
rect.size.width,
rect.size.height,
);
let uv_rect_address = render_tasks[task_id]
.get_texture_handle()
.as_int(gpu_cache);
batch.push(PrimitiveInstance::from(instance));
}
_ => {
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),
],
};
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,
@ -824,6 +814,8 @@ impl AlphaBatchBuilder {
}
};
let cache_task_address = render_tasks.get_task_address(cache_task_id);
let instance = BrushInstance {
picture_address: task_address,
prim_address: prim_cache_address,
@ -843,90 +835,107 @@ impl AlphaBatchBuilder {
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));
}
}
}
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,
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 => {
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,
);
}
}
_ => {
@ -1228,7 +1237,7 @@ impl AlphaBatchBuilder {
impl BrushPrimitive {
pub fn get_picture_index(&self) -> PictureIndex {
match self.kind {
BrushKind::Picture { pic_index } => {
BrushKind::Picture { pic_index, .. } => {
pic_index
}
_ => {
@ -1263,8 +1272,9 @@ impl BrushPrimitive {
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,
],
))
}

View File

@ -166,7 +166,7 @@ impl NormalBorderHelpers for NormalBorder {
}
}
// 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) |
@ -445,7 +445,7 @@ impl<'a> DisplayListFlattener<'a> {
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
);

View File

@ -5,7 +5,7 @@
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;
@ -123,10 +123,8 @@ pub struct ExternalCaptureImage {
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,
}

View File

@ -3,9 +3,11 @@
* 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};
@ -19,9 +21,9 @@ 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))]
@ -60,6 +62,7 @@ const DEFAULT_TEXTURE: TextureSlot = TextureSlot(0);
#[repr(u32)]
pub enum DepthFunction {
#[cfg(feature = "debug_renderer")]
Less = gl::LESS,
LessEqual = gl::LEQUAL,
}
@ -76,6 +79,7 @@ pub enum TextureFilter {
#[derive(Debug)]
pub enum VertexAttributeKind {
F32,
#[cfg(feature = "debug_renderer")]
U8Norm,
U16Norm,
I32,
@ -109,6 +113,11 @@ pub enum UploadMethod {
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),
@ -230,6 +239,7 @@ 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,
@ -265,6 +275,7 @@ impl VertexAttribute {
offset,
);
}
#[cfg(feature = "debug_renderer")]
VertexAttributeKind::U8Norm => {
gl.vertex_attrib_pointer(
attr_index,
@ -458,10 +469,12 @@ impl Texture {
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()
}
@ -636,13 +649,14 @@ 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
}
@ -661,7 +675,8 @@ pub struct Device {
device_pixel_ratio: f32,
upload_method: UploadMethod,
// HW or API capabilties
// HW or API capabilities
#[cfg(feature = "debug_renderer")]
capabilities: Capabilities,
// debug
@ -708,6 +723,7 @@ impl Device {
upload_method,
inside_frame: false,
#[cfg(feature = "debug_renderer")]
capabilities: Capabilities {
supports_multisampling: false, //TODO
},
@ -749,6 +765,7 @@ impl Device {
self.max_texture_size
}
#[cfg(feature = "debug_renderer")]
pub fn get_capabilities(&self) -> &Capabilities {
&self.capabilities
}
@ -786,7 +803,7 @@ impl Device {
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);
@ -995,7 +1012,7 @@ impl Device {
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) {
@ -1008,7 +1025,7 @@ impl Device {
self.bind_read_target(None);
}
pub fn init_texture(
pub fn init_texture<T: Texel>(
&mut self,
texture: &mut Texture,
mut width: u32,
@ -1016,7 +1033,7 @@ impl Device {
filter: TextureFilter,
render_target: Option<RenderTargetInfo>,
layer_count: i32,
pixels: Option<&[u8]>,
pixels: Option<&[T]>,
) {
debug_assert!(self.inside_frame);
@ -1049,12 +1066,12 @@ impl Device {
}
/// 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);
@ -1075,7 +1092,7 @@ impl Device {
0,
desc.external,
desc.pixel_type,
pixels,
pixels.map(texels_to_u8_slice),
)
}
_ => {
@ -1089,7 +1106,7 @@ impl Device {
0,
desc.external,
desc.pixel_type,
pixels,
pixels.map(texels_to_u8_slice),
)
}
}
@ -1172,7 +1189,7 @@ impl Device {
}
}
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 => {
@ -1186,7 +1203,7 @@ impl Device {
0,
desc.external,
desc.pixel_type,
pixels,
pixels.map(texels_to_u8_slice),
);
}
gl::TEXTURE_2D | gl::TEXTURE_RECTANGLE | gl::TEXTURE_EXTERNAL_OES => {
@ -1199,7 +1216,7 @@ impl Device {
0,
desc.external,
desc.pixel_type,
pixels,
pixels.map(texels_to_u8_slice),
);
}
_ => panic!("BUG: Unexpected texture target!"),
@ -1288,6 +1305,13 @@ impl Device {
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;
}
@ -1451,6 +1475,7 @@ impl Device {
}
}
#[cfg(feature = "debug_renderer")]
pub fn get_uniform_location(&self, program: &Program, name: &str) -> UniformLocation {
UniformLocation(self.gl.get_uniform_location(program.id, name))
}
@ -1518,6 +1543,7 @@ impl Device {
}
}
#[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(
@ -1564,6 +1590,7 @@ impl Device {
}
/// Get texels of a texture into the specified output slice.
#[cfg(feature = "debug_renderer")]
pub fn get_tex_image_into(
&mut self,
texture: &Texture,
@ -1582,6 +1609,7 @@ impl Device {
}
/// 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
) {
@ -1608,12 +1636,14 @@ impl Device {
}
}
#[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)
}
@ -1849,6 +1879,7 @@ impl Device {
);
}
#[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(
@ -1864,6 +1895,7 @@ impl Device {
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);
@ -2014,6 +2046,7 @@ impl Device {
.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);
@ -2021,9 +2054,11 @@ impl Device {
}
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);
@ -2046,6 +2081,7 @@ impl Device {
}
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 {
@ -2260,3 +2296,9 @@ impl<'a> UploadTarget<'a> {
}
}
}
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>())
}
}

View File

@ -14,6 +14,7 @@ use api::{RepeatMode, ScrollFrameDisplayItem, ScrollPolicy, ScrollSensitivity, S
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};
@ -1055,7 +1056,11 @@ impl<'a> DisplayListFlattener<'a> {
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(),
@ -1108,8 +1113,34 @@ impl<'a> DisplayListFlattener<'a> {
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,
@ -1121,6 +1152,14 @@ impl<'a> DisplayListFlattener<'a> {
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,
@ -1140,7 +1179,11 @@ impl<'a> DisplayListFlattener<'a> {
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(),
@ -1195,7 +1238,11 @@ impl<'a> DisplayListFlattener<'a> {
);
// 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(
@ -1410,6 +1457,12 @@ impl<'a> DisplayListFlattener<'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
@ -1420,11 +1473,15 @@ impl<'a> DisplayListFlattener<'a> {
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,

View File

@ -4,7 +4,7 @@
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};
@ -16,13 +16,13 @@ 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)]
@ -65,6 +65,7 @@ pub struct FrameBuildingState<'a> {
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> {
@ -157,6 +158,7 @@ impl FrameBuilder {
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,
@ -194,6 +196,7 @@ impl FrameBuilder {
local_clip_rects,
resource_cache,
gpu_cache,
special_render_passes,
cached_gradients: &mut self.cached_gradients,
};
@ -223,10 +226,7 @@ impl FrameBuilder {
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,
);
@ -313,12 +313,16 @@ impl FrameBuilder {
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,
@ -326,8 +330,14 @@ impl FrameBuilder {
&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;
@ -337,14 +347,14 @@ impl FrameBuilder {
// 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..],
);
}
@ -354,7 +364,7 @@ impl FrameBuilder {
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,
@ -365,7 +375,7 @@ impl FrameBuilder {
};
pass.build(
&ctx,
&mut ctx,
gpu_cache,
&mut render_tasks,
&mut deferred_resolves,

View File

@ -2,12 +2,31 @@
* 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 {
@ -24,9 +43,48 @@ pub enum GlyphCacheEntry {
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 {
@ -86,7 +144,9 @@ impl GlyphCache {
// 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() {
@ -94,13 +154,7 @@ impl GlyphCache {
// 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
});
@ -112,7 +166,9 @@ impl GlyphCache {
});
}
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);
}
}

View File

@ -7,26 +7,75 @@ 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))]
@ -79,10 +128,12 @@ impl FontTransform {
)
}
#[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 {
@ -94,6 +145,7 @@ impl FontTransform {
}
}
#[cfg(not(feature = "pathfinder"))]
pub fn pre_scale(&self, scale_x: f32, scale_y: f32) -> Self {
FontTransform::new(
self.scale_x * scale_x,
@ -103,6 +155,7 @@ impl FontTransform {
)
}
#[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)
}
@ -254,19 +307,27 @@ pub struct RasterizedGlyph {
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)
@ -289,17 +350,24 @@ impl FontContexts {
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>,
@ -309,10 +377,13 @@ pub struct GlyphRasterizer {
// 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:
@ -320,6 +391,9 @@ pub struct GlyphRasterizer {
// - 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 {
@ -335,10 +409,13 @@ impl GlyphRasterizer {
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,
@ -346,6 +423,7 @@ impl GlyphRasterizer {
glyph_tx,
workers,
fonts_to_remove: Vec::new(),
next_gpu_glyph_cache_key: GpuGlyphCacheKey(0),
})
}
@ -368,8 +446,19 @@ impl GlyphRasterizer {
.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);
}
@ -378,6 +467,65 @@ impl GlyphRasterizer {
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,
@ -385,6 +533,95 @@ impl GlyphRasterizer {
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
@ -408,21 +645,21 @@ impl GlyphRasterizer {
}
}
// 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() {
@ -430,13 +667,20 @@ impl GlyphRasterizer {
}
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");
@ -447,7 +691,7 @@ impl GlyphRasterizer {
};
// 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(),
@ -479,14 +723,30 @@ impl GlyphRasterizer {
.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;
@ -500,7 +760,7 @@ impl GlyphRasterizer {
// 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));
@ -508,8 +768,13 @@ impl GlyphRasterizer {
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);
@ -535,31 +800,35 @@ impl GlyphRasterizer {
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 || {
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 {
context.delete_font(font_key);
}
}
});
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 {
context.delete_font(font_key);
}
}
});
}
#[cfg(feature = "replay")]
@ -570,7 +839,11 @@ impl GlyphRasterizer {
}
}
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) => {
@ -583,6 +856,20 @@ impl FontContext {
}
}
#[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))]
@ -600,11 +887,41 @@ impl GlyphRequest {
}
}
#[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>,
@ -630,6 +947,9 @@ fn rasterize_200_glyphs() {
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");
@ -670,6 +990,9 @@ fn rasterize_200_glyphs() {
&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,
);
}
@ -679,6 +1002,68 @@ fn rasterize_200_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))
}

View File

@ -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 = &current_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((&current_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,
}
}

View File

@ -195,7 +195,7 @@ bitflags! {
}
}
// 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

View File

@ -57,6 +57,7 @@ pub struct HitTestingItem {
rect: LayerRect,
clip_rect: LayerRect,
tag: ItemTag,
is_backface_visible: bool,
}
impl HitTestingItem {
@ -65,6 +66,7 @@ impl HitTestingItem {
rect: info.rect,
clip_rect: info.clip_rect,
tag: tag,
is_backface_visible: info.is_backface_visible,
}
}
}
@ -232,6 +234,7 @@ impl HitTester {
}
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,
@ -265,6 +268,13 @@ impl HitTester {
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,

View File

@ -48,6 +48,8 @@ extern crate lazy_static;
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;
@ -61,7 +63,9 @@ 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;
@ -76,6 +80,8 @@ 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;
@ -148,6 +154,14 @@ 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")]

View File

@ -2,12 +2,12 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{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};
@ -159,10 +159,9 @@ impl PicturePrimitive {
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
@ -179,20 +178,26 @@ impl PicturePrimitive {
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;
@ -214,10 +219,7 @@ impl PicturePrimitive {
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,
);
@ -235,25 +237,35 @@ impl PicturePrimitive {
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(
@ -270,16 +282,13 @@ impl PicturePrimitive {
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,
);
@ -294,7 +303,7 @@ impl PicturePrimitive {
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
@ -305,41 +314,43 @@ impl PicturePrimitive {
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);
}
prim_screen_rect.clipped
device_rect
}
}
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,
);
@ -347,38 +358,46 @@ impl PicturePrimitive {
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);
}
}

View File

@ -11,18 +11,25 @@ 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;
@ -30,6 +37,7 @@ 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,
}
@ -39,6 +47,7 @@ unsafe impl Send for FontContext {}
struct GlyphMetrics {
rasterized_left: i32,
#[allow(dead_code)]
rasterized_descent: i32,
rasterized_ascent: i32,
rasterized_width: u32,
@ -411,6 +420,7 @@ impl FontContext {
}
// Assumes the pixels here are linear values from CG
#[cfg(not(feature = "pathfinder"))]
fn gamma_correct_pixels(
&self,
pixels: &mut Vec<u8>,
@ -478,16 +488,13 @@ impl FontContext {
}
}
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);
@ -533,7 +540,7 @@ impl FontContext {
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
@ -713,7 +720,7 @@ impl FontContext {
}
}
Some(RasterizedGlyph {
GlyphRasterResult::Bitmap(RasterizedGlyph {
left: metrics.rasterized_left as f32,
top: metrics.rasterized_ascent as f32,
width: metrics.rasterized_width,

View File

@ -17,7 +17,8 @@ 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;
@ -152,7 +153,7 @@ impl FontContext {
FT_Init_FreeType(&mut lib)
};
if result.succeeded() {
if succeeded(result) {
Ok(FontContext {
lib,
faces: FastHashMap::default(),
@ -161,7 +162,7 @@ impl FontContext {
} else {
// TODO(gw): Provide detailed error values.
Err(ResourceCacheError::new(
format!("Failed to initialize FreeType - {}", result.0)
format!("Failed to initialize FreeType - {}", result)
))
}
}
@ -182,7 +183,7 @@ impl FontContext {
&mut face,
)
};
if result.succeeded() && !face.is_null() {
if succeeded(result) && !face.is_null() {
self.faces.insert(
*font_key,
Face {
@ -209,7 +210,7 @@ impl FontContext {
&mut face,
)
};
if result.succeeded() && !face.is_null() {
if succeeded(result) && !face.is_null() {
self.faces.insert(
*font_key,
Face {
@ -227,7 +228,7 @@ impl FontContext {
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));
}
}
@ -316,11 +317,11 @@ impl FontContext {
}
};
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());
@ -567,7 +568,7 @@ impl FontContext {
(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 {:?}, {:?}",
@ -581,26 +582,23 @@ impl FontContext {
}
}
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 };
@ -612,13 +610,13 @@ impl FontContext {
}
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;
}
};
@ -771,7 +769,7 @@ impl FontContext {
_ => font.get_alpha_glyph_format(),
};
Some(RasterizedGlyph {
GlyphRasterResult::Bitmap(RasterizedGlyph {
left: left as f32,
top: top as f32,
width: actual_width as u32,

View File

@ -6,7 +6,8 @@ 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;
@ -361,11 +362,8 @@ impl FontContext {
}
}
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);
@ -409,7 +407,7 @@ impl FontContext {
// 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);
@ -427,7 +425,7 @@ impl FontContext {
};
lut_correction.preblend(&mut bgra_pixels, font.color);
Some(RasterizedGlyph {
GlyphRasterResult::Bitmap(RasterizedGlyph {
left: bounds.left as f32,
top: -bounds.top as f32,
width,

View File

@ -7,6 +7,7 @@ use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, Epoch, ExtendMode, Fon
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};
@ -20,10 +21,10 @@ use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuData
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;
@ -201,6 +202,12 @@ pub enum BrushKind {
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,
@ -237,12 +244,15 @@ 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,
}
}
@ -317,10 +327,16 @@ impl BrushPrimitive {
}
}
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,
}
@ -329,19 +345,12 @@ impl BrushPrimitive {
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());
}
@ -650,11 +659,10 @@ impl TextRunPrimitiveCpu {
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);
@ -693,7 +701,12 @@ impl TextRunPrimitiveCpu {
}
}
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) {
@ -1168,11 +1181,10 @@ impl PrimitiveStore {
// 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 => {
@ -1233,6 +1245,7 @@ impl PrimitiveStore {
},
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.
@ -1347,16 +1360,42 @@ impl PrimitiveStore {
);
}
}
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 => {}
@ -1385,7 +1424,7 @@ impl PrimitiveStore {
}
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 {
@ -1797,7 +1836,7 @@ impl PrimitiveStore {
// 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];
@ -1852,7 +1891,11 @@ impl PrimitiveStore {
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);
}
}

View File

@ -2,21 +2,33 @@
* 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;
@ -25,7 +37,7 @@ pub struct GpuProfileTag {
pub label: &'static str,
pub color: ColorF,
}
impl NamedTag for GpuProfileTag {
fn get_label(&self) -> &str {
self.label
@ -85,11 +97,13 @@ impl ProfileCounter for IntProfileCounter {
}
}
#[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
@ -489,12 +503,14 @@ struct GraphStats {
}
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,
@ -506,6 +522,7 @@ impl ProfileGraph {
}
}
#[cfg(feature = "debug_renderer")]
fn push(&mut self, ns: u64) {
let ms = ns as f64 / 1000000.0;
if self.values.len() == self.max_samples {
@ -534,6 +551,7 @@ impl ProfileGraph {
stats
}
#[cfg(feature = "debug_renderer")]
fn draw_graph(
&self,
x: f32,
@ -633,15 +651,18 @@ impl ProfileCounter for ProfileGraph {
}
}
#[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 {
@ -660,6 +681,7 @@ impl GpuFrameCollection {
}
}
#[cfg(feature = "debug_renderer")]
impl GpuFrameCollection {
fn draw(&self, x: f32, y: f32, debug_renderer: &mut DebugRenderer) -> Rect<f32> {
let graph_rect = Rect::new(
@ -750,6 +772,7 @@ impl GpuFrameCollection {
}
}
#[cfg(feature = "debug_renderer")]
struct DrawState {
x_left: f32,
y_left: f32,
@ -757,6 +780,7 @@ struct DrawState {
y_right: f32,
}
#[cfg(feature = "debug_renderer")]
pub struct Profiler {
draw_state: DrawState,
backend_time: ProfileGraph,
@ -766,7 +790,9 @@ pub struct Profiler {
ipc_time: ProfileGraph,
}
#[cfg(feature = "debug_renderer")]
impl Profiler {
pub fn new() -> Self {
Profiler {
draw_state: DrawState {

View File

@ -3,6 +3,7 @@
* 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;
@ -54,6 +55,7 @@ impl<T> QuerySet<T> {
})
}
#[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()) {
@ -157,6 +159,7 @@ impl<T: NamedTag> GpuFrameProfile<T> {
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;
@ -233,6 +236,7 @@ impl<T> GpuProfiler<T> {
}
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()
}

View File

@ -2,15 +2,21 @@
* 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};
@ -20,6 +26,8 @@ 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;
@ -76,7 +84,7 @@ impl RenderTaskTree {
&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];
@ -173,9 +181,7 @@ pub struct ClipRegionTask {
#[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,
}
@ -196,6 +202,25 @@ impl BlurTask {
}
}
#[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))]
@ -232,6 +257,8 @@ pub enum RenderTaskKind {
ClipRegion(ClipRegionTask),
VerticalBlur(BlurTask),
HorizontalBlur(BlurTask),
#[allow(dead_code)]
Glyph(GlyphTask),
Readback(DeviceIntRect),
Scaling(RenderTargetKind),
Blit(BlitTask),
@ -264,10 +291,7 @@ 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 {
@ -275,12 +299,10 @@ impl RenderTask {
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,
}
}
@ -362,6 +384,7 @@ impl RenderTask {
},
gpu_cache,
render_tasks,
None,
|render_tasks| {
// Draw the rounded rect.
let mask_task = RenderTask::new_rounded_rect_mask(
@ -517,6 +540,29 @@ impl RenderTask {
}
}
#[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.
@ -560,6 +606,9 @@ impl RenderTask {
0.0,
]
}
RenderTaskKind::Glyph(_) => {
[1.0, 0.0, 0.0]
}
RenderTaskKind::Readback(..) |
RenderTaskKind::Scaling(..) |
RenderTaskKind::Blit(..) => {
@ -596,7 +645,8 @@ impl RenderTask {
RenderTaskKind::Readback(..) |
RenderTaskKind::Scaling(..) |
RenderTaskKind::Blit(..) |
RenderTaskKind::CacheMask(..) => {
RenderTaskKind::CacheMask(..) |
RenderTaskKind::Glyph(..) => {
panic!("texture handle not supported for this task kind");
}
}
@ -655,12 +705,16 @@ impl RenderTask {
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(..) => {
@ -683,7 +737,8 @@ impl RenderTask {
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
@ -712,7 +767,8 @@ impl RenderTask {
RenderTaskKind::Scaling(..) |
RenderTaskKind::Blit(..) |
RenderTaskKind::ClipRegion(..) |
RenderTaskKind::CacheMask(..) => {
RenderTaskKind::CacheMask(..) |
RenderTaskKind::Glyph(..) => {
return;
}
};
@ -733,7 +789,6 @@ impl RenderTask {
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()));
@ -762,6 +817,9 @@ impl RenderTask {
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));
@ -790,15 +848,17 @@ impl RenderTask {
}
}
#[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 {
@ -859,8 +919,10 @@ impl RenderTaskCache {
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
@ -869,11 +931,11 @@ impl RenderTaskCache {
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.
@ -909,7 +971,7 @@ impl RenderTaskCache {
descriptor,
TextureFilter::Linear,
None,
[0.0; 3],
user_data.unwrap_or([0.0; 3]),
None,
gpu_cache,
None,
@ -931,8 +993,27 @@ impl RenderTaskCache {
// 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

View File

@ -9,24 +9,17 @@
//!
//! [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,
@ -37,15 +30,17 @@ 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;
@ -54,8 +49,6 @@ 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;
@ -72,8 +65,28 @@ 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
@ -128,10 +141,6 @@ 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,
@ -200,7 +209,6 @@ impl BatchKind {
#[cfg(feature = "debugger")]
fn debug_name(&self) -> &'static str {
match *self {
BatchKind::HardwareComposite => "HardwareComposite",
BatchKind::SplitComposite => "SplitComposite",
BatchKind::Brush(kind) => {
match kind {
@ -219,7 +227,6 @@ impl BatchKind {
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 {
@ -305,7 +312,7 @@ pub(crate) enum TextureSampler {
}
impl TextureSampler {
fn color(n: usize) -> TextureSampler {
pub(crate) fn color(n: usize) -> TextureSampler {
match n {
0 => TextureSampler::Color0,
1 => TextureSampler::Color1,
@ -440,6 +447,90 @@ pub(crate) mod desc {
],
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)]
@ -447,6 +538,8 @@ pub(crate) enum VertexArrayKind {
Primitive,
Blur,
Clip,
VectorStencil,
VectorCover,
}
#[derive(Clone, Debug, PartialEq)]
@ -496,6 +589,7 @@ pub struct GpuProfile {
}
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 {
@ -532,6 +626,19 @@ impl CpuProfile {
}
}
#[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>,
@ -576,7 +683,7 @@ 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,
@ -860,7 +967,7 @@ impl CacheTexture {
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,
@ -894,7 +1001,7 @@ impl CacheTexture {
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,
@ -1076,7 +1183,7 @@ impl VertexDataTexture {
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,
@ -1126,13 +1233,36 @@ struct TargetSelector {
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>,
@ -1140,19 +1270,23 @@ pub struct Renderer {
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,
@ -1187,7 +1321,7 @@ pub struct Renderer {
// 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().
@ -1385,8 +1519,6 @@ impl Renderer {
None
};
let debug_renderer = DebugRenderer::new(&mut device);
let x0 = 0.0;
let y0 = 0.0;
let x1 = 1.0;
@ -1405,9 +1537,12 @@ impl Renderer {
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);
@ -1538,10 +1673,12 @@ impl Renderer {
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,
@ -1549,6 +1686,7 @@ impl Renderer {
enable_clear_scissor: options.enable_clear_scissor,
last_time: 0,
gpu_profile,
gpu_glyph_renderer,
prim_vao,
blur_vao,
clip_vao,
@ -2023,6 +2161,7 @@ impl Renderer {
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.
@ -2137,20 +2276,23 @@ impl Renderer {
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),
);
}
}
}
@ -2161,7 +2303,11 @@ impl Renderer {
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;
@ -2185,7 +2331,7 @@ impl Renderer {
// 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,
@ -2206,7 +2352,7 @@ impl Renderer {
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(
@ -2272,7 +2418,7 @@ impl Renderer {
// 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,
@ -2324,7 +2470,9 @@ impl Renderer {
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);
}
@ -2339,7 +2487,7 @@ impl Renderer {
}
}
fn draw_instanced_batch<T>(
pub(crate) fn draw_instanced_batch<T>(
&mut self,
data: &[T],
vertex_array_kind: VertexArrayKind,
@ -2359,11 +2507,20 @@ impl Renderer {
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);
@ -3132,28 +3289,42 @@ impl Renderer {
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);
@ -3171,8 +3342,29 @@ impl Renderer {
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
@ -3307,7 +3499,7 @@ impl Renderer {
}
};
self.device.init_texture(
self.device.init_texture::<u8>(
&mut texture,
list.max_size.width,
list.max_size.height,
@ -3509,6 +3701,8 @@ impl Renderer {
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.
@ -3524,8 +3718,9 @@ impl Renderer {
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 {
@ -3656,19 +3851,22 @@ impl Renderer {
}
}
#[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),
@ -3677,7 +3875,7 @@ impl Renderer {
}
let margin = 10.0;
self.debug.add_quad(
debug_renderer.add_quad(
&x0 - margin,
y0 - margin,
x0 + text_width + margin,
@ -3729,7 +3927,12 @@ impl Renderer {
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);
}
@ -4103,8 +4306,7 @@ impl Renderer {
}
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);
@ -4178,9 +4380,9 @@ impl Renderer {
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") {
@ -4266,3 +4468,36 @@ impl Renderer {
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!(),
}
}

View File

@ -19,7 +19,9 @@ 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};
@ -35,7 +37,7 @@ use std::mem;
use std::path::PathBuf;
use std::sync::{Arc, RwLock};
use texture_cache::{TextureCache, TextureCacheHandle};
use tiling::SpecialRenderPasses;
const DEFAULT_TILE_SIZE: TileSize = 512;
@ -320,15 +322,17 @@ impl ResourceCache {
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(
@ -646,6 +650,8 @@ impl ResourceCache {
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);
@ -656,6 +662,9 @@ impl ResourceCache {
glyph_keys,
&mut self.texture_cache,
gpu_cache,
&mut self.cached_render_tasks,
render_task_tree,
render_passes,
);
}
@ -663,12 +672,63 @@ impl ResourceCache {
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]),
@ -683,22 +743,25 @@ impl ResourceCache {
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() {
@ -795,7 +858,7 @@ impl ResourceCache {
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;
}
@ -803,6 +866,7 @@ impl ResourceCache {
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");
@ -814,6 +878,8 @@ impl ResourceCache {
&mut self.cached_glyphs,
&mut self.texture_cache,
gpu_cache,
&mut self.cached_render_tasks,
render_tasks,
texture_cache_profile,
);
@ -1322,11 +1388,7 @@ impl ResourceCache {
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)
}

View File

@ -36,7 +36,7 @@ pub enum SceneBuilderResult {
},
}
/// 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,

View File

@ -23,7 +23,7 @@ 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 => "",
@ -54,12 +54,16 @@ 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 {
@ -70,7 +74,7 @@ pub struct LazilyCompiledShader {
}
impl LazilyCompiledShader {
fn new(
pub(crate) fn new(
kind: ShaderKind,
name: &'static str,
features: &[&'static str],
@ -134,6 +138,18 @@ impl LazilyCompiledShader {
&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)
}
@ -354,6 +370,8 @@ fn create_prim_shader(
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);
@ -447,7 +465,6 @@ pub struct Shaders {
ps_border_corner: PrimitiveShader,
ps_border_edge: PrimitiveShader,
ps_hw_composite: LazilyCompiledShader,
ps_split_composite: LazilyCompiledShader,
}
@ -657,14 +674,6 @@ impl Shaders {
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",
@ -693,7 +702,6 @@ impl Shaders {
ps_image,
ps_border_corner,
ps_border_edge,
ps_hw_composite,
ps_split_composite,
})
}
@ -709,9 +717,6 @@ impl Shaders {
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
}
@ -801,7 +806,6 @@ impl Shaders {
}
self.ps_border_corner.deinit(device);
self.ps_border_edge.deinit(device);
self.ps_hw_composite.deinit(device);
self.ps_split_composite.deinit(device);
}
}

View File

@ -104,7 +104,7 @@ fn next(cur: f32, prev: f32, dest: f32, stiffness: f32, damping: f32) -> f32 {
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
}

View File

@ -172,7 +172,7 @@ type WeakCacheEntryHandle = WeakFreeListHandle<CacheEntry>;
// 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>,
@ -497,7 +497,7 @@ impl TextureCache {
// 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 {

View File

@ -9,18 +9,24 @@ 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;
@ -36,10 +42,10 @@ pub struct ScrollbarPrimitive {
#[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],
@ -94,7 +100,7 @@ pub trait RenderTarget {
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>,
@ -156,7 +162,7 @@ impl<T: RenderTarget> RenderTargetList<T> {
fn build(
&mut self,
ctx: &RenderTargetContext,
ctx: &mut RenderTargetContext,
gpu_cache: &mut GpuCache,
render_tasks: &mut RenderTaskTree,
deferred_resolves: &mut Vec<DeferredResolve>,
@ -261,6 +267,23 @@ pub struct BlitJob {
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))]
@ -307,7 +330,7 @@ impl RenderTarget for ColorRenderTarget {
fn build(
&mut self,
ctx: &RenderTargetContext,
ctx: &mut RenderTargetContext,
gpu_cache: &mut GpuCache,
render_tasks: &mut RenderTaskTree,
deferred_resolves: &mut Vec<DeferredResolve>,
@ -323,7 +346,7 @@ impl RenderTarget for ColorRenderTarget {
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();
@ -372,19 +395,17 @@ impl RenderTarget for ColorRenderTarget {
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) => {
@ -415,6 +436,10 @@ impl RenderTarget for ColorRenderTarget {
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);
}
@ -539,25 +564,24 @@ impl RenderTarget for AlphaRenderTarget {
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) => {
@ -601,6 +625,7 @@ impl RenderTarget for AlphaRenderTarget {
pub struct TextureCacheRenderTarget {
pub horizontal_blurs: Vec<BlurInstance>,
pub blits: Vec<BlitJob>,
pub glyphs: Vec<GlyphJob>,
}
impl TextureCacheRenderTarget {
@ -609,26 +634,32 @@ impl TextureCacheRenderTarget {
_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) => {
@ -641,14 +672,16 @@ impl TextureCacheRenderTarget {
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(..) |
@ -659,6 +692,21 @@ impl TextureCacheRenderTarget {
}
}
}
#[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))]
@ -724,7 +772,7 @@ impl RenderPass {
pub fn build(
&mut self,
ctx: &RenderTargetContext,
ctx: &mut RenderTargetContext,
gpu_cache: &mut GpuCache,
render_tasks: &mut RenderTaskTree,
deferred_resolves: &mut Vec<DeferredResolve>,
@ -927,17 +975,30 @@ 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),
}
}
}

View File

@ -58,10 +58,6 @@ const SHADERS: &[Shader] = &[
name: "ps_border_edge",
features: PRIM_FEATURES,
},
Shader {
name: "ps_hardware_composite",
features: PRIM_FEATURES,
},
Shader {
name: "ps_split_composite",
features: PRIM_FEATURES,
@ -80,19 +76,19 @@ const SHADERS: &[Shader] = &[
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 {

View File

@ -1,6 +1,6 @@
[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"
@ -20,6 +20,7 @@ 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]

View File

@ -2,6 +2,8 @@
* 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;
@ -405,7 +407,11 @@ pub struct UpdateImage {
#[derive(Clone, Deserialize, Serialize)]
pub enum AddFont {
Raw(FontKey, Vec<u8>, u32),
Raw(
FontKey,
#[serde(with = "serde_bytes")] Vec<u8>,
u32
),
Native(FontKey, NativeFontHandle),
}
@ -691,7 +697,7 @@ 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 {
@ -740,7 +746,7 @@ impl RenderApiSender {
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)),
}
}

View File

@ -55,12 +55,12 @@ impl<T> MsgSender<T> {
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 }))
}
///

View File

@ -627,7 +627,7 @@ 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,
}
}

View File

@ -195,7 +195,7 @@ impl<'a> BuiltDisplayListIter<'a> {
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,
@ -236,7 +236,7 @@ impl<'a> BuiltDisplayListIter<'a> {
self.cur_clip_chain_items = ItemRange::default();
loop {
if self.data.len() == 0 {
if self.data.is_empty() {
return None;
}
@ -334,8 +334,8 @@ impl<'a, 'b> DisplayItemRef<'a, 'b> {
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,
}
@ -389,7 +389,7 @@ impl<'a, 'b> DisplayItemRef<'a, 'b> {
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?")
@ -742,7 +742,7 @@ impl<'a, 'b> 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 }
}
}
@ -875,7 +875,7 @@ impl DisplayListBuilder {
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");
}
@ -1176,8 +1176,8 @@ impl DisplayListBuilder {
RadialGradient {
center,
radius,
start_offset: start_offset,
end_offset: end_offset,
start_offset,
end_offset,
extend_mode,
}
}
@ -1223,7 +1223,7 @@ impl DisplayListBuilder {
/// `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.
///
@ -1425,7 +1425,7 @@ impl DisplayListBuilder {
let id = self.generate_clip_id();
let item = SpecificDisplayItem::Clip(ClipDisplayItem {
id,
image_mask: image_mask,
image_mask,
});
let info = LayoutPrimitiveInfo::new(clip_rect);
@ -1473,7 +1473,7 @@ impl DisplayListBuilder {
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) {

View File

@ -103,7 +103,7 @@ pub enum SubpixelDirection {
}
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

View File

@ -2,6 +2,8 @@
* 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};
@ -110,11 +112,26 @@ impl ImageDescriptor {
#[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))
@ -130,8 +147,8 @@ impl ImageData {
#[inline]
pub fn is_blob(&self) -> bool {
match self {
&ImageData::Blob(_) => true,
match *self {
ImageData::Blob(_) => true,
_ => false,
}
}

View File

@ -14,7 +14,7 @@ log = "0.4"
[dependencies.webrender]
path = "../webrender"
version = "0.57.0"
version = "0.57.2"
default-features = false
features = ["capture"]

View File

@ -1 +1 @@
22493328352ba432a7cd89491d81bfaa19bc1bce
941bf5ac998061689a1bcd18d417f1f315e41ae6

View File

@ -12,7 +12,7 @@ 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"] }

View File

@ -110,7 +110,7 @@ subcommands:
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)

View File

@ -9,7 +9,7 @@ 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]
@ -55,7 +55,7 @@ fn render_blob(
} 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 {
@ -70,7 +70,7 @@ fn render_blob(
}
_ => {
return Err(BlobImageError::Other(
format!("Usupported image format {:?}", descriptor.format),
format!("Unsupported image format {:?}", descriptor.format),
));
}
}