Bug 1470125 - Update webrender to commit cdfaaeb5f74e416f39af1081c9a676c752d23896. r=Gankro

Includes regenerated webrender_ffi_generated.h header.

MozReview-Commit-ID: I3lvkCH5IGz

--HG--
extra : rebase_source : 654102de6773960af67069e7e4c2b27474d9a04c
This commit is contained in:
Kartikaya Gupta 2018-06-28 11:48:27 -04:00
parent c83eb1f34f
commit 7bcd1495a9
47 changed files with 1481 additions and 1257 deletions

View File

@ -27,12 +27,12 @@ cfg-if = "0.1.2"
euclid = "0.17.3"
fxhash = "0.2.1"
gleam = "0.5"
image = { optional = true, version = "0.18" }
image = { optional = true, version = "0.19" }
lazy_static = "1"
log = "0.4"
num-traits = "0.1.43"
plane-split = "0.9.1"
png = { optional = true, version = "0.11" }
png = { optional = true, version = "0.12" }
rayon = "1"
ron = { optional = true, version = "0.1.7" }
serde = { optional = true, version = "1.0", features = ["serde_derive"] }

View File

@ -16,7 +16,6 @@ void brush_vs(
vec4 segment_data
);
#define VECS_PER_BRUSH_PRIM 2
#define VECS_PER_SEGMENT 2
#define BRUSH_FLAG_PERSPECTIVE_INTERPOLATION 1
@ -24,76 +23,19 @@ void brush_vs(
#define BRUSH_FLAG_SEGMENT_REPEAT_X 4
#define BRUSH_FLAG_SEGMENT_REPEAT_Y 8
//Note: these have to match `gpu_types` constants
#define INT_BITS (31)
#define CLIP_CHAIN_RECT_BITS (22)
#define SEGMENT_BITS (INT_BITS - CLIP_CHAIN_RECT_BITS)
#define EDGE_FLAG_BITS (4)
#define BRUSH_FLAG_BITS (4)
#define CLIP_SCROLL_INDEX_BITS (INT_BITS - EDGE_FLAG_BITS - BRUSH_FLAG_BITS)
struct BrushInstance {
int picture_address;
int prim_address;
int clip_chain_rect_index;
int scroll_node_id;
int clip_address;
int z;
int segment_index;
int edge_mask;
int flags;
ivec3 user_data;
};
BrushInstance load_brush() {
BrushInstance bi;
bi.picture_address = aData0.x & 0xffff;
bi.clip_address = aData0.x >> 16;
bi.prim_address = aData0.y;
bi.clip_chain_rect_index = aData0.z & ((1 << CLIP_CHAIN_RECT_BITS) - 1);
bi.segment_index = aData0.z >> CLIP_CHAIN_RECT_BITS;
bi.z = aData0.w;
bi.scroll_node_id = aData1.x & ((1 << CLIP_SCROLL_INDEX_BITS) - 1);
bi.edge_mask = (aData1.x >> CLIP_SCROLL_INDEX_BITS) & 0xf;
bi.flags = (aData1.x >> (CLIP_SCROLL_INDEX_BITS + EDGE_FLAG_BITS)) & 0xf;
bi.user_data = aData1.yzw;
return bi;
}
struct BrushPrimitive {
RectWithSize local_rect;
RectWithSize local_clip_rect;
};
BrushPrimitive fetch_brush_primitive(int address, int clip_chain_rect_index) {
vec4 data[2] = fetch_from_resource_cache_2(address);
RectWithSize clip_chain_rect = fetch_clip_chain_rect(clip_chain_rect_index);
RectWithSize brush_clip_rect = RectWithSize(data[1].xy, data[1].zw);
RectWithSize clip_rect = intersect_rects(clip_chain_rect, brush_clip_rect);
BrushPrimitive prim = BrushPrimitive(RectWithSize(data[0].xy, data[0].zw), clip_rect);
return prim;
}
void main(void) {
// Load the brush instance from vertex attributes.
BrushInstance brush = load_brush();
// Load the geometry for this brush. For now, this is simply the
// local rect of the primitive. In the future, this will support
// loading segment rects, and other rect formats (glyphs).
BrushPrimitive brush_prim =
fetch_brush_primitive(brush.prim_address, brush.clip_chain_rect_index);
int prim_header_address = aData.x;
int clip_address = aData.y;
int segment_index = aData.z & 0xffff;
int edge_flags = (aData.z >> 16) & 0xff;
int brush_flags = (aData.z >> 24) & 0xff;
PrimitiveHeader ph = fetch_prim_header(prim_header_address);
// Fetch the segment of this brush primitive we are drawing.
int segment_address = brush.prim_address +
VECS_PER_BRUSH_PRIM +
int segment_address = ph.specific_prim_address +
VECS_PER_SPECIFIC_BRUSH +
brush.segment_index * VECS_PER_SEGMENT;
segment_index * VECS_PER_SEGMENT;
vec4[2] segment_data = fetch_from_resource_cache_2(segment_address);
RectWithSize local_segment_rect = RectWithSize(segment_data[0].xy, segment_data[0].zw);
@ -101,20 +43,20 @@ void main(void) {
VertexInfo vi;
// Fetch the dynamic picture that we are drawing on.
PictureTask pic_task = fetch_picture_task(brush.picture_address);
ClipArea clip_area = fetch_clip_area(brush.clip_address);
PictureTask pic_task = fetch_picture_task(ph.render_task_index);
ClipArea clip_area = fetch_clip_area(clip_address);
ClipScrollNode scroll_node = fetch_clip_scroll_node(brush.scroll_node_id);
Transform transform = fetch_transform(ph.transform_id);
// Write the normal vertex information out.
if (scroll_node.is_axis_aligned) {
if (transform.is_axis_aligned) {
vi = write_vertex(
local_segment_rect,
brush_prim.local_clip_rect,
float(brush.z),
scroll_node,
ph.local_clip_rect,
ph.z,
transform,
pic_task,
brush_prim.local_rect
ph.local_rect
);
// TODO(gw): transform bounds may be referenced by
@ -127,16 +69,16 @@ void main(void) {
init_transform_vs(vec4(vec2(-1000000.0), vec2(1000000.0)));
#endif
} else {
bvec4 edge_mask = notEqual(brush.edge_mask & ivec4(1, 2, 4, 8), ivec4(0));
bool do_perspective_interpolation = (brush.flags & BRUSH_FLAG_PERSPECTIVE_INTERPOLATION) != 0;
bvec4 edge_mask = notEqual(edge_flags & ivec4(1, 2, 4, 8), ivec4(0));
bool do_perspective_interpolation = (brush_flags & BRUSH_FLAG_PERSPECTIVE_INTERPOLATION) != 0;
vi = write_transform_vertex(
local_segment_rect,
brush_prim.local_rect,
brush_prim.local_clip_rect,
ph.local_rect,
ph.local_clip_rect,
mix(vec4(0.0), vec4(1.0), edge_mask),
float(brush.z),
scroll_node,
ph.z,
transform,
pic_task,
do_perspective_interpolation
);
@ -158,13 +100,13 @@ void main(void) {
// Run the specific brush VS code to write interpolators.
brush_vs(
vi,
brush.prim_address + VECS_PER_BRUSH_PRIM,
brush_prim.local_rect,
ph.specific_prim_address,
ph.local_rect,
local_segment_rect,
brush.user_data,
scroll_node.transform,
ph.user_data,
transform.m,
pic_task,
brush.flags,
brush_flags,
segment_data[1]
);
}

View File

@ -49,11 +49,16 @@ void brush_vs(
mat4 transform,
PictureTask pic_task,
int brush_flags,
vec4 unused
vec4 texel_rect
) {
Gradient gradient = fetch_gradient(prim_address);
vPos = vi.local_pos - local_rect.p0;
if ((brush_flags & BRUSH_FLAG_SEGMENT_RELATIVE) != 0) {
vPos = (vi.local_pos - segment_rect.p0) / segment_rect.size;
vPos = vPos * (texel_rect.zw - texel_rect.xy) + texel_rect.xy;
} else {
vPos = vi.local_pos - local_rect.p0;
}
vec2 start_point = gradient.start_end_point.xy;
vec2 end_point = gradient.start_end_point.zw;

View File

@ -49,11 +49,16 @@ void brush_vs(
mat4 transform,
PictureTask pic_task,
int brush_flags,
vec4 unused
vec4 texel_rect
) {
RadialGradient gradient = fetch_radial_gradient(prim_address);
vPos = vi.local_pos - local_rect.p0;
if ((brush_flags & BRUSH_FLAG_SEGMENT_RELATIVE) != 0) {
vPos = (vi.local_pos - segment_rect.p0) / segment_rect.size;
vPos = vPos * (texel_rect.zw - texel_rect.xy) + texel_rect.xy;
} else {
vPos = vi.local_pos - local_rect.p0;
}
vCenter = gradient.center_start_end_radius.xy;
vStartRadius = gradient.center_start_end_radius.z;

View File

@ -1,88 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifdef WR_VERTEX_SHADER
#define VECS_PER_CLIP_SCROLL_NODE 9
uniform HIGHP_SAMPLER_FLOAT sampler2D sClipScrollNodes;
struct ClipScrollNode {
mat4 transform;
mat4 inv_transform;
bool is_axis_aligned;
};
ClipScrollNode fetch_clip_scroll_node(int index) {
ClipScrollNode node;
// Create a UV base coord for each 8 texels.
// This is required because trying to use an offset
// of more than 8 texels doesn't work on some versions
// of OSX.
ivec2 uv = get_fetch_uv(index, VECS_PER_CLIP_SCROLL_NODE);
ivec2 uv0 = ivec2(uv.x + 0, uv.y);
ivec2 uv1 = ivec2(uv.x + 8, uv.y);
node.transform[0] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(0, 0));
node.transform[1] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(1, 0));
node.transform[2] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(2, 0));
node.transform[3] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(3, 0));
node.inv_transform[0] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(4, 0));
node.inv_transform[1] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(5, 0));
node.inv_transform[2] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(6, 0));
node.inv_transform[3] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(7, 0));
vec4 misc = TEXEL_FETCH(sClipScrollNodes, uv1, 0, ivec2(0, 0));
node.is_axis_aligned = misc.x == 0.0;
return node;
}
// Return the intersection of the plane (set up by "normal" and "point")
// with the ray (set up by "ray_origin" and "ray_dir"),
// writing the resulting scaler into "t".
bool ray_plane(vec3 normal, vec3 pt, vec3 ray_origin, vec3 ray_dir, out float t)
{
float denom = dot(normal, ray_dir);
if (abs(denom) > 1e-6) {
vec3 d = pt - ray_origin;
t = dot(d, normal) / denom;
return t >= 0.0;
}
return false;
}
// Apply the inverse transform "inv_transform"
// to the reference point "ref" in CSS space,
// producing a local point on a ClipScrollNode plane,
// set by a base point "a" and a normal "n".
vec4 untransform(vec2 ref, vec3 n, vec3 a, mat4 inv_transform) {
vec3 p = vec3(ref, -10000.0);
vec3 d = vec3(0, 0, 1.0);
float t = 0.0;
// get an intersection of the ClipScrollNode plane with Z axis vector,
// originated from the "ref" point
ray_plane(n, a, p, d, t);
float z = p.z + d.z * t; // Z of the visible point on the ClipScrollNode
vec4 r = inv_transform * vec4(ref, z, 1.0);
return r;
}
// Given a CSS space position, transform it back into the ClipScrollNode space.
vec4 get_node_pos(vec2 pos, ClipScrollNode node) {
// get a point on the scroll node plane
vec4 ah = node.transform * vec4(0.0, 0.0, 0.0, 1.0);
vec3 a = ah.xyz / ah.w;
// get the normal to the scroll node plane
vec3 n = transpose(mat3(node.inv_transform)) * vec3(0.0, 0.0, 1.0);
return untransform(pos, n, a, node.inv_transform);
}
#endif //WR_VERTEX_SHADER

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/. */
#include rect,clip_scroll,render_task,resource_cache,snap,transform
#include rect,render_task,resource_cache,snap,transform
#ifdef WR_VERTEX_SHADER
@ -19,7 +19,7 @@ in ivec4 aClipDataResourceAddress;
struct ClipMaskInstance {
int render_task_address;
int scroll_node_id;
int transform_id;
int segment;
ivec2 clip_data_address;
ivec2 resource_address;
@ -29,7 +29,7 @@ ClipMaskInstance fetch_clip_item() {
ClipMaskInstance cmi;
cmi.render_task_address = aClipRenderTaskAddress;
cmi.scroll_node_id = aScrollNodeId;
cmi.transform_id = aScrollNodeId;
cmi.segment = aClipSegment;
cmi.clip_data_address = aClipDataResourceAddress.xy;
cmi.resource_address = aClipDataResourceAddress.zw;
@ -51,20 +51,20 @@ RectWithSize intersect_rect(RectWithSize a, RectWithSize b) {
// The transformed vertex function that always covers the whole clip area,
// which is the intersection of all clip instances of a given primitive
ClipVertexInfo write_clip_tile_vertex(RectWithSize local_clip_rect,
ClipScrollNode scroll_node,
Transform transform,
ClipArea area) {
vec2 device_pos = area.screen_origin + aPosition.xy * area.common_data.task_rect.size;
vec2 actual_pos = device_pos;
if (scroll_node.is_axis_aligned) {
if (transform.is_axis_aligned) {
vec4 snap_positions = compute_snap_positions(
scroll_node.transform,
transform.m,
local_clip_rect
);
vec2 snap_offsets = compute_snap_offset_impl(
device_pos,
scroll_node.transform,
transform.m,
local_clip_rect,
RectWithSize(snap_positions.xy, snap_positions.zw - snap_positions.xy),
snap_positions,
@ -81,7 +81,7 @@ ClipVertexInfo write_clip_tile_vertex(RectWithSize local_clip_rect,
if (area.local_space) {
node_pos = vec4(actual_pos / uDevicePixelRatio, 0.0, 1.0);
} else {
node_pos = get_node_pos(actual_pos / uDevicePixelRatio, scroll_node);
node_pos = get_node_pos(actual_pos / uDevicePixelRatio, transform);
}
// compute the point position inside the scroll node, in CSS space

View File

@ -41,12 +41,12 @@ BoxShadowData fetch_data(ivec2 address) {
void main(void) {
ClipMaskInstance cmi = fetch_clip_item();
ClipArea area = fetch_clip_area(cmi.render_task_address);
ClipScrollNode scroll_node = fetch_clip_scroll_node(cmi.scroll_node_id);
Transform transform = fetch_transform(cmi.transform_id);
BoxShadowData bs_data = fetch_data(cmi.clip_data_address);
ImageResource res = fetch_image_resource_direct(cmi.resource_address);
ClipVertexInfo vi = write_clip_tile_vertex(bs_data.dest_rect,
scroll_node,
transform,
area);
vLayer = res.layer;

View File

@ -26,13 +26,13 @@ ImageMaskData fetch_mask_data(ivec2 address) {
void main(void) {
ClipMaskInstance cmi = fetch_clip_item();
ClipArea area = fetch_clip_area(cmi.render_task_address);
ClipScrollNode scroll_node = fetch_clip_scroll_node(cmi.scroll_node_id);
Transform transform = fetch_transform(cmi.transform_id);
ImageMaskData mask = fetch_mask_data(cmi.clip_data_address);
RectWithSize local_rect = mask.local_rect;
ImageResource res = fetch_image_resource_direct(cmi.resource_address);
ClipVertexInfo vi = write_clip_tile_vertex(local_rect,
scroll_node,
transform,
area);
vPos = vi.local_pos;

View File

@ -43,11 +43,11 @@ LineDecorationData fetch_data(ivec2 address) {
void main(void) {
ClipMaskInstance cmi = fetch_clip_item();
ClipArea area = fetch_clip_area(cmi.render_task_address);
ClipScrollNode scroll_node = fetch_clip_scroll_node(cmi.scroll_node_id);
Transform transform = fetch_transform(cmi.transform_id);
LineDecorationData data = fetch_data(cmi.clip_data_address);
ClipVertexInfo vi = write_clip_tile_vertex(data.local_rect,
scroll_node,
transform,
area);

View File

@ -60,11 +60,11 @@ ClipData fetch_clip(ivec2 address) {
void main(void) {
ClipMaskInstance cmi = fetch_clip_item();
ClipArea area = fetch_clip_area(cmi.render_task_address);
ClipScrollNode scroll_node = fetch_clip_scroll_node(cmi.scroll_node_id);
Transform transform = fetch_transform(cmi.transform_id);
ClipData clip = fetch_clip(cmi.clip_data_address);
RectWithSize local_rect = clip.rect.rect;
ClipVertexInfo vi = write_clip_tile_vertex(local_rect, scroll_node, area);
ClipVertexInfo vi = write_clip_tile_vertex(local_rect, transform, area);
vPos = vi.local_pos;
vClipMode = clip.rect.mode.x;

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/. */
#include rect,clip_scroll,render_task,resource_cache,snap,transform
#include rect,render_task,resource_cache,snap,transform
#define EXTEND_MODE_CLAMP 0
#define EXTEND_MODE_REPEAT 1
@ -32,11 +32,6 @@ varying vec3 vClipMaskUv;
#ifdef WR_VERTEX_SHADER
#define VECS_PER_LOCAL_CLIP_RECT 1
#define VECS_PER_PRIM_HEADER 2
#define VECS_PER_TEXT_RUN 3
#define VECS_PER_GRADIENT_STOP 2
#define COLOR_MODE_FROM_PASS 0
#define COLOR_MODE_ALPHA 1
#define COLOR_MODE_SUBPX_CONST_COLOR 2
@ -47,16 +42,46 @@ varying vec3 vClipMaskUv;
#define COLOR_MODE_BITMAP 7
#define COLOR_MODE_COLOR_BITMAP 8
uniform HIGHP_SAMPLER_FLOAT sampler2D sLocalClipRects;
uniform HIGHP_SAMPLER_FLOAT sampler2D sPrimitiveHeadersF;
uniform HIGHP_SAMPLER_FLOAT isampler2D sPrimitiveHeadersI;
// Instanced attributes
in ivec4 aData0;
in ivec4 aData1;
in ivec4 aData;
RectWithSize fetch_clip_chain_rect(int index) {
ivec2 uv = get_fetch_uv(index, VECS_PER_LOCAL_CLIP_RECT);
vec4 rect = TEXEL_FETCH(sLocalClipRects, uv, 0, ivec2(0, 0));
return RectWithSize(rect.xy, rect.zw);
#define VECS_PER_PRIM_HEADER_F 2
#define VECS_PER_PRIM_HEADER_I 2
struct PrimitiveHeader {
RectWithSize local_rect;
RectWithSize local_clip_rect;
float z;
int specific_prim_address;
int render_task_index;
int clip_task_index;
int transform_id;
ivec3 user_data;
};
PrimitiveHeader fetch_prim_header(int index) {
PrimitiveHeader ph;
ivec2 uv_f = get_fetch_uv(index, VECS_PER_PRIM_HEADER_F);
vec4 local_rect = TEXEL_FETCH(sPrimitiveHeadersF, uv_f, 0, ivec2(0, 0));
vec4 local_clip_rect = TEXEL_FETCH(sPrimitiveHeadersF, uv_f, 0, ivec2(1, 0));
ph.local_rect = RectWithSize(local_rect.xy, local_rect.zw);
ph.local_clip_rect = RectWithSize(local_clip_rect.xy, local_clip_rect.zw);
ivec2 uv_i = get_fetch_uv(index, VECS_PER_PRIM_HEADER_I);
ivec4 data0 = TEXEL_FETCH(sPrimitiveHeadersI, uv_i, 0, ivec2(0, 0));
ivec4 data1 = TEXEL_FETCH(sPrimitiveHeadersI, uv_i, 0, ivec2(1, 0));
ph.z = float(data0.x);
ph.render_task_index = data0.y;
ph.specific_prim_address = data0.z;
ph.clip_task_index = data0.w;
ph.transform_id = data1.x;
ph.user_data = data1.yzw;
return ph;
}
struct VertexInfo {
@ -69,7 +94,7 @@ struct VertexInfo {
VertexInfo write_vertex(RectWithSize instance_rect,
RectWithSize local_clip_rect,
float z,
ClipScrollNode scroll_node,
Transform transform,
PictureTask task,
RectWithSize snap_rect) {
@ -82,13 +107,13 @@ VertexInfo write_vertex(RectWithSize instance_rect,
/// Compute the snapping offset.
vec2 snap_offset = compute_snap_offset(
clamped_local_pos,
scroll_node.transform,
transform.m,
snap_rect,
vec2(0.5)
);
// Transform the current vertex to world space.
vec4 world_pos = scroll_node.transform * vec4(clamped_local_pos, 0.0, 1.0);
vec4 world_pos = transform.m * vec4(clamped_local_pos, 0.0, 1.0);
// Convert the world positions to device pixel space.
vec2 device_pos = world_pos.xy / world_pos.w * uDevicePixelRatio;
@ -135,7 +160,7 @@ VertexInfo write_transform_vertex(RectWithSize local_segment_rect,
RectWithSize local_clip_rect,
vec4 clip_edge_mask,
float z,
ClipScrollNode scroll_node,
Transform transform,
PictureTask task,
bool do_perspective_interpolation) {
// Calculate a clip rect from local_rect + local clip
@ -168,7 +193,7 @@ VertexInfo write_transform_vertex(RectWithSize local_segment_rect,
vec2 local_pos = local_segment_rect.p0 + local_segment_rect.size * aPosition.xy;
// Transform the current vertex to the world cpace.
vec4 world_pos = scroll_node.transform * vec4(local_pos, 0.0, 1.0);
vec4 world_pos = transform.m * vec4(local_pos, 0.0, 1.0);
// Convert the world positions to device pixel space.
vec2 device_pos = world_pos.xy / world_pos.w * uDevicePixelRatio;

View File

@ -44,10 +44,10 @@ struct SplitCompositeInstance {
SplitCompositeInstance fetch_composite_instance() {
SplitCompositeInstance ci;
ci.render_task_index = aData0.x;
ci.src_task_index = aData0.y;
ci.polygons_address = aData0.z;
ci.z = float(aData0.w);
ci.render_task_index = aData.x;
ci.src_task_index = aData.y;
ci.polygons_address = aData.z;
ci.z = float(aData.w);
return ci;
}

View File

@ -15,6 +15,8 @@ varying vec4 vUvClip;
#ifdef WR_VERTEX_SHADER
#define VECS_PER_TEXT_RUN 3
struct Glyph {
vec2 offset;
};
@ -57,94 +59,16 @@ TextRun fetch_text_run(int address) {
return TextRun(data[0], data[1], data[2].xy);
}
struct PrimitiveInstance {
int prim_address;
int specific_prim_address;
int render_task_index;
int clip_task_index;
int scroll_node_id;
int clip_chain_rect_index;
int z;
int user_data0;
int user_data1;
int user_data2;
};
PrimitiveInstance fetch_prim_instance() {
PrimitiveInstance pi;
pi.prim_address = aData0.x;
pi.specific_prim_address = pi.prim_address + VECS_PER_PRIM_HEADER;
pi.render_task_index = aData0.y % 0x10000;
pi.clip_task_index = aData0.y / 0x10000;
pi.clip_chain_rect_index = aData0.z;
pi.scroll_node_id = aData0.w;
pi.z = aData1.x;
pi.user_data0 = aData1.y;
pi.user_data1 = aData1.z;
pi.user_data2 = aData1.w;
return pi;
}
struct Primitive {
ClipScrollNode scroll_node;
ClipArea clip_area;
PictureTask task;
RectWithSize local_rect;
RectWithSize local_clip_rect;
int specific_prim_address;
int user_data0;
int user_data1;
int user_data2;
float z;
};
struct PrimitiveGeometry {
RectWithSize local_rect;
RectWithSize local_clip_rect;
};
PrimitiveGeometry fetch_primitive_geometry(int address) {
vec4 geom[2] = fetch_from_resource_cache_2(address);
return PrimitiveGeometry(RectWithSize(geom[0].xy, geom[0].zw),
RectWithSize(geom[1].xy, geom[1].zw));
}
Primitive load_primitive() {
PrimitiveInstance pi = fetch_prim_instance();
Primitive prim;
prim.scroll_node = fetch_clip_scroll_node(pi.scroll_node_id);
prim.clip_area = fetch_clip_area(pi.clip_task_index);
prim.task = fetch_picture_task(pi.render_task_index);
RectWithSize clip_chain_rect = fetch_clip_chain_rect(pi.clip_chain_rect_index);
PrimitiveGeometry geom = fetch_primitive_geometry(pi.prim_address);
prim.local_rect = geom.local_rect;
prim.local_clip_rect = intersect_rects(clip_chain_rect, geom.local_clip_rect);
prim.specific_prim_address = pi.specific_prim_address;
prim.user_data0 = pi.user_data0;
prim.user_data1 = pi.user_data1;
prim.user_data2 = pi.user_data2;
prim.z = float(pi.z);
return prim;
}
VertexInfo write_text_vertex(vec2 clamped_local_pos,
RectWithSize local_clip_rect,
float z,
ClipScrollNode scroll_node,
Transform transform,
PictureTask task,
vec2 text_offset,
RectWithSize snap_rect,
vec2 snap_bias) {
// Transform the current vertex to world space.
vec4 world_pos = scroll_node.transform * vec4(clamped_local_pos, 0.0, 1.0);
vec4 world_pos = transform.m * vec4(clamped_local_pos, 0.0, 1.0);
// Convert the world positions to device pixel space.
float device_scale = uDevicePixelRatio / world_pos.w;
@ -159,13 +83,13 @@ VertexInfo write_text_vertex(vec2 clamped_local_pos,
bool remove_subpx_offset = true;
#else
// Compute the snapping offset only if the scroll node transform is axis-aligned.
bool remove_subpx_offset = scroll_node.is_axis_aligned;
bool remove_subpx_offset = transform.is_axis_aligned;
#endif
if (remove_subpx_offset) {
// Ensure the transformed text offset does not contain a subpixel translation
// such that glyph snapping is stable for equivalent glyph subpixel positions.
vec2 world_text_offset = mat2(scroll_node.transform) * text_offset;
vec2 device_text_pos = (scroll_node.transform[3].xy + world_text_offset) * device_scale;
vec2 world_text_offset = mat2(transform.m) * text_offset;
vec2 device_text_pos = (transform.m[3].xy + world_text_offset) * device_scale;
final_pos += floor(device_text_pos + 0.5) - device_text_pos;
#ifdef WR_FEATURE_GLYPH_TRANSFORM
@ -177,7 +101,7 @@ VertexInfo write_text_vertex(vec2 clamped_local_pos,
#else
// The transformed text offset has already been snapped, so remove it from the transform
// when snapping the glyph.
mat4 snap_transform = scroll_node.transform;
mat4 snap_transform = transform.m;
snap_transform[3].xy = -world_text_offset;
final_pos += compute_snap_offset(
clamped_local_pos,
@ -201,31 +125,37 @@ VertexInfo write_text_vertex(vec2 clamped_local_pos,
}
void main(void) {
Primitive prim = load_primitive();
TextRun text = fetch_text_run(prim.specific_prim_address);
int prim_header_address = aData.x;
int glyph_index = aData.y;
int resource_address = aData.z;
int subpx_dir = aData.w >> 16;
int color_mode = aData.w & 0xffff;
int glyph_index = prim.user_data0;
int resource_address = prim.user_data1;
int subpx_dir = prim.user_data2 >> 16;
int color_mode = prim.user_data2 & 0xffff;
PrimitiveHeader ph = fetch_prim_header(prim_header_address);
Transform transform = fetch_transform(ph.transform_id);
ClipArea clip_area = fetch_clip_area(ph.clip_task_index);
PictureTask task = fetch_picture_task(ph.render_task_index);
TextRun text = fetch_text_run(ph.specific_prim_address);
if (color_mode == COLOR_MODE_FROM_PASS) {
color_mode = uMode;
}
Glyph glyph = fetch_glyph(prim.specific_prim_address, glyph_index);
Glyph glyph = fetch_glyph(ph.specific_prim_address, glyph_index);
GlyphResource res = fetch_glyph_resource(resource_address);
#ifdef WR_FEATURE_GLYPH_TRANSFORM
// Transform from local space to glyph space.
mat2 transform = mat2(prim.scroll_node.transform) * uDevicePixelRatio;
mat2 glyph_transform = mat2(transform.m) * uDevicePixelRatio;
// Compute the glyph rect in glyph space.
RectWithSize glyph_rect = RectWithSize(res.offset + transform * (text.offset + glyph.offset),
RectWithSize glyph_rect = RectWithSize(res.offset + glyph_transform * (text.offset + glyph.offset),
res.uv_rect.zw - res.uv_rect.xy);
// Transform the glyph rect back to local space.
mat2 inv = inverse(transform);
mat2 inv = inverse(glyph_transform);
RectWithSize local_rect = transform_rect(glyph_rect, inv);
// Select the corner of the glyph's local space rect that we are processing.
@ -234,9 +164,9 @@ void main(void) {
// If the glyph's local rect would fit inside the local clip rect, then select a corner from
// the device space glyph rect to reduce overdraw of clipped pixels in the fragment shader.
// Otherwise, fall back to clamping the glyph's local rect to the local clip rect.
local_pos = rect_inside_rect(local_rect, prim.local_clip_rect) ?
local_pos = rect_inside_rect(local_rect, ph.local_clip_rect) ?
inv * (glyph_rect.p0 + glyph_rect.size * aPosition.xy) :
clamp_rect(local_pos, prim.local_clip_rect);
clamp_rect(local_pos, ph.local_clip_rect);
#else
// Scale from glyph space to local space.
float scale = res.scale / uDevicePixelRatio;
@ -249,7 +179,7 @@ void main(void) {
vec2 local_pos = glyph_rect.p0 + glyph_rect.size * aPosition.xy;
// Clamp to the local clip rect.
local_pos = clamp_rect(local_pos, prim.local_clip_rect);
local_pos = clamp_rect(local_pos, ph.local_clip_rect);
#endif
vec2 snap_bias;
@ -278,22 +208,22 @@ void main(void) {
}
VertexInfo vi = write_text_vertex(local_pos,
prim.local_clip_rect,
prim.z,
prim.scroll_node,
prim.task,
ph.local_clip_rect,
ph.z,
transform,
task,
text.offset,
glyph_rect,
snap_bias);
#ifdef WR_FEATURE_GLYPH_TRANSFORM
vec2 f = (transform * vi.local_pos - glyph_rect.p0) / glyph_rect.size;
vec2 f = (glyph_transform * vi.local_pos - glyph_rect.p0) / glyph_rect.size;
vUvClip = vec4(f, 1.0 - f);
#else
vec2 f = (vi.local_pos - glyph_rect.p0) / glyph_rect.size;
#endif
write_clip(vi.screen_pos, prim.clip_area);
write_clip(vi.screen_pos, clip_area);
switch (color_mode) {
case COLOR_MODE_ALPHA:

View File

@ -8,7 +8,7 @@ vec4 compute_snap_positions(mat4 transform, RectWithSize snap_rect) {
// Ensure that the snap rect is at *least* one device pixel in size.
// TODO(gw): It's not clear to me that this is "correct". Specifically,
// how should it interact with sub-pixel snap rects when there
// is a scroll_node transform with scale present? But it does fix
// is a transform with scale present? But it does fix
// the test cases we have in Servo that are failing without it
// and seem better than not having this at all.
snap_rect.size = max(snap_rect.size, vec2(1.0 / uDevicePixelRatio));
@ -41,7 +41,7 @@ vec2 compute_snap_offset_impl(
}
// Compute a snapping offset in world space (adjusted to pixel ratio),
// given local position on the scroll_node and a snap rectangle.
// given local position on the transform and a snap rectangle.
vec2 compute_snap_offset(vec2 local_pos,
mat4 transform,
RectWithSize snap_rect,

View File

@ -6,10 +6,89 @@ flat varying vec4 vTransformBounds;
#ifdef WR_VERTEX_SHADER
#define VECS_PER_TRANSFORM 8
uniform HIGHP_SAMPLER_FLOAT sampler2D sTransformPalette;
void init_transform_vs(vec4 local_bounds) {
vTransformBounds = local_bounds;
}
struct Transform {
mat4 m;
mat4 inv_m;
bool is_axis_aligned;
};
Transform fetch_transform(int id) {
Transform transform;
transform.is_axis_aligned = (id >> 24) == 0;
int index = id & 0x00ffffff;
// Create a UV base coord for each 8 texels.
// This is required because trying to use an offset
// of more than 8 texels doesn't work on some versions
// of OSX.
ivec2 uv = get_fetch_uv(index, VECS_PER_TRANSFORM);
ivec2 uv0 = ivec2(uv.x + 0, uv.y);
transform.m[0] = TEXEL_FETCH(sTransformPalette, uv0, 0, ivec2(0, 0));
transform.m[1] = TEXEL_FETCH(sTransformPalette, uv0, 0, ivec2(1, 0));
transform.m[2] = TEXEL_FETCH(sTransformPalette, uv0, 0, ivec2(2, 0));
transform.m[3] = TEXEL_FETCH(sTransformPalette, uv0, 0, ivec2(3, 0));
transform.inv_m[0] = TEXEL_FETCH(sTransformPalette, uv0, 0, ivec2(4, 0));
transform.inv_m[1] = TEXEL_FETCH(sTransformPalette, uv0, 0, ivec2(5, 0));
transform.inv_m[2] = TEXEL_FETCH(sTransformPalette, uv0, 0, ivec2(6, 0));
transform.inv_m[3] = TEXEL_FETCH(sTransformPalette, uv0, 0, ivec2(7, 0));
return transform;
}
// Return the intersection of the plane (set up by "normal" and "point")
// with the ray (set up by "ray_origin" and "ray_dir"),
// writing the resulting scaler into "t".
bool ray_plane(vec3 normal, vec3 pt, vec3 ray_origin, vec3 ray_dir, out float t)
{
float denom = dot(normal, ray_dir);
if (abs(denom) > 1e-6) {
vec3 d = pt - ray_origin;
t = dot(d, normal) / denom;
return t >= 0.0;
}
return false;
}
// Apply the inverse transform "inv_transform"
// to the reference point "ref" in CSS space,
// producing a local point on a Transform plane,
// set by a base point "a" and a normal "n".
vec4 untransform(vec2 ref, vec3 n, vec3 a, mat4 inv_transform) {
vec3 p = vec3(ref, -10000.0);
vec3 d = vec3(0, 0, 1.0);
float t = 0.0;
// get an intersection of the Transform plane with Z axis vector,
// originated from the "ref" point
ray_plane(n, a, p, d, t);
float z = p.z + d.z * t; // Z of the visible point on the Transform
vec4 r = inv_transform * vec4(ref, z, 1.0);
return r;
}
// Given a CSS space position, transform it back into the Transform space.
vec4 get_node_pos(vec2 pos, Transform transform) {
// get a point on the scroll node plane
vec4 ah = transform.m * vec4(0.0, 0.0, 0.0, 1.0);
vec3 a = ah.xyz / ah.w;
// get the normal to the scroll node plane
vec3 n = transpose(mat3(transform.inv_m)) * vec3(0.0, 0.0, 1.0);
return untransform(pos, n, a, transform.inv_m);
}
#endif //WR_VERTEX_SHADER
#ifdef WR_FRAGMENT_SHADER

View File

@ -11,10 +11,10 @@ use clip_scroll_tree::{CoordinateSystemId};
use euclid::{TypedTransform3D, vec3};
use glyph_rasterizer::GlyphFormat;
use gpu_cache::{GpuCache, GpuCacheHandle, GpuCacheAddress};
use gpu_types::{BrushFlags, BrushInstance, ClipChainRectIndex};
use gpu_types::{ClipMaskInstance, ClipScrollNodeIndex, SplitCompositeInstance};
use gpu_types::{PrimitiveInstance, RasterizationSpace, GlyphInstance, ZBufferId};
use gpu_types::ZBufferIdGenerator;
use gpu_types::{BrushFlags, BrushInstance, PrimitiveHeaders};
use gpu_types::{ClipMaskInstance, SplitCompositeInstance};
use gpu_types::{PrimitiveInstance, RasterizationSpace, GlyphInstance};
use gpu_types::{PrimitiveHeader, PrimitiveHeaderIndex, TransformPaletteId, TransformPalette};
use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture};
use picture::{PictureCompositeMode, PicturePrimitive, PictureSurface};
use plane_split::{BspSplitter, Polygon, Splitter};
@ -29,7 +29,7 @@ use resource_cache::{CacheItem, GlyphFetchResult, ImageRequest, ResourceCache};
use scene::FilterOpHelpers;
use std::{usize, f32, i32};
use tiling::{RenderTargetContext};
use util::{MatrixHelpers, TransformedRectKind};
use util::{TransformedRectKind};
// Special sentinel value recognized by the shader. It is considered to be
// a dummy task that doesn't mask out anything.
@ -455,7 +455,7 @@ impl AlphaBatchBuilder {
gpu_cache: &mut GpuCache,
render_tasks: &RenderTaskTree,
deferred_resolves: &mut Vec<DeferredResolve>,
z_generator: &mut ZBufferIdGenerator,
prim_headers: &mut PrimitiveHeaders,
) {
let task_address = render_tasks.get_task_address(task_id);
@ -476,10 +476,10 @@ impl AlphaBatchBuilder {
// Add each run in this picture to the batch.
for run in &pic.runs {
let scroll_node = &ctx.clip_scroll_tree.nodes[run.clip_and_scroll.scroll_node_id.0];
let scroll_id = scroll_node.node_data_index;
let transform_id = ctx.transforms.get_id(scroll_node.transform_index);
self.add_run_to_batch(
run,
scroll_id,
transform_id,
ctx,
gpu_cache,
render_tasks,
@ -488,7 +488,7 @@ impl AlphaBatchBuilder {
deferred_resolves,
&mut splitter,
content_origin,
z_generator,
prim_headers,
);
}
@ -526,7 +526,7 @@ impl AlphaBatchBuilder {
task_address,
source_task_address,
gpu_address,
z_generator.next(),
prim_headers.z_generator.next(),
);
batch.push(PrimitiveInstance::from(instance));
@ -539,7 +539,7 @@ impl AlphaBatchBuilder {
fn add_run_to_batch(
&mut self,
run: &PrimitiveRun,
scroll_id: ClipScrollNodeIndex,
transform_id: TransformPaletteId,
ctx: &RenderTargetContext,
gpu_cache: &mut GpuCache,
render_tasks: &RenderTaskTree,
@ -548,7 +548,7 @@ impl AlphaBatchBuilder {
deferred_resolves: &mut Vec<DeferredResolve>,
splitter: &mut BspSplitter<f64, WorldPixel>,
content_origin: DeviceIntPoint,
z_generator: &mut ZBufferIdGenerator,
prim_headers: &mut PrimitiveHeaders,
) {
for i in 0 .. run.count {
let prim_index = PrimitiveIndex(run.base_prim_index.0 + i);
@ -556,8 +556,7 @@ impl AlphaBatchBuilder {
if metadata.screen_rect.is_some() {
self.add_prim_to_batch(
metadata.clip_chain_rect_index,
scroll_id,
transform_id,
prim_index,
ctx,
gpu_cache,
@ -567,7 +566,7 @@ impl AlphaBatchBuilder {
deferred_resolves,
splitter,
content_origin,
z_generator,
prim_headers,
);
}
}
@ -579,8 +578,7 @@ impl AlphaBatchBuilder {
// in that picture are being drawn into the same target.
fn add_prim_to_batch(
&mut self,
clip_chain_rect_index: ClipChainRectIndex,
scroll_id: ClipScrollNodeIndex,
transform_id: TransformPaletteId,
prim_index: PrimitiveIndex,
ctx: &RenderTargetContext,
gpu_cache: &mut GpuCache,
@ -590,18 +588,16 @@ impl AlphaBatchBuilder {
deferred_resolves: &mut Vec<DeferredResolve>,
splitter: &mut BspSplitter<f64, WorldPixel>,
content_origin: DeviceIntPoint,
z_generator: &mut ZBufferIdGenerator,
prim_headers: &mut PrimitiveHeaders,
) {
let z = z_generator.next();
let prim_metadata = ctx.prim_store.get_metadata(prim_index);
#[cfg(debug_assertions)] //TODO: why is this needed?
debug_assert_eq!(prim_metadata.prepared_frame_id, render_tasks.frame_id());
let scroll_node = &ctx.node_data[scroll_id.0 as usize];
// TODO(gw): Calculating this for every primitive is a bit
// wasteful. We should probably cache this in
// the scroll node...
let transform_kind = scroll_node.transform.transform_kind();
let transform_kind = transform_id.transform_kind();
let screen_rect = prim_metadata.screen_rect.expect("bug");
let task_relative_bounding_rect = DeviceIntRect::new(
@ -648,6 +644,15 @@ impl AlphaBatchBuilder {
BlendMode::None
};
let prim_header = PrimitiveHeader {
local_rect: prim_metadata.local_rect,
local_clip_rect: prim_metadata.combined_local_clip_rect,
task_address,
specific_prim_address: prim_cache_address,
clip_task_address,
transform_id,
};
match prim_metadata.prim_kind {
PrimitiveKind::Brush => {
let brush = &ctx.prim_store.cpu_brushes[prim_metadata.cpu_prim_index.0];
@ -701,23 +706,19 @@ impl AlphaBatchBuilder {
textures,
);
let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
let prim_header_index = prim_headers.push(&prim_header, [
uv_rect_address.as_int(),
(ShaderColorMode::ColorBitmap as i32) << 16 |
RasterizationSpace::Screen as i32,
0,
]);
let instance = BrushInstance {
picture_address: task_address,
prim_address: prim_cache_address,
clip_chain_rect_index,
scroll_id,
clip_task_address,
z,
prim_header_index,
segment_index: 0,
edge_flags: EdgeAaSegmentMask::empty(),
brush_flags: BrushFlags::empty(),
user_data: [
uv_rect_address.as_int(),
(ShaderColorMode::ColorBitmap as i32) << 16 |
RasterizationSpace::Screen as i32,
0,
],
clip_task_address,
};
batch.push(PrimitiveInstance::from(instance));
false
@ -727,7 +728,7 @@ impl AlphaBatchBuilder {
}
}
}
FilterOp::DropShadow(..) => {
FilterOp::DropShadow(offset, ..) => {
// Draw an instance of the shadow first, following by the content.
// Both the shadow and the content get drawn as a brush image.
@ -768,33 +769,44 @@ impl AlphaBatchBuilder {
// Get the GPU cache address of the extra data handle.
let shadow_prim_address = gpu_cache.get_address(&picture.extra_gpu_data_handle);
let content_prim_header_index = prim_headers.push(&prim_header, [
content_uv_rect_address,
(ShaderColorMode::ColorBitmap as i32) << 16 |
RasterizationSpace::Screen as i32,
0,
]);
let shadow_rect = prim_metadata.local_rect.translate(&offset);
let shadow_clip_rect = prim_metadata.local_clip_rect.translate(&offset);
let shadow_prim_header = PrimitiveHeader {
local_rect: shadow_rect,
local_clip_rect: shadow_clip_rect,
specific_prim_address: shadow_prim_address,
..prim_header
};
let shadow_prim_header_index = prim_headers.push(&shadow_prim_header, [
shadow_uv_rect_address,
(ShaderColorMode::Alpha as i32) << 16 |
RasterizationSpace::Screen as i32,
0,
]);
let shadow_instance = BrushInstance {
picture_address: task_address,
prim_address: shadow_prim_address,
clip_chain_rect_index,
scroll_id,
prim_header_index: shadow_prim_header_index,
clip_task_address,
z,
segment_index: 0,
edge_flags: EdgeAaSegmentMask::empty(),
brush_flags: BrushFlags::empty(),
user_data: [
shadow_uv_rect_address,
(ShaderColorMode::Alpha as i32) << 16 |
RasterizationSpace::Screen as i32,
0,
],
};
let content_instance = BrushInstance {
prim_address: prim_cache_address,
user_data: [
content_uv_rect_address,
(ShaderColorMode::ColorBitmap as i32) << 16 |
RasterizationSpace::Screen as i32,
0,
],
..shadow_instance
prim_header_index: content_prim_header_index,
clip_task_address,
segment_index: 0,
edge_flags: EdgeAaSegmentMask::empty(),
brush_flags: BrushFlags::empty(),
};
self.batch_list
@ -856,22 +868,18 @@ impl AlphaBatchBuilder {
let cache_task_id = surface.resolve_render_task_id();
let cache_task_address = render_tasks.get_task_address(cache_task_id);
let prim_header_index = prim_headers.push(&prim_header, [
cache_task_address.0 as i32,
filter_mode,
user_data,
]);
let instance = BrushInstance {
picture_address: task_address,
prim_address: prim_cache_address,
clip_chain_rect_index,
scroll_id,
prim_header_index,
clip_task_address,
z,
segment_index: 0,
edge_flags: EdgeAaSegmentMask::empty(),
brush_flags: BrushFlags::empty(),
user_data: [
cache_task_address.0 as i32,
filter_mode,
user_data,
],
};
let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
@ -907,22 +915,18 @@ impl AlphaBatchBuilder {
let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
let backdrop_task_address = render_tasks.get_task_address(backdrop_id);
let source_task_address = render_tasks.get_task_address(cache_task_id);
let prim_header_index = prim_headers.push(&prim_header, [
mode as u32 as i32,
backdrop_task_address.0 as i32,
source_task_address.0 as i32,
]);
let instance = BrushInstance {
picture_address: task_address,
prim_address: prim_cache_address,
clip_chain_rect_index,
scroll_id,
prim_header_index,
clip_task_address,
z,
segment_index: 0,
edge_flags: EdgeAaSegmentMask::empty(),
brush_flags: BrushFlags::empty(),
user_data: [
mode as u32 as i32,
backdrop_task_address.0 as i32,
source_task_address.0 as i32,
],
};
batch.push(PrimitiveInstance::from(instance));
@ -950,23 +954,19 @@ impl AlphaBatchBuilder {
let uv_rect_address = render_tasks[cache_task_id]
.get_texture_address(gpu_cache)
.as_int();
let prim_header_index = prim_headers.push(&prim_header, [
uv_rect_address,
(ShaderColorMode::ColorBitmap as i32) << 16 |
RasterizationSpace::Screen as i32,
0,
]);
let instance = BrushInstance {
picture_address: task_address,
prim_address: prim_cache_address,
clip_chain_rect_index,
scroll_id,
prim_header_index,
clip_task_address,
z,
segment_index: 0,
edge_flags: EdgeAaSegmentMask::empty(),
brush_flags: BrushFlags::empty(),
user_data: [
uv_rect_address,
(ShaderColorMode::ColorBitmap as i32) << 16 |
RasterizationSpace::Screen as i32,
0,
],
};
batch.push(PrimitiveInstance::from(instance));
false
@ -986,7 +986,7 @@ impl AlphaBatchBuilder {
gpu_cache,
render_tasks,
deferred_resolves,
z_generator,
prim_headers,
);
}
}
@ -999,18 +999,21 @@ impl AlphaBatchBuilder {
request.with_tile(tile.tile_offset),
) {
let prim_cache_address = gpu_cache.get_address(&tile.handle);
let prim_header = PrimitiveHeader {
specific_prim_address: prim_cache_address,
local_rect: tile.local_rect,
local_clip_rect: tile.local_clip_rect,
..prim_header
};
let prim_header_index = prim_headers.push(&prim_header, user_data);
self.add_image_tile_to_batch(
batch_kind,
specified_blend_mode,
textures,
clip_chain_rect_index,
prim_header_index,
clip_task_address,
&task_relative_bounding_rect,
prim_cache_address,
scroll_id,
task_address,
z,
user_data,
tile.edge_flags
);
}
@ -1023,13 +1026,11 @@ impl AlphaBatchBuilder {
BrushBatchKind::LinearGradient,
specified_blend_mode,
&task_relative_bounding_rect,
clip_chain_rect_index,
scroll_id,
task_address,
clip_task_address,
z,
gpu_cache,
&mut self.batch_list,
&prim_header,
prim_headers,
);
}
BrushKind::RadialGradient { ref stops_handle, ref visible_tiles, .. } if !visible_tiles.is_empty() => {
@ -1039,13 +1040,11 @@ impl AlphaBatchBuilder {
BrushBatchKind::RadialGradient,
specified_blend_mode,
&task_relative_bounding_rect,
clip_chain_rect_index,
scroll_id,
task_address,
clip_task_address,
z,
gpu_cache,
&mut self.batch_list,
&prim_header,
prim_headers,
);
}
_ => {
@ -1054,6 +1053,8 @@ impl AlphaBatchBuilder {
gpu_cache,
deferred_resolves,
) {
let prim_header_index = prim_headers.push(&prim_header, user_data);
self.add_brush_to_batch(
brush,
prim_metadata,
@ -1061,16 +1062,11 @@ impl AlphaBatchBuilder {
specified_blend_mode,
non_segmented_blend_mode,
textures,
clip_chain_rect_index,
prim_header_index,
clip_task_address,
&task_relative_bounding_rect,
prim_cache_address,
scroll_id,
task_address,
transform_kind,
z,
render_tasks,
user_data,
);
}
}
@ -1080,17 +1076,13 @@ impl AlphaBatchBuilder {
let text_cpu =
&ctx.prim_store.cpu_text_runs[prim_metadata.cpu_prim_index.0];
let font = text_cpu.get_font(
ctx.device_pixel_scale,
scroll_node.transform,
);
let subpx_dir = font.get_subpx_dir();
let subpx_dir = text_cpu.used_font.get_subpx_dir();
let glyph_fetch_buffer = &mut self.glyph_fetch_buffer;
let batch_list = &mut self.batch_list;
ctx.resource_cache.fetch_glyphs(
font,
text_cpu.used_font.clone(),
&text_cpu.glyph_keys,
glyph_fetch_buffer,
gpu_cache,
@ -1117,7 +1109,7 @@ impl AlphaBatchBuilder {
let (blend_mode, color_mode) = match glyph_format {
GlyphFormat::Subpixel |
GlyphFormat::TransformedSubpixel => {
if text_cpu.font.bg_color.a != 0 {
if text_cpu.used_font.bg_color.a != 0 {
(
BlendMode::SubpixelWithBgColor,
ShaderColorMode::FromRenderPassMode,
@ -1129,7 +1121,7 @@ impl AlphaBatchBuilder {
)
} else {
(
BlendMode::SubpixelConstantTextColor(text_cpu.font.color.into()),
BlendMode::SubpixelConstantTextColor(text_cpu.used_font.color.into()),
ShaderColorMode::SubpixelConstantTextColor,
)
}
@ -1155,15 +1147,11 @@ impl AlphaBatchBuilder {
}
};
let prim_header_index = prim_headers.push(&prim_header, [0; 3]);
let key = BatchKey::new(kind, blend_mode, textures);
let batch = batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
let base_instance = GlyphInstance::new(
prim_cache_address,
task_address,
clip_task_address,
clip_chain_rect_index,
scroll_id,
z,
prim_header_index,
);
for glyph in glyphs {
@ -1185,27 +1173,17 @@ impl AlphaBatchBuilder {
batch_kind: BrushBatchKind,
blend_mode: BlendMode,
textures: BatchTextures,
clip_chain_rect_index: ClipChainRectIndex,
prim_header_index: PrimitiveHeaderIndex,
clip_task_address: RenderTaskAddress,
task_relative_bounding_rect: &DeviceIntRect,
prim_cache_address: GpuCacheAddress,
scroll_id: ClipScrollNodeIndex,
task_address: RenderTaskAddress,
z: ZBufferId,
user_data: [i32; 3],
edge_flags: EdgeAaSegmentMask,
) {
let base_instance = BrushInstance {
picture_address: task_address,
prim_address: prim_cache_address,
clip_chain_rect_index,
scroll_id,
prim_header_index,
clip_task_address,
z,
segment_index: 0,
edge_flags,
brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
user_data,
};
self.batch_list.add_bounding_rect(task_relative_bounding_rect);
@ -1227,28 +1205,18 @@ impl AlphaBatchBuilder {
alpha_blend_mode: BlendMode,
non_segmented_blend_mode: BlendMode,
textures: BatchTextures,
clip_chain_rect_index: ClipChainRectIndex,
prim_header_index: PrimitiveHeaderIndex,
clip_task_address: RenderTaskAddress,
task_relative_bounding_rect: &DeviceIntRect,
prim_cache_address: GpuCacheAddress,
scroll_id: ClipScrollNodeIndex,
task_address: RenderTaskAddress,
transform_kind: TransformedRectKind,
z: ZBufferId,
render_tasks: &RenderTaskTree,
user_data: [i32; 3],
) {
let base_instance = BrushInstance {
picture_address: task_address,
prim_address: prim_cache_address,
clip_chain_rect_index,
scroll_id,
prim_header_index,
clip_task_address,
z,
segment_index: 0,
edge_flags: EdgeAaSegmentMask::all(),
brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
user_data,
};
self.batch_list.add_bounding_rect(task_relative_bounding_rect);
@ -1326,13 +1294,11 @@ fn add_gradient_tiles(
kind: BrushBatchKind,
blend_mode: BlendMode,
task_relative_bounding_rect: &DeviceIntRect,
clip_chain_rect_index: ClipChainRectIndex,
scroll_id: ClipScrollNodeIndex,
task_address: RenderTaskAddress,
clip_task_address: RenderTaskAddress,
z: ZBufferId,
gpu_cache: &GpuCache,
batch_list: &mut BatchList,
base_prim_header: &PrimitiveHeader,
prim_headers: &mut PrimitiveHeaders,
) {
batch_list.add_bounding_rect(task_relative_bounding_rect);
let batch = batch_list.get_suitable_batch(
@ -1346,24 +1312,22 @@ fn add_gradient_tiles(
let user_data = [stops_handle.as_int(gpu_cache), 0, 0];
let base_instance = BrushInstance {
picture_address: task_address,
prim_address: GpuCacheAddress::invalid(),
clip_chain_rect_index,
scroll_id,
clip_task_address,
z,
segment_index: 0,
edge_flags: EdgeAaSegmentMask::all(),
brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
user_data,
};
for tile in visible_tiles {
let prim_header = PrimitiveHeader {
specific_prim_address: gpu_cache.get_address(&tile.handle),
local_rect: tile.local_rect,
local_clip_rect: tile.local_clip_rect,
..*base_prim_header
};
let prim_header_index = prim_headers.push(&prim_header, user_data);
batch.push(PrimitiveInstance::from(
BrushInstance {
prim_address: gpu_cache.get_address(&tile.handle),
..base_instance
prim_header_index,
clip_task_address,
segment_index: 0,
edge_flags: EdgeAaSegmentMask::all(),
brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
}
));
}
@ -1783,7 +1747,7 @@ impl ClipBatcher {
) {
let instance = ClipMaskInstance {
render_task_address: task_address,
scroll_node_data_index: ClipScrollNodeIndex(0),
transform_id: TransformPaletteId::identity(),
segment: 0,
clip_data_address,
resource_address: GpuCacheAddress::invalid(),
@ -1800,12 +1764,13 @@ impl ClipBatcher {
resource_cache: &ResourceCache,
gpu_cache: &GpuCache,
clip_store: &ClipStore,
transforms: &TransformPalette,
) {
let mut coordinate_system_id = coordinate_system_id;
for work_item in clips.iter() {
let instance = ClipMaskInstance {
render_task_address: task_address,
scroll_node_data_index: work_item.scroll_node_data_index,
transform_id: transforms.get_id(work_item.transform_index),
segment: 0,
clip_data_address: GpuCacheAddress::invalid(),
resource_address: GpuCacheAddress::invalid(),

View File

@ -7,11 +7,11 @@ use api::{ImageRendering, LayoutRect, LayoutSize, LayoutPoint, LayoutVector2D, L
use api::{BoxShadowClipMode, LayoutToWorldScale, LineOrientation, LineStyle};
use border::{ensure_no_corner_overlap};
use box_shadow::{BLUR_SAMPLE_SCALE, BoxShadowClipSource, BoxShadowCacheKey};
use clip_scroll_tree::{ClipChainIndex, CoordinateSystemId};
use clip_scroll_tree::{ClipChainIndex, CoordinateSystemId, TransformIndex};
use ellipse::Ellipse;
use freelist::{FreeList, FreeListHandle, WeakFreeListHandle};
use gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks};
use gpu_types::{BoxShadowStretchMode, ClipScrollNodeIndex};
use gpu_types::{BoxShadowStretchMode};
use prim_store::{ClipData, ImageMaskData};
use render_task::to_cache_size;
use resource_cache::{ImageRequest, ResourceCache};
@ -625,7 +625,7 @@ impl Iterator for ClipChainNodeIter {
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct ClipWorkItem {
pub scroll_node_data_index: ClipScrollNodeIndex,
pub transform_index: TransformIndex,
pub clip_sources: ClipSourcesWeakHandle,
pub coordinate_system_id: CoordinateSystemId,
}

View File

@ -7,10 +7,10 @@ use api::{LayoutVector2D, LayoutTransform, PipelineId, PropertyBinding};
use api::{ScrollClamping, ScrollLocation, ScrollSensitivity, StickyOffsetBounds};
use clip::{ClipChain, ClipChainNode, ClipSourcesHandle, ClipStore, ClipWorkItem};
use clip_scroll_tree::{ClipChainIndex, ClipScrollNodeIndex, CoordinateSystemId};
use clip_scroll_tree::TransformUpdateState;
use clip_scroll_tree::{TransformUpdateState, TransformIndex};
use euclid::SideOffsets2D;
use gpu_cache::GpuCache;
use gpu_types::{ClipScrollNodeIndex as GPUClipScrollNodeIndex, ClipScrollNodeData};
use gpu_types::{TransformData, TransformPalette};
use resource_cache::ResourceCache;
use scene::SceneProperties;
use util::{LayoutToWorldFastTransform, LayoutFastTransform};
@ -46,9 +46,26 @@ impl StickyFrameInfo {
}
#[derive(Debug)]
pub enum NodeType {
pub enum SpatialNodeKind {
/// A special kind of node that adjusts its position based on the position
/// of its parent node and a given set of sticky positioning offset bounds.
/// Sticky positioned is described in the CSS Positioned Layout Module Level 3 here:
/// https://www.w3.org/TR/css-position-3/#sticky-pos
StickyFrame(StickyFrameInfo),
/// Transforms it's content, but doesn't clip it. Can also be adjusted
/// by scroll events or setting scroll offsets.
ScrollFrame(ScrollFrameInfo),
/// A reference frame establishes a new coordinate space in the tree.
ReferenceFrame(ReferenceFrameInfo),
}
#[derive(Debug)]
pub enum NodeType {
Spatial {
kind: SpatialNodeKind,
},
/// Other nodes just do clipping, but no transformation.
Clip {
@ -61,16 +78,6 @@ pub enum NodeType {
clip_chain_node: Option<ClipChainNode>,
},
/// Transforms it's content, but doesn't clip it. Can also be adjusted
/// by scroll events or setting scroll offsets.
ScrollFrame(ScrollFrameInfo),
/// A special kind of node that adjusts its position based on the position
/// of its parent node and a given set of sticky positioning offset bounds.
/// Sticky positioned is described in the CSS Positioned Layout Module Level 3 here:
/// https://www.w3.org/TR/css-position-3/#sticky-pos
StickyFrame(StickyFrameInfo),
/// An empty node, used to pad the ClipScrollTree's array of nodes so that
/// we can immediately use each assigned ClipScrollNodeIndex. After display
/// list flattening this node type should never be used.
@ -80,7 +87,7 @@ pub enum NodeType {
impl NodeType {
fn is_reference_frame(&self) -> bool {
match *self {
NodeType::ReferenceFrame(_) => true,
NodeType::Spatial { kind: SpatialNodeKind::ReferenceFrame(_), .. } => true,
_ => false,
}
}
@ -126,16 +133,18 @@ pub struct ClipScrollNode {
/// reference frame transforms.
pub coordinate_system_relative_transform: LayoutFastTransform,
/// A linear ID / index of this clip-scroll node. Used as a reference to
/// pass to shaders, to allow them to fetch a given clip-scroll node.
pub node_data_index: GPUClipScrollNodeIndex,
/// The index of the spatial node that provides positioning information for this node.
/// For reference frames, scroll and sticky frames it is a unique identfier.
/// For clip nodes, this is the nearest ancestor spatial node.
pub transform_index: TransformIndex,
}
impl ClipScrollNode {
pub fn new(
pipeline_id: PipelineId,
parent_index: Option<ClipScrollNodeIndex>,
node_type: NodeType
node_type: NodeType,
transform_index: TransformIndex,
) -> Self {
ClipScrollNode {
world_viewport_transform: LayoutToWorldFastTransform::identity(),
@ -148,12 +157,17 @@ impl ClipScrollNode {
invertible: true,
coordinate_system_id: CoordinateSystemId(0),
coordinate_system_relative_transform: LayoutFastTransform::identity(),
node_data_index: GPUClipScrollNodeIndex(0),
transform_index,
}
}
pub fn empty() -> ClipScrollNode {
Self::new(PipelineId::dummy(), None, NodeType::Empty)
Self::new(
PipelineId::dummy(),
None,
NodeType::Empty,
TransformIndex(0),
)
}
pub fn new_scroll_frame(
@ -163,18 +177,26 @@ impl ClipScrollNode {
frame_rect: &LayoutRect,
content_size: &LayoutSize,
scroll_sensitivity: ScrollSensitivity,
transform_index: TransformIndex,
) -> Self {
let node_type = NodeType::ScrollFrame(ScrollFrameInfo::new(
*frame_rect,
scroll_sensitivity,
LayoutSize::new(
(content_size.width - frame_rect.size.width).max(0.0),
(content_size.height - frame_rect.size.height).max(0.0)
),
external_id,
));
let node_type = NodeType::Spatial {
kind: SpatialNodeKind::ScrollFrame(ScrollFrameInfo::new(
*frame_rect,
scroll_sensitivity,
LayoutSize::new(
(content_size.width - frame_rect.size.width).max(0.0),
(content_size.height - frame_rect.size.height).max(0.0)
),
external_id,
)),
};
Self::new(pipeline_id, Some(parent_index), node_type)
Self::new(
pipeline_id,
Some(parent_index),
node_type,
transform_index,
)
}
pub fn new_reference_frame(
@ -183,6 +205,7 @@ impl ClipScrollNode {
source_perspective: Option<LayoutTransform>,
origin_in_parent_reference_frame: LayoutVector2D,
pipeline_id: PipelineId,
transform_index: TransformIndex,
) -> Self {
let identity = LayoutTransform::identity();
let source_perspective = source_perspective.map_or_else(
@ -194,16 +217,31 @@ impl ClipScrollNode {
origin_in_parent_reference_frame,
invertible: true,
};
Self::new(pipeline_id, parent_index, NodeType::ReferenceFrame(info))
Self::new(
pipeline_id,
parent_index,
NodeType::Spatial {
kind: SpatialNodeKind::ReferenceFrame(info),
},
transform_index,
)
}
pub fn new_sticky_frame(
parent_index: ClipScrollNodeIndex,
sticky_frame_info: StickyFrameInfo,
pipeline_id: PipelineId,
transform_index: TransformIndex,
) -> Self {
let node_type = NodeType::StickyFrame(sticky_frame_info);
Self::new(pipeline_id, Some(parent_index), node_type)
let node_type = NodeType::Spatial {
kind: SpatialNodeKind::StickyFrame(sticky_frame_info),
};
Self::new(
pipeline_id,
Some(parent_index),
node_type,
transform_index,
)
}
@ -213,7 +251,7 @@ impl ClipScrollNode {
pub fn apply_old_scrolling_state(&mut self, old_scroll_info: &ScrollFrameInfo) {
match self.node_type {
NodeType::ScrollFrame(ref mut scrolling) => {
NodeType::Spatial { kind: SpatialNodeKind::ScrollFrame(ref mut scrolling), .. } => {
*scrolling = scrolling.combine_with_old_scroll_info(old_scroll_info);
}
_ if old_scroll_info.offset != LayoutVector2D::zero() => {
@ -229,7 +267,7 @@ impl ClipScrollNode {
let scrollable_height = scrollable_size.height;
let scrolling = match self.node_type {
NodeType::ScrollFrame(ref mut scrolling) => scrolling,
NodeType::Spatial { kind: SpatialNodeKind::ScrollFrame(ref mut scrolling), .. } => scrolling,
_ => {
warn!("Tried to scroll a non-scroll node.");
return false;
@ -265,29 +303,32 @@ impl ClipScrollNode {
self.world_viewport_transform = LayoutToWorldFastTransform::identity();
}
pub fn push_gpu_node_data(&mut self, node_data: &mut Vec<ClipScrollNodeData>) {
if !self.invertible {
node_data.push(ClipScrollNodeData::invalid());
return;
}
let inv_transform = match self.world_content_transform.inverse() {
Some(inverted) => inverted.to_transform(),
None => {
node_data.push(ClipScrollNodeData::invalid());
pub fn push_gpu_data(
&mut self,
transform_palette: &mut TransformPalette,
) {
if let NodeType::Spatial { .. } = self.node_type {
if !self.invertible {
transform_palette.set(self.transform_index, TransformData::invalid());
return;
}
};
let data = ClipScrollNodeData {
transform: self.world_content_transform.into(),
inv_transform,
transform_kind: self.transform_kind as u32 as f32,
padding: [0.0; 3],
};
let inv_transform = match self.world_content_transform.inverse() {
Some(inverted) => inverted.to_transform(),
None => {
transform_palette.set(self.transform_index, TransformData::invalid());
return;
}
};
// Write the data that will be made available to the GPU for this node.
node_data.push(data);
let data = TransformData {
transform: self.world_content_transform.into(),
inv_transform,
};
// Write the data that will be made available to the GPU for this node.
transform_palette.set(self.transform_index, data);
}
}
pub fn update(
@ -320,7 +361,7 @@ impl ClipScrollNode {
// For non-reference-frames we assume that they will produce only additional
// translations which should be invertible.
match self.node_type {
NodeType::ReferenceFrame(info) if !info.invertible => {
NodeType::Spatial { kind: SpatialNodeKind::ReferenceFrame(info), .. } if !info.invertible => {
self.mark_uninvertible();
return;
}
@ -347,7 +388,7 @@ impl ClipScrollNode {
clip_chains: &mut [ClipChain],
) {
let (clip_sources_handle, clip_chain_index, stored_clip_chain_node) = match self.node_type {
NodeType::Clip { ref handle, clip_chain_index, ref mut clip_chain_node } =>
NodeType::Clip { ref handle, clip_chain_index, ref mut clip_chain_node, .. } =>
(handle, clip_chain_index, clip_chain_node),
_ => {
self.invertible = true;
@ -378,7 +419,7 @@ impl ClipScrollNode {
let new_node = ClipChainNode {
work_item: ClipWorkItem {
scroll_node_data_index: self.node_data_index,
transform_index: self.transform_index,
clip_sources: clip_sources_handle.weak(),
coordinate_system_id: state.current_coordinate_system_id,
},
@ -442,9 +483,8 @@ impl ClipScrollNode {
self.coordinate_system_relative_transform =
state.coordinate_system_relative_transform.offset(added_offset);
match self.node_type {
NodeType::StickyFrame(ref mut info) => info.current_offset = sticky_offset,
_ => {},
if let NodeType::Spatial { kind: SpatialNodeKind::StickyFrame(ref mut info), .. } = self.node_type {
info.current_offset = sticky_offset;
}
self.coordinate_system_id = state.current_coordinate_system_id;
@ -457,7 +497,7 @@ impl ClipScrollNode {
scene_properties: &SceneProperties,
) {
let info = match self.node_type {
NodeType::ReferenceFrame(ref mut info) => info,
NodeType::Spatial { kind: SpatialNodeKind::ReferenceFrame(ref mut info), .. } => info,
_ => unreachable!("Called update_transform_for_reference_frame on non-ReferenceFrame"),
};
@ -505,7 +545,7 @@ impl ClipScrollNode {
viewport_rect: &LayoutRect,
) -> LayoutVector2D {
let info = match self.node_type {
NodeType::StickyFrame(ref info) => info,
NodeType::Spatial { kind: SpatialNodeKind::StickyFrame(ref info), .. } => info,
_ => return LayoutVector2D::zero(),
};
@ -619,45 +659,48 @@ impl ClipScrollNode {
// between us and the parent reference frame. If we are a reference frame,
// we need to reset both these values.
match self.node_type {
NodeType::ReferenceFrame(ref info) => {
state.parent_reference_frame_transform = self.world_viewport_transform;
state.parent_accumulated_scroll_offset = LayoutVector2D::zero();
state.coordinate_system_relative_transform =
self.coordinate_system_relative_transform.clone();
let translation = -info.origin_in_parent_reference_frame;
state.nearest_scrolling_ancestor_viewport =
state.nearest_scrolling_ancestor_viewport
.translate(&translation);
NodeType::Spatial { ref kind, .. } => {
match *kind {
SpatialNodeKind::StickyFrame(ref info) => {
// We don't translate the combined rect by the sticky offset, because sticky
// offsets actually adjust the node position itself, whereas scroll offsets
// only apply to contents inside the node.
state.parent_accumulated_scroll_offset =
info.current_offset + state.parent_accumulated_scroll_offset;
}
SpatialNodeKind::ScrollFrame(ref scrolling) => {
state.parent_accumulated_scroll_offset =
scrolling.offset + state.parent_accumulated_scroll_offset;
state.nearest_scrolling_ancestor_offset = scrolling.offset;
state.nearest_scrolling_ancestor_viewport = scrolling.viewport_rect;
}
SpatialNodeKind::ReferenceFrame(ref info) => {
state.parent_reference_frame_transform = self.world_viewport_transform;
state.parent_accumulated_scroll_offset = LayoutVector2D::zero();
state.coordinate_system_relative_transform =
self.coordinate_system_relative_transform.clone();
let translation = -info.origin_in_parent_reference_frame;
state.nearest_scrolling_ancestor_viewport =
state.nearest_scrolling_ancestor_viewport
.translate(&translation);
}
}
}
NodeType::Clip{ .. } => { }
NodeType::ScrollFrame(ref scrolling) => {
state.parent_accumulated_scroll_offset =
scrolling.offset + state.parent_accumulated_scroll_offset;
state.nearest_scrolling_ancestor_offset = scrolling.offset;
state.nearest_scrolling_ancestor_viewport = scrolling.viewport_rect;
}
NodeType::StickyFrame(ref info) => {
// We don't translate the combined rect by the sticky offset, because sticky
// offsets actually adjust the node position itself, whereas scroll offsets
// only apply to contents inside the node.
state.parent_accumulated_scroll_offset =
info.current_offset + state.parent_accumulated_scroll_offset;
}
NodeType::Empty => unreachable!("Empty node remaining in ClipScrollTree."),
}
}
pub fn scrollable_size(&self) -> LayoutSize {
match self.node_type {
NodeType:: ScrollFrame(state) => state.scrollable_size,
NodeType::Spatial { kind: SpatialNodeKind::ScrollFrame(state), .. } => state.scrollable_size,
_ => LayoutSize::zero(),
}
}
pub fn scroll(&mut self, scroll_location: ScrollLocation) -> bool {
let scrolling = match self.node_type {
NodeType::ScrollFrame(ref mut scrolling) => scrolling,
NodeType::Spatial { kind: SpatialNodeKind::ScrollFrame(ref mut scrolling), .. } => scrolling,
_ => return false,
};
@ -707,14 +750,14 @@ impl ClipScrollNode {
pub fn scroll_offset(&self) -> LayoutVector2D {
match self.node_type {
NodeType::ScrollFrame(ref scrolling) => scrolling.offset,
NodeType::Spatial { kind: SpatialNodeKind::ScrollFrame(ref scrolling), .. } => scrolling.offset,
_ => LayoutVector2D::zero(),
}
}
pub fn matches_external_id(&self, external_id: ExternalScrollId) -> bool {
match self.node_type {
NodeType::ScrollFrame(info) if info.external_id == Some(external_id) => true,
NodeType::Spatial { kind: SpatialNodeKind::ScrollFrame(info), .. } if info.external_id == Some(external_id) => true,
_ => false,
}
}

View File

@ -4,11 +4,11 @@
use api::{DeviceIntRect, DevicePixelScale, ExternalScrollId, LayoutPoint, LayoutRect, LayoutVector2D};
use api::{PipelineId, ScrollClamping, ScrollLocation, ScrollNodeState};
use api::WorldPoint;
use api::{LayoutSize, LayoutTransform, PropertyBinding, ScrollSensitivity, WorldPoint};
use clip::{ClipChain, ClipSourcesHandle, ClipStore};
use clip_scroll_node::{ClipScrollNode, NodeType, ScrollFrameInfo, StickyFrameInfo};
use clip_scroll_node::{ClipScrollNode, NodeType, SpatialNodeKind, ScrollFrameInfo, StickyFrameInfo};
use gpu_cache::GpuCache;
use gpu_types::{ClipScrollNodeIndex as GPUClipScrollNodeIndex, ClipScrollNodeData};
use gpu_types::TransformPalette;
use internal_types::{FastHashMap, FastHashSet};
use print_tree::{PrintTree, PrintTreePrinter};
use resource_cache::ResourceCache;
@ -29,6 +29,15 @@ pub struct CoordinateSystemId(pub u32);
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
pub struct ClipScrollNodeIndex(pub usize);
// Used to index the smaller subset of nodes in the CST that define
// new transform / positioning.
// TODO(gw): In the future if we split the CST into a positioning and
// clipping tree, this can be tidied up a bit.
#[derive(Copy, Debug, Clone, PartialEq)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct TransformIndex(pub u32);
const ROOT_REFERENCE_FRAME_INDEX: ClipScrollNodeIndex = ClipScrollNodeIndex(0);
const TOPMOST_SCROLL_NODE_INDEX: ClipScrollNodeIndex = ClipScrollNodeIndex(1);
@ -70,14 +79,13 @@ pub struct ClipScrollTree {
pub pending_scroll_offsets: FastHashMap<ExternalScrollId, (LayoutPoint, ScrollClamping)>,
/// The current frame id, used for giving a unique id to all new dynamically
/// added frames and clips. The ClipScrollTree increments this by one every
/// time a new dynamic frame is created.
current_new_node_item: u64,
/// A set of pipelines which should be discarded the next time this
/// tree is drained.
pub pipelines_to_discard: FastHashSet<PipelineId>,
/// The number of nodes in the CST that are spatial. Currently, this is all
/// nodes that are not clip nodes.
spatial_node_count: usize,
}
#[derive(Clone)]
@ -112,8 +120,8 @@ impl ClipScrollTree {
clip_chains_descriptors: Vec::new(),
clip_chains: vec![ClipChain::empty(&DeviceIntRect::zero())],
pending_scroll_offsets: FastHashMap::default(),
current_new_node_item: 1,
pipelines_to_discard: FastHashSet::default(),
spatial_node_count: 0,
}
}
@ -136,7 +144,7 @@ impl ClipScrollTree {
pub fn get_scroll_node_state(&self) -> Vec<ScrollNodeState> {
let mut result = vec![];
for node in &self.nodes {
if let NodeType::ScrollFrame(info) = node.node_type {
if let NodeType::Spatial { kind: SpatialNodeKind::ScrollFrame(info), .. } = node.node_type {
if let Some(id) = info.external_id {
result.push(ScrollNodeState { id, scroll_offset: info.offset })
}
@ -146,8 +154,6 @@ impl ClipScrollTree {
}
pub fn drain(&mut self) -> ScrollStates {
self.current_new_node_item = 1;
let mut scroll_states = FastHashMap::default();
for old_node in &mut self.nodes.drain(..) {
if self.pipelines_to_discard.contains(&old_node.pipeline_id) {
@ -155,13 +161,14 @@ impl ClipScrollTree {
}
match old_node.node_type {
NodeType::ScrollFrame(info) if info.external_id.is_some() => {
NodeType::Spatial { kind: SpatialNodeKind::ScrollFrame(info), .. } if info.external_id.is_some() => {
scroll_states.insert(info.external_id.unwrap(), info);
}
_ => {}
}
}
self.spatial_node_count = 0;
self.pipelines_to_discard.clear();
self.clip_chains = vec![ClipChain::empty(&DeviceIntRect::zero())];
self.clip_chains_descriptors.clear();
@ -195,7 +202,7 @@ impl ClipScrollTree {
let node = &self.nodes[index.0];
match node.node_type {
NodeType::ScrollFrame(state) if state.sensitive_to_input_events() => index,
NodeType::Spatial { kind: SpatialNodeKind::ScrollFrame(state), .. } if state.sensitive_to_input_events() => index,
_ => self.find_nearest_scrolling_ancestor(node.parent)
}
}
@ -220,40 +227,41 @@ impl ClipScrollTree {
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
pan: WorldPoint,
node_data: &mut Vec<ClipScrollNodeData>,
scene_properties: &SceneProperties,
) {
if self.nodes.is_empty() {
return;
) -> TransformPalette {
let mut transform_palette = TransformPalette::new(self.spatial_node_count);
if !self.nodes.is_empty() {
self.clip_chains[0] = ClipChain::empty(screen_rect);
let root_reference_frame_index = self.root_reference_frame_index();
let mut state = TransformUpdateState {
parent_reference_frame_transform: LayoutVector2D::new(pan.x, pan.y).into(),
parent_accumulated_scroll_offset: LayoutVector2D::zero(),
nearest_scrolling_ancestor_offset: LayoutVector2D::zero(),
nearest_scrolling_ancestor_viewport: LayoutRect::zero(),
parent_clip_chain_index: ClipChainIndex(0),
current_coordinate_system_id: CoordinateSystemId::root(),
coordinate_system_relative_transform: LayoutFastTransform::identity(),
invertible: true,
};
let mut next_coordinate_system_id = state.current_coordinate_system_id.next();
self.update_node(
root_reference_frame_index,
&mut state,
&mut next_coordinate_system_id,
device_pixel_scale,
clip_store,
resource_cache,
gpu_cache,
&mut transform_palette,
scene_properties,
);
self.build_clip_chains(screen_rect);
}
self.clip_chains[0] = ClipChain::empty(screen_rect);
let root_reference_frame_index = self.root_reference_frame_index();
let mut state = TransformUpdateState {
parent_reference_frame_transform: LayoutVector2D::new(pan.x, pan.y).into(),
parent_accumulated_scroll_offset: LayoutVector2D::zero(),
nearest_scrolling_ancestor_offset: LayoutVector2D::zero(),
nearest_scrolling_ancestor_viewport: LayoutRect::zero(),
parent_clip_chain_index: ClipChainIndex(0),
current_coordinate_system_id: CoordinateSystemId::root(),
coordinate_system_relative_transform: LayoutFastTransform::identity(),
invertible: true,
};
let mut next_coordinate_system_id = state.current_coordinate_system_id.next();
self.update_node(
root_reference_frame_index,
&mut state,
&mut next_coordinate_system_id,
device_pixel_scale,
clip_store,
resource_cache,
gpu_cache,
node_data,
scene_properties,
);
self.build_clip_chains(screen_rect);
transform_palette
}
fn update_node(
@ -265,7 +273,7 @@ impl ClipScrollTree {
clip_store: &mut ClipStore,
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
gpu_node_data: &mut Vec<ClipScrollNodeData>,
transform_palette: &mut TransformPalette,
scene_properties: &SceneProperties,
) {
// TODO(gw): This is an ugly borrow check workaround to clone these.
@ -277,9 +285,6 @@ impl ClipScrollTree {
None => return,
};
// We set this early so that we can use it to populate the ClipChain.
node.node_data_index = GPUClipScrollNodeIndex(gpu_node_data.len() as u32);
node.update(
&mut state,
next_coordinate_system_id,
@ -291,7 +296,7 @@ impl ClipScrollTree {
&mut self.clip_chains,
);
node.push_gpu_node_data(gpu_node_data);
node.push_gpu_data(transform_palette);
if node.children.is_empty() {
return;
@ -310,7 +315,7 @@ impl ClipScrollTree {
clip_store,
resource_cache,
gpu_cache,
gpu_node_data,
transform_palette,
scene_properties,
);
}
@ -346,7 +351,7 @@ impl ClipScrollTree {
pub fn finalize_and_apply_pending_scroll_offsets(&mut self, old_states: ScrollStates) {
for node in &mut self.nodes {
let external_id = match node.node_type {
NodeType::ScrollFrame(ScrollFrameInfo { external_id: Some(id), ..} ) => id,
NodeType::Spatial { kind: SpatialNodeKind::ScrollFrame(ScrollFrameInfo { external_id: Some(id), ..} ), .. } => id,
_ => continue,
};
@ -360,6 +365,13 @@ impl ClipScrollTree {
}
}
// Generate the next valid TransformIndex for the CST.
fn next_transform_index(&mut self) -> TransformIndex {
let transform_index = TransformIndex(self.spatial_node_count as u32);
self.spatial_node_count += 1;
transform_index
}
pub fn add_clip_node(
&mut self,
index: ClipScrollNodeIndex,
@ -368,12 +380,65 @@ impl ClipScrollTree {
pipeline_id: PipelineId,
) -> ClipChainIndex {
let clip_chain_index = self.allocate_clip_chain();
let node_type = NodeType::Clip { handle, clip_chain_index, clip_chain_node: None };
let node = ClipScrollNode::new(pipeline_id, Some(parent_index), node_type);
let transform_index = self.nodes[parent_index.0].transform_index;
let node_type = NodeType::Clip {
handle,
clip_chain_index,
clip_chain_node: None,
};
let node = ClipScrollNode::new(
pipeline_id,
Some(parent_index),
node_type,
transform_index,
);
self.add_node(node, index);
clip_chain_index
}
pub fn add_scroll_frame(
&mut self,
index: ClipScrollNodeIndex,
parent_index: ClipScrollNodeIndex,
external_id: Option<ExternalScrollId>,
pipeline_id: PipelineId,
frame_rect: &LayoutRect,
content_size: &LayoutSize,
scroll_sensitivity: ScrollSensitivity,
) {
let node = ClipScrollNode::new_scroll_frame(
pipeline_id,
parent_index,
external_id,
frame_rect,
content_size,
scroll_sensitivity,
self.next_transform_index(),
);
self.add_node(node, index);
}
pub fn add_reference_frame(
&mut self,
index: ClipScrollNodeIndex,
parent_index: Option<ClipScrollNodeIndex>,
source_transform: Option<PropertyBinding<LayoutTransform>>,
source_perspective: Option<LayoutTransform>,
origin_in_parent_reference_frame: LayoutVector2D,
pipeline_id: PipelineId,
) {
let node = ClipScrollNode::new_reference_frame(
parent_index,
source_transform,
source_perspective,
origin_in_parent_reference_frame,
pipeline_id,
self.next_transform_index(),
);
self.add_node(node, index);
}
pub fn add_sticky_frame(
&mut self,
index: ClipScrollNodeIndex,
@ -381,7 +446,12 @@ impl ClipScrollTree {
sticky_frame_info: StickyFrameInfo,
pipeline_id: PipelineId,
) {
let node = ClipScrollNode::new_sticky_frame(parent_index, sticky_frame_info, pipeline_id);
let node = ClipScrollNode::new_sticky_frame(
parent_index,
sticky_frame_info,
pipeline_id,
self.next_transform_index(),
);
self.add_node(node, index);
}
@ -436,6 +506,26 @@ impl ClipScrollTree {
) {
let node = &self.nodes[index.0];
match node.node_type {
NodeType::Spatial { ref kind, .. } => {
match *kind {
SpatialNodeKind::StickyFrame(ref sticky_frame_info) => {
pt.new_level(format!("StickyFrame"));
pt.add_item(format!("index: {:?}", index));
pt.add_item(format!("sticky info: {:?}", sticky_frame_info));
}
SpatialNodeKind::ScrollFrame(scrolling_info) => {
pt.new_level(format!("ScrollFrame"));
pt.add_item(format!("index: {:?}", index));
pt.add_item(format!("viewport: {:?}", scrolling_info.viewport_rect));
pt.add_item(format!("scrollable_size: {:?}", scrolling_info.scrollable_size));
pt.add_item(format!("scroll offset: {:?}", scrolling_info.offset));
}
SpatialNodeKind::ReferenceFrame(ref info) => {
pt.new_level(format!("ReferenceFrame {:?}", info.resolved_transform));
pt.add_item(format!("index: {:?}", index));
}
}
}
NodeType::Clip { ref handle, .. } => {
pt.new_level("Clip".to_owned());
@ -447,22 +537,6 @@ impl ClipScrollTree {
}
pt.end_level();
}
NodeType::ReferenceFrame(ref info) => {
pt.new_level(format!("ReferenceFrame {:?}", info.resolved_transform));
pt.add_item(format!("index: {:?}", index));
}
NodeType::ScrollFrame(scrolling_info) => {
pt.new_level(format!("ScrollFrame"));
pt.add_item(format!("index: {:?}", index));
pt.add_item(format!("viewport: {:?}", scrolling_info.viewport_rect));
pt.add_item(format!("scrollable_size: {:?}", scrolling_info.scrollable_size));
pt.add_item(format!("scroll offset: {:?}", scrolling_info.offset));
}
NodeType::StickyFrame(ref sticky_frame_info) => {
pt.new_level(format!("StickyFrame"));
pt.add_item(format!("index: {:?}", index));
pt.add_item(format!("sticky info: {:?}", sticky_frame_info));
}
NodeType::Empty => unreachable!("Empty node remaining in ClipScrollTree."),
}

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 super::shader_source;
use super::super::shader_source;
use api::{ColorF, ImageFormat};
use api::{DeviceIntPoint, DeviceIntRect, DeviceUintRect, DeviceUintSize};
use api::TextureTarget;
@ -2234,6 +2234,11 @@ impl Device {
external: gl::RGBA,
pixel_type: gl::FLOAT,
},
ImageFormat::RGBAI32 => FormatDesc {
internal: gl::RGBA32I as _,
external: gl::RGBA_INTEGER,
pixel_type: gl::INT,
},
ImageFormat::RG8 => FormatDesc {
internal: gl::RG8 as _,
external: gl::RG,
@ -2360,6 +2365,7 @@ impl<'a> UploadTarget<'a> {
ImageFormat::BGRA8 => (self.bgra_format, 4, gl::UNSIGNED_BYTE),
ImageFormat::RG8 => (gl::RG, 2, gl::UNSIGNED_BYTE),
ImageFormat::RGBAF32 => (gl::RGBA, 16, gl::FLOAT),
ImageFormat::RGBAI32 => (gl::RGBA_INTEGER, 16, gl::INT),
};
let row_length = match chunk.stride {

View File

@ -0,0 +1,7 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
mod gl;
pub use self::gl::*;

View File

@ -14,10 +14,10 @@ use api::{PropertyBinding, ReferenceFrame, RepeatMode, ScrollFrameDisplayItem, S
use api::{Shadow, SpecificDisplayItem, StackingContext, StickyFrameDisplayItem, TexelRect};
use api::{TransformStyle, YuvColorSpace, YuvData};
use clip::{ClipRegion, ClipSource, ClipSources, ClipStore};
use clip_scroll_node::{ClipScrollNode, NodeType, StickyFrameInfo};
use clip_scroll_node::{NodeType, SpatialNodeKind, StickyFrameInfo};
use clip_scroll_tree::{ClipChainIndex, ClipScrollNodeIndex, ClipScrollTree};
use euclid::{SideOffsets2D, vec2};
use frame_builder::{FrameBuilder, FrameBuilderConfig};
use euclid::vec2;
use frame_builder::{ChasePrimitive, FrameBuilder, FrameBuilderConfig};
use glyph_rasterizer::FontInstance;
use gpu_cache::GpuCacheHandle;
use gpu_types::BrushFlags;
@ -628,8 +628,7 @@ impl<'a> DisplayListFlattener<'a> {
);
}
SpecificDisplayItem::Gradient(ref info) => {
self.add_gradient(
clip_and_scroll,
let brush_kind = self.create_brush_kind_for_gradient(
&prim_info,
info.gradient.start_point,
info.gradient.end_point,
@ -638,10 +637,11 @@ impl<'a> DisplayListFlattener<'a> {
info.tile_size,
info.tile_spacing,
);
let prim = PrimitiveContainer::Brush(BrushPrimitive::new(brush_kind, None));
self.add_primitive(clip_and_scroll, &prim_info, Vec::new(), prim);
}
SpecificDisplayItem::RadialGradient(ref info) => {
self.add_radial_gradient(
clip_and_scroll,
let brush_kind = self.create_brush_kind_for_radial_gradient(
&prim_info,
info.gradient.center,
info.gradient.start_offset * info.gradient.radius.width,
@ -652,6 +652,8 @@ impl<'a> DisplayListFlattener<'a> {
info.tile_size,
info.tile_spacing,
);
let prim = PrimitiveContainer::Brush(BrushPrimitive::new(brush_kind, None));
self.add_primitive(clip_and_scroll, &prim_info, Vec::new(), prim);
}
SpecificDisplayItem::BoxShadow(ref box_shadow_info) => {
let bounds = box_shadow_info
@ -875,6 +877,10 @@ impl<'a> DisplayListFlattener<'a> {
if container.is_visible() {
let prim_index = self.create_primitive(info, clip_sources, container);
if cfg!(debug_assertions) && ChasePrimitive::LocalRect(info.rect) == self.config.chase_primitive {
println!("Chasing {:?}", prim_index);
self.prim_store.chase_id = Some(prim_index);
}
self.add_primitive_to_hit_testing_list(info, clip_and_scroll);
self.add_primitive_to_draw_list(prim_index, clip_and_scroll);
}
@ -1197,14 +1203,15 @@ impl<'a> DisplayListFlattener<'a> {
origin_in_parent_reference_frame: LayoutVector2D,
) -> ClipScrollNodeIndex {
let index = self.id_to_index_mapper.get_node_index(reference_frame_id);
let node = ClipScrollNode::new_reference_frame(
parent_id.map(|id| self.id_to_index_mapper.get_node_index(id)),
let parent_index = parent_id.map(|id| self.id_to_index_mapper.get_node_index(id));
self.clip_scroll_tree.add_reference_frame(
index,
parent_index,
source_transform,
source_perspective,
origin_in_parent_reference_frame,
pipeline_id,
);
self.clip_scroll_tree.add_node(node, index);
self.reference_frame_stack.push((reference_frame_id, index));
match parent_id {
@ -1227,7 +1234,7 @@ impl<'a> DisplayListFlattener<'a> {
let viewport_offset = (inner_rect.origin.to_vector().to_f32() / device_pixel_scale).round();
let root_id = self.clip_scroll_tree.root_reference_frame_index();
let root_node = &mut self.clip_scroll_tree.nodes[root_id.0];
if let NodeType::ReferenceFrame(ref mut info) = root_node.node_type {
if let NodeType::Spatial { kind: SpatialNodeKind::ReferenceFrame(ref mut info), .. } = root_node.node_type {
info.resolved_transform =
LayoutVector2D::new(viewport_offset.x, viewport_offset.y).into();
}
@ -1290,16 +1297,15 @@ impl<'a> DisplayListFlattener<'a> {
scroll_sensitivity: ScrollSensitivity,
) -> ClipScrollNodeIndex {
let node_index = self.id_to_index_mapper.get_node_index(new_node_id);
let node = ClipScrollNode::new_scroll_frame(
pipeline_id,
self.clip_scroll_tree.add_scroll_frame(
node_index,
self.id_to_index_mapper.get_node_index(parent_id),
external_id,
pipeline_id,
frame_rect,
content_size,
scroll_sensitivity,
);
self.clip_scroll_tree.add_node(node, node_index);
self.id_to_index_mapper.map_to_parent_clip_chain(new_node_id, &parent_id);
node_index
}
@ -1492,51 +1498,6 @@ impl<'a> DisplayListFlattener<'a> {
gradient_stops: ItemRange<GradientStop>,
) {
let rect = info.rect;
let create_segments = |outset: SideOffsets2D<f32>| {
// Calculate the modified rect as specific by border-image-outset
let origin = LayoutPoint::new(rect.origin.x - outset.left, rect.origin.y - outset.top);
let size = LayoutSize::new(
rect.size.width + outset.left + outset.right,
rect.size.height + outset.top + outset.bottom,
);
let rect = LayoutRect::new(origin, size);
let tl_outer = LayoutPoint::new(rect.origin.x, rect.origin.y);
let tl_inner = tl_outer + vec2(border_item.widths.left, border_item.widths.top);
let tr_outer = LayoutPoint::new(rect.origin.x + rect.size.width, rect.origin.y);
let tr_inner = tr_outer + vec2(-border_item.widths.right, border_item.widths.top);
let bl_outer = LayoutPoint::new(rect.origin.x, rect.origin.y + rect.size.height);
let bl_inner = bl_outer + vec2(border_item.widths.left, -border_item.widths.bottom);
let br_outer = LayoutPoint::new(
rect.origin.x + rect.size.width,
rect.origin.y + rect.size.height,
);
let br_inner = br_outer - vec2(border_item.widths.right, border_item.widths.bottom);
// Build the list of gradient segments
vec![
// Top left
LayoutRect::from_floats(tl_outer.x, tl_outer.y, tl_inner.x, tl_inner.y),
// Top right
LayoutRect::from_floats(tr_inner.x, tr_outer.y, tr_outer.x, tr_inner.y),
// Bottom right
LayoutRect::from_floats(br_inner.x, br_inner.y, br_outer.x, br_outer.y),
// Bottom left
LayoutRect::from_floats(bl_outer.x, bl_inner.y, bl_inner.x, bl_outer.y),
// Top
LayoutRect::from_floats(tl_inner.x, tl_outer.y, tr_inner.x, tl_inner.y),
// Bottom
LayoutRect::from_floats(bl_inner.x, bl_inner.y, br_inner.x, bl_outer.y),
// Left
LayoutRect::from_floats(tl_outer.x, tl_inner.y, tl_inner.x, bl_inner.y),
// Right
LayoutRect::from_floats(tr_inner.x, tr_inner.y, br_outer.x, br_inner.y),
]
};
match border_item.details {
BorderDetails::NinePatch(ref border) => {
// Calculate the modified rect as specific by border-image-outset
@ -1701,70 +1662,55 @@ impl<'a> DisplayListFlattener<'a> {
clip_mask_kind: BrushClipMaskKind::Unknown,
};
let prim = PrimitiveContainer::Brush(match border.source {
let brush_kind = match border.source {
NinePatchBorderSource::Image(image_key) => {
let source = BorderSource::Image(ImageRequest {
key: image_key,
rendering: ImageRendering::Auto,
tile: None,
});
BrushPrimitive::new(
BrushKind::Border {
source
},
Some(descriptor),
BrushKind::Border {
source: BorderSource::Image(ImageRequest {
key: image_key,
rendering: ImageRendering::Auto,
tile: None,
})
}
}
NinePatchBorderSource::Gradient(gradient) => {
self.create_brush_kind_for_gradient(
&info,
gradient.start_point,
gradient.end_point,
gradient_stops,
gradient.extend_mode,
LayoutSize::new(border.height as f32, border.width as f32),
LayoutSize::zero(),
)
}
});
NinePatchBorderSource::RadialGradient(gradient) => {
self.create_brush_kind_for_radial_gradient(
&info,
gradient.center,
gradient.start_offset * gradient.radius.width,
gradient.end_offset * gradient.radius.width,
gradient.radius.width / gradient.radius.height,
gradient_stops,
gradient.extend_mode,
LayoutSize::new(border.height as f32, border.width as f32),
LayoutSize::zero(),
)
}
};
let prim = PrimitiveContainer::Brush(
BrushPrimitive::new(brush_kind, Some(descriptor))
);
self.add_primitive(clip_and_scroll, info, Vec::new(), prim);
}
BorderDetails::Normal(ref border) => {
self.add_normal_border(info, border, &border_item.widths, clip_and_scroll);
}
BorderDetails::Gradient(ref border) => for segment in create_segments(border.outset) {
let segment_rel = segment.origin - rect.origin;
let mut info = info.clone();
info.rect = segment;
self.add_gradient(
clip_and_scroll,
&info,
border.gradient.start_point - segment_rel,
border.gradient.end_point - segment_rel,
gradient_stops,
border.gradient.extend_mode,
segment.size,
LayoutSize::zero(),
);
},
BorderDetails::RadialGradient(ref border) => {
for segment in create_segments(border.outset) {
let segment_rel = segment.origin - rect.origin;
let mut info = info.clone();
info.rect = segment;
self.add_radial_gradient(
clip_and_scroll,
&info,
border.gradient.center - segment_rel,
border.gradient.start_offset * border.gradient.radius.width,
border.gradient.end_offset * border.gradient.radius.width,
border.gradient.radius.width / border.gradient.radius.height,
gradient_stops,
border.gradient.extend_mode,
segment.size,
LayoutSize::zero(),
);
}
}
}
}
pub fn add_gradient(
pub fn create_brush_kind_for_gradient(
&mut self,
clip_and_scroll: ScrollNodeAndClipChain,
info: &LayoutPrimitiveInfo,
start_point: LayoutPoint,
end_point: LayoutPoint,
@ -1772,13 +1718,9 @@ impl<'a> DisplayListFlattener<'a> {
extend_mode: ExtendMode,
stretch_size: LayoutSize,
mut tile_spacing: LayoutSize,
) {
) -> BrushKind {
let mut prim_rect = info.rect;
simplify_repeated_primitive(&stretch_size, &mut tile_spacing, &mut prim_rect);
let info = LayoutPrimitiveInfo {
rect: prim_rect,
.. *info
};
// Try to ensure that if the gradient is specified in reverse, then so long as the stops
// are also supplied in reverse that the rendered result will be equivalent. To do this,
@ -1798,29 +1740,21 @@ impl<'a> DisplayListFlattener<'a> {
(start_point, end_point)
};
let prim = BrushPrimitive::new(
BrushKind::LinearGradient {
stops_range: stops,
extend_mode,
reverse_stops,
start_point: sp,
end_point: ep,
stops_handle: GpuCacheHandle::new(),
stretch_size,
tile_spacing,
visible_tiles: Vec::new(),
},
None,
);
let prim = PrimitiveContainer::Brush(prim);
self.add_primitive(clip_and_scroll, &info, Vec::new(), prim);
BrushKind::LinearGradient {
stops_range: stops,
extend_mode,
reverse_stops,
start_point: sp,
end_point: ep,
stops_handle: GpuCacheHandle::new(),
stretch_size,
tile_spacing,
visible_tiles: Vec::new(),
}
}
pub fn add_radial_gradient(
pub fn create_brush_kind_for_radial_gradient(
&mut self,
clip_and_scroll: ScrollNodeAndClipChain,
info: &LayoutPrimitiveInfo,
center: LayoutPoint,
start_radius: f32,
@ -1830,36 +1764,22 @@ impl<'a> DisplayListFlattener<'a> {
extend_mode: ExtendMode,
stretch_size: LayoutSize,
mut tile_spacing: LayoutSize,
) {
) -> BrushKind {
let mut prim_rect = info.rect;
simplify_repeated_primitive(&stretch_size, &mut tile_spacing, &mut prim_rect);
let info = LayoutPrimitiveInfo {
rect: prim_rect,
.. *info
};
let prim = BrushPrimitive::new(
BrushKind::RadialGradient {
stops_range: stops,
extend_mode,
center,
start_radius,
end_radius,
ratio_xy,
stops_handle: GpuCacheHandle::new(),
stretch_size,
tile_spacing,
visible_tiles: Vec::new(),
},
None,
);
self.add_primitive(
clip_and_scroll,
&info,
Vec::new(),
PrimitiveContainer::Brush(prim),
);
BrushKind::RadialGradient {
stops_range: stops,
extend_mode,
center,
start_radius,
end_radius,
ratio_xy,
stops_handle: GpuCacheHandle::new(),
stretch_size,
tile_spacing,
visible_tiles: Vec::new(),
}
}
pub fn add_text(

View File

@ -4,13 +4,13 @@
use api::{BuiltDisplayList, ColorF, DeviceIntPoint, DeviceIntRect, DevicePixelScale};
use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, DocumentLayer, FontRenderMode};
use api::{LayoutRect, LayoutSize, PipelineId, WorldPoint};
use api::{LayoutPoint, LayoutRect, LayoutSize, PipelineId, WorldPoint};
use clip::{ClipChain, ClipStore};
use clip_scroll_node::{ClipScrollNode};
use clip_scroll_tree::{ClipScrollNodeIndex, ClipScrollTree};
use display_list_flattener::{DisplayListFlattener};
use gpu_cache::GpuCache;
use gpu_types::{ClipChainRectIndex, ClipScrollNodeData, UvRectKind};
use gpu_types::{PrimitiveHeaders, TransformData, UvRectKind};
use hit_test::{HitTester, HitTestingRun};
use internal_types::{FastHashMap};
use picture::PictureSurface;
@ -24,7 +24,22 @@ use std::{mem, f32};
use std::sync::Arc;
use tiling::{Frame, RenderPass, RenderPassKind, RenderTargetContext};
use tiling::{ScrollbarPrimitive, SpecialRenderPasses};
use util::{self, MaxRect, WorldToLayoutFastTransform};
use util::{self, WorldToLayoutFastTransform};
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum ChasePrimitive {
Nothing,
LocalRect(LayoutRect),
}
impl Default for ChasePrimitive {
fn default() -> Self {
ChasePrimitive::Nothing
}
}
#[derive(Clone, Copy)]
#[cfg_attr(feature = "capture", derive(Serialize))]
@ -34,6 +49,7 @@ pub struct FrameBuilderConfig {
pub default_font_render_mode: FontRenderMode,
pub dual_source_blending_is_supported: bool,
pub dual_source_blending_is_enabled: bool,
pub chase_primitive: ChasePrimitive,
}
/// A builder structure for `tiling::Frame`
@ -56,14 +72,14 @@ pub struct FrameBuildingContext<'a> {
pub pipelines: &'a FastHashMap<PipelineId, Arc<ScenePipeline>>,
pub screen_rect: DeviceIntRect,
pub clip_scroll_tree: &'a ClipScrollTree,
pub node_data: &'a [ClipScrollNodeData],
pub transforms: &'a [TransformData],
pub max_local_clip: LayoutRect,
}
pub struct FrameBuildingState<'a> {
pub render_tasks: &'a mut RenderTaskTree,
pub profile_counters: &'a mut FrameProfileCounters,
pub clip_store: &'a mut ClipStore,
pub local_clip_rects: &'a mut Vec<LayoutRect>,
pub resource_cache: &'a mut ResourceCache,
pub gpu_cache: &'a mut GpuCache,
pub special_render_passes: &'a mut SpecialRenderPasses,
@ -99,19 +115,19 @@ impl PictureState {
pub struct PrimitiveRunContext<'a> {
pub clip_chain: &'a ClipChain,
pub scroll_node: &'a ClipScrollNode,
pub clip_chain_rect_index: ClipChainRectIndex,
pub local_clip_rect: LayoutRect,
}
impl<'a> PrimitiveRunContext<'a> {
pub fn new(
clip_chain: &'a ClipChain,
scroll_node: &'a ClipScrollNode,
clip_chain_rect_index: ClipChainRectIndex,
local_clip_rect: LayoutRect,
) -> Self {
PrimitiveRunContext {
clip_chain,
scroll_node,
clip_chain_rect_index,
local_clip_rect,
}
}
}
@ -132,6 +148,7 @@ impl FrameBuilder {
default_font_render_mode: FontRenderMode::Mono,
dual_source_blending_is_enabled: true,
dual_source_blending_is_supported: false,
chase_primitive: ChasePrimitive::Nothing,
},
}
}
@ -169,8 +186,7 @@ impl FrameBuilder {
profile_counters: &mut FrameProfileCounters,
device_pixel_scale: DevicePixelScale,
scene_properties: &SceneProperties,
local_clip_rects: &mut Vec<LayoutRect>,
node_data: &[ClipScrollNodeData],
transforms: &[TransformData],
) -> Option<RenderTaskId> {
profile_scope!("cull");
@ -187,6 +203,8 @@ impl FrameBuilder {
.expect("No display list?")
.display_list;
const MAX_CLIP_COORD: f32 = 1.0e9;
let frame_context = FrameBuildingContext {
scene_id: self.scene_id,
device_pixel_scale,
@ -194,14 +212,17 @@ impl FrameBuilder {
pipelines,
screen_rect: self.screen_rect.to_i32(),
clip_scroll_tree,
node_data,
transforms,
max_local_clip: LayoutRect::new(
LayoutPoint::new(-MAX_CLIP_COORD, -MAX_CLIP_COORD),
LayoutSize::new(2.0 * MAX_CLIP_COORD, 2.0 * MAX_CLIP_COORD),
),
};
let mut frame_state = FrameBuildingState {
render_tasks,
profile_counters,
clip_store: &mut self.clip_store,
local_clip_rects,
resource_cache,
gpu_cache,
special_render_passes,
@ -301,20 +322,13 @@ impl FrameBuilder {
resource_cache.begin_frame(frame_id);
gpu_cache.begin_frame();
let mut node_data = Vec::with_capacity(clip_scroll_tree.nodes.len());
let total_prim_runs =
self.prim_store.pictures.iter().fold(1, |count, pic| count + pic.runs.len());
let mut clip_chain_local_clip_rects = Vec::with_capacity(total_prim_runs);
clip_chain_local_clip_rects.push(LayoutRect::max_rect());
clip_scroll_tree.update_tree(
let transform_palette = clip_scroll_tree.update_tree(
&self.screen_rect.to_i32(),
device_pixel_scale,
&mut self.clip_store,
resource_cache,
gpu_cache,
pan,
&mut node_data,
scene_properties,
);
@ -335,8 +349,7 @@ impl FrameBuilder {
&mut profile_counters,
device_pixel_scale,
scene_properties,
&mut clip_chain_local_clip_rects,
&node_data,
&transform_palette.transforms,
);
resource_cache.block_until_all_resources_added(gpu_cache,
@ -369,6 +382,7 @@ impl FrameBuilder {
let mut deferred_resolves = vec![];
let mut has_texture_cache_tasks = false;
let mut prim_headers = PrimitiveHeaders::new();
let use_dual_source_blending = self.config.dual_source_blending_is_enabled &&
self.config.dual_source_blending_is_supported;
@ -379,7 +393,7 @@ impl FrameBuilder {
resource_cache,
clip_scroll_tree,
use_dual_source_blending,
node_data: &node_data,
transforms: &transform_palette,
};
pass.build(
@ -388,6 +402,7 @@ impl FrameBuilder {
&mut render_tasks,
&mut deferred_resolves,
&self.clip_store,
&mut prim_headers,
);
if let RenderPassKind::OffScreen { ref texture_cache, .. } = pass.kind {
@ -409,13 +424,13 @@ impl FrameBuilder {
layer,
profile_counters,
passes,
node_data,
clip_chain_local_clip_rects,
transform_palette: transform_palette.transforms,
render_tasks,
deferred_resolves,
gpu_cache_frame_id,
has_been_rendered: false,
has_texture_cache_tasks,
prim_headers,
}
}

View File

@ -2,29 +2,25 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{DevicePoint, DeviceSize, DeviceRect, LayoutToWorldTransform};
use api::{DevicePoint, DeviceSize, DeviceRect, LayoutRect, LayoutToWorldTransform};
use api::{PremultipliedColorF, WorldToLayoutTransform};
use clip_scroll_tree::TransformIndex;
use gpu_cache::{GpuCacheAddress, GpuDataRequest};
use prim_store::{VECS_PER_SEGMENT, EdgeAaSegmentMask};
use prim_store::{EdgeAaSegmentMask};
use render_task::RenderTaskAddress;
use renderer::MAX_VERTEX_TEXTURE_WIDTH;
use util::{MatrixHelpers, TransformedRectKind};
// Contains type that must exactly match the same structures declared in GLSL.
const INT_BITS: usize = 31; //TODO: convert to unsigned
const CLIP_CHAIN_RECT_BITS: usize = 22;
const SEGMENT_BITS: usize = INT_BITS - CLIP_CHAIN_RECT_BITS;
// The guard ensures (at compile time) that the designated number of bits cover
// the maximum supported segment count for the texture width.
const _SEGMENT_GUARD: usize = (1 << SEGMENT_BITS) * VECS_PER_SEGMENT - MAX_VERTEX_TEXTURE_WIDTH;
const EDGE_FLAG_BITS: usize = 4;
const BRUSH_FLAG_BITS: usize = 4;
const CLIP_SCROLL_INDEX_BITS: usize = INT_BITS - EDGE_FLAG_BITS - BRUSH_FLAG_BITS;
#[derive(Copy, Clone, Debug)]
#[repr(C)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct ZBufferId(i32);
#[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct ZBufferIdGenerator {
next: i32,
}
@ -119,7 +115,7 @@ pub struct BorderInstance {
#[repr(C)]
pub struct ClipMaskInstance {
pub render_task_address: RenderTaskAddress,
pub scroll_node_data_index: ClipScrollNodeIndex,
pub transform_id: TransformPaletteId,
pub segment: i32,
pub clip_data_address: GpuCacheAddress,
pub resource_address: GpuCacheAddress,
@ -135,50 +131,124 @@ pub struct ClipMaskBorderCornerDotDash {
pub dot_dash_data: [f32; 8],
}
// 32 bytes per instance should be enough for anyone!
// 16 bytes per instance should be enough for anyone!
#[derive(Debug, Clone)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct PrimitiveInstance {
data: [i32; 8],
data: [i32; 4],
}
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct PrimitiveHeaderIndex(pub i32);
#[derive(Debug)]
#[repr(C)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct PrimitiveHeaders {
// The integer-type headers for a primitive.
pub headers_int: Vec<PrimitiveHeaderI>,
// The float-type headers for a primitive.
pub headers_float: Vec<PrimitiveHeaderF>,
// Used to generated a unique z-buffer value per primitive.
pub z_generator: ZBufferIdGenerator,
}
impl PrimitiveHeaders {
pub fn new() -> PrimitiveHeaders {
PrimitiveHeaders {
headers_int: Vec::new(),
headers_float: Vec::new(),
z_generator: ZBufferIdGenerator::new(),
}
}
// Add a new primitive header.
pub fn push(
&mut self,
prim_header: &PrimitiveHeader,
user_data: [i32; 3],
) -> PrimitiveHeaderIndex {
debug_assert_eq!(self.headers_int.len(), self.headers_float.len());
let id = self.headers_float.len();
self.headers_float.push(PrimitiveHeaderF {
local_rect: prim_header.local_rect,
local_clip_rect: prim_header.local_clip_rect,
});
self.headers_int.push(PrimitiveHeaderI {
z: self.z_generator.next(),
task_address: prim_header.task_address,
specific_prim_address: prim_header.specific_prim_address.as_int(),
clip_task_address: prim_header.clip_task_address,
transform_id: prim_header.transform_id,
user_data,
});
PrimitiveHeaderIndex(id as i32)
}
}
// This is a convenience type used to make it easier to pass
// the common parts around during batching.
pub struct PrimitiveHeader {
pub local_rect: LayoutRect,
pub local_clip_rect: LayoutRect,
pub task_address: RenderTaskAddress,
pub specific_prim_address: GpuCacheAddress,
pub clip_task_address: RenderTaskAddress,
pub transform_id: TransformPaletteId,
}
// f32 parts of a primitive header
#[derive(Debug)]
#[repr(C)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct PrimitiveHeaderF {
pub local_rect: LayoutRect,
pub local_clip_rect: LayoutRect,
}
// i32 parts of a primitive header
// TODO(gw): Compress parts of these down to u16
#[derive(Debug)]
#[repr(C)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct PrimitiveHeaderI {
pub z: ZBufferId,
pub task_address: RenderTaskAddress,
pub specific_prim_address: i32,
pub clip_task_address: RenderTaskAddress,
pub transform_id: TransformPaletteId,
pub user_data: [i32; 3],
}
pub struct GlyphInstance {
pub specific_prim_address: GpuCacheAddress,
pub task_address: RenderTaskAddress,
pub clip_task_address: RenderTaskAddress,
pub clip_chain_rect_index: ClipChainRectIndex,
pub scroll_id: ClipScrollNodeIndex,
pub z: ZBufferId,
pub prim_header_index: PrimitiveHeaderIndex,
}
impl GlyphInstance {
pub fn new(
specific_prim_address: GpuCacheAddress,
task_address: RenderTaskAddress,
clip_task_address: RenderTaskAddress,
clip_chain_rect_index: ClipChainRectIndex,
scroll_id: ClipScrollNodeIndex,
z: ZBufferId,
prim_header_index: PrimitiveHeaderIndex,
) -> Self {
GlyphInstance {
specific_prim_address,
task_address,
clip_task_address,
clip_chain_rect_index,
scroll_id,
z,
prim_header_index,
}
}
// TODO(gw): Some of these fields can be moved to the primitive
// header since they are constant, and some can be
// compressed to a smaller size.
pub fn build(&self, data0: i32, data1: i32, data2: i32) -> PrimitiveInstance {
PrimitiveInstance {
data: [
self.specific_prim_address.as_int(),
self.task_address.0 as i32 | (self.clip_task_address.0 as i32) << 16,
self.clip_chain_rect_index.0 as i32,
self.scroll_id.0 as i32,
self.z.0,
self.prim_header_index.0 as i32,
data0,
data1,
data2,
@ -218,10 +288,6 @@ impl From<SplitCompositeInstance> for PrimitiveInstance {
instance.src_task_address.0 as i32,
instance.polygons_address.as_int(),
instance.z.0,
0,
0,
0,
0,
],
}
}
@ -243,80 +309,141 @@ bitflags! {
}
}
// TODO(gw): While we are converting things over, we
// need to have the instance be the same
// size as an old PrimitiveInstance. In the
// future, we can compress this vertex
// format a lot - e.g. z, render task
// addresses etc can reasonably become
// a u16 type.
// TODO(gw): Some of these fields can be moved to the primitive
// header since they are constant, and some can be
// compressed to a smaller size.
#[repr(C)]
pub struct BrushInstance {
pub picture_address: RenderTaskAddress,
pub prim_address: GpuCacheAddress,
pub clip_chain_rect_index: ClipChainRectIndex,
pub scroll_id: ClipScrollNodeIndex,
pub prim_header_index: PrimitiveHeaderIndex,
pub clip_task_address: RenderTaskAddress,
pub z: ZBufferId,
pub segment_index: i32,
pub edge_flags: EdgeAaSegmentMask,
pub brush_flags: BrushFlags,
pub user_data: [i32; 3],
}
impl From<BrushInstance> for PrimitiveInstance {
fn from(instance: BrushInstance) -> Self {
debug_assert_eq!(0, instance.clip_chain_rect_index.0 >> CLIP_CHAIN_RECT_BITS);
debug_assert_eq!(0, instance.scroll_id.0 >> CLIP_SCROLL_INDEX_BITS);
debug_assert_eq!(0, instance.segment_index >> SEGMENT_BITS);
PrimitiveInstance {
data: [
instance.picture_address.0 as i32 | (instance.clip_task_address.0 as i32) << 16,
instance.prim_address.as_int(),
instance.clip_chain_rect_index.0 as i32 | (instance.segment_index << CLIP_CHAIN_RECT_BITS),
instance.z.0,
instance.scroll_id.0 as i32 |
((instance.edge_flags.bits() as i32) << CLIP_SCROLL_INDEX_BITS) |
((instance.brush_flags.bits() as i32) << (CLIP_SCROLL_INDEX_BITS + EDGE_FLAG_BITS)),
instance.user_data[0],
instance.user_data[1],
instance.user_data[2],
instance.prim_header_index.0,
instance.clip_task_address.0 as i32,
instance.segment_index |
((instance.edge_flags.bits() as i32) << 16) |
((instance.brush_flags.bits() as i32) << 24),
0,
]
}
}
}
// Represents the information about a transform palette
// entry that is passed to shaders. It includes an index
// into the transform palette, and a set of flags. The
// only flag currently used determines whether the
// transform is axis-aligned (and this should have
// pixel snapping applied).
#[derive(Copy, Debug, Clone, PartialEq)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[repr(C)]
pub struct ClipScrollNodeIndex(pub u32);
pub struct TransformPaletteId(pub u32);
#[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[repr(C)]
pub struct ClipScrollNodeData {
pub transform: LayoutToWorldTransform,
pub inv_transform: WorldToLayoutTransform,
pub transform_kind: f32,
pub padding: [f32; 3],
}
impl TransformPaletteId {
// Get the palette ID for an identity transform.
pub fn identity() -> TransformPaletteId {
TransformPaletteId(0)
}
impl ClipScrollNodeData {
pub fn invalid() -> Self {
ClipScrollNodeData {
transform: LayoutToWorldTransform::identity(),
inv_transform: WorldToLayoutTransform::identity(),
transform_kind: 0.0,
padding: [0.0; 3],
// Extract the transform kind from the id.
pub fn transform_kind(&self) -> TransformedRectKind {
if (self.0 >> 24) == 0 {
TransformedRectKind::AxisAligned
} else {
TransformedRectKind::Complex
}
}
}
#[derive(Copy, Debug, Clone, PartialEq)]
// The GPU data payload for a transform palette entry.
#[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[repr(C)]
pub struct ClipChainRectIndex(pub usize);
pub struct TransformData {
pub transform: LayoutToWorldTransform,
pub inv_transform: WorldToLayoutTransform,
}
impl TransformData {
pub fn invalid() -> Self {
TransformData {
transform: LayoutToWorldTransform::identity(),
inv_transform: WorldToLayoutTransform::identity(),
}
}
}
// Extra data stored about each transform palette entry.
pub struct TransformMetadata {
pub transform_kind: TransformedRectKind,
}
// Stores a contiguous list of TransformData structs, that
// are ready for upload to the GPU.
// TODO(gw): For now, this only stores the complete local
// to world transform for each spatial node. In
// the future, the transform palette will support
// specifying a coordinate system that the transform
// should be relative to.
pub struct TransformPalette {
pub transforms: Vec<TransformData>,
metadata: Vec<TransformMetadata>,
}
impl TransformPalette {
pub fn new(spatial_node_count: usize) -> TransformPalette {
TransformPalette {
transforms: Vec::with_capacity(spatial_node_count),
metadata: Vec::with_capacity(spatial_node_count),
}
}
// Set the local -> world transform for a given spatial
// node in the transform palette.
pub fn set(
&mut self,
index: TransformIndex,
data: TransformData,
) {
let index = index.0 as usize;
// Pad the vectors out if they are not long enough to
// account for this index. This can occur, for instance,
// when we stop recursing down the CST due to encountering
// a node with an invalid transform.
while index >= self.transforms.len() {
self.transforms.push(TransformData::invalid());
self.metadata.push(TransformMetadata {
transform_kind: TransformedRectKind::AxisAligned,
});
}
// Store the transform itself, along with metadata about it.
self.metadata[index] = TransformMetadata {
transform_kind: data.transform.transform_kind(),
};
self.transforms[index] = data;
}
// Get a transform palette id for the given spatial node.
// TODO(gw): In the future, it will be possible to specify
// a coordinate system id here, to allow retrieving
// transforms in the local space of a given spatial node.
pub fn get_id(&self, index: TransformIndex) -> TransformPaletteId {
let transform_kind = self.metadata[index.0 as usize].transform_kind as u32;
TransformPaletteId(index.0 | (transform_kind << 24))
}
}
// Texture cache resources can be either a simple rect, or define
// a polygon within a rect by specifying a UV coordinate for each

View File

@ -182,6 +182,7 @@ pub extern crate webrender_api;
#[doc(hidden)]
pub use device::{build_shader_strings, ReadPixelsFormat, UploadMethod, VertexUsageHint};
pub use device::{ProgramBinary, ProgramCache, ProgramCacheObserver, ProgramSources};
pub use frame_builder::ChasePrimitive;
pub use renderer::{AsyncPropertySampler, CpuProfile, DebugFlags, OutputImageHandler, RendererKind};
pub use renderer::{ExternalImage, ExternalImageHandler, ExternalImageSource, GpuProfile};
pub use renderer::{GraphicsApi, GraphicsApiInfo, PipelineInfo, Renderer, RendererOptions};

View File

@ -276,7 +276,7 @@ impl PicturePrimitive {
self.can_draw_directly_to_parent_surface()
}
pub fn prepare_for_render_inner(
pub fn prepare_for_render(
&mut self,
prim_index: PrimitiveIndex,
prim_metadata: &mut PrimitiveMetadata,
@ -489,16 +489,9 @@ impl PicturePrimitive {
// that writes a brush primitive header.
// Basic brush primitive header is (see end of prepare_prim_for_render_inner in prim_store.rs)
// local_rect
// clip_rect
// [brush specific data]
// [segment_rect, segment data]
let shadow_rect = prim_metadata.local_rect.translate(&offset);
let shadow_clip_rect = prim_metadata.local_clip_rect.translate(&offset);
// local_rect, clip_rect
request.push(shadow_rect);
request.push(shadow_clip_rect);
// ImageBrush colors
request.push(color.premultiplied());
@ -592,27 +585,6 @@ impl PicturePrimitive {
}
}
}
pub fn prepare_for_render(
&mut self,
prim_index: PrimitiveIndex,
prim_metadata: &mut PrimitiveMetadata,
prim_run_context: &PrimitiveRunContext,
pic_state_for_children: PictureState,
pic_state: &mut PictureState,
frame_context: &FrameBuildingContext,
frame_state: &mut FrameBuildingState,
) {
self.prepare_for_render_inner(
prim_index,
prim_metadata,
prim_run_context,
pic_state_for_children,
pic_state,
frame_context,
frame_state,
);
}
}
// Calculate a single screen-space UV for a picture.

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, BorderRadius, BoxShadowClipMode, BuiltDisplayList, ClipMode, ColorF, ComplexClipRegion};
use api::{AlphaType, BorderRadius, BoxShadowClipMode, BuiltDisplayList, ClipMode, ColorF};
use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, Epoch, ExtendMode};
use api::{FilterOp, GlyphInstance, GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag, TileOffset};
use api::{GlyphRasterSpace, LayoutPoint, LayoutRect, LayoutSize, LayoutToWorldTransform, LayoutVector2D};
@ -20,7 +20,7 @@ use frame_builder::PrimitiveRunContext;
use glyph_rasterizer::{FontInstance, FontTransform, GlyphKey, FONT_SIZE_LIMIT};
use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest,
ToGpuBlocks};
use gpu_types::{BrushFlags, ClipChainRectIndex};
use gpu_types::BrushFlags;
use image::{for_each_tile, for_each_repetition};
use picture::{PictureCompositeMode, PictureId, PicturePrimitive};
#[cfg(debug_assertions)]
@ -50,7 +50,7 @@ impl ScrollNodeAndClipChain {
pub fn new(
scroll_node_id: ClipScrollNodeIndex,
clip_chain_index: ClipChainIndex
) -> ScrollNodeAndClipChain {
) -> Self {
ScrollNodeAndClipChain { scroll_node_id, clip_chain_index }
}
}
@ -62,6 +62,17 @@ pub struct PrimitiveRun {
pub clip_and_scroll: ScrollNodeAndClipChain,
}
impl PrimitiveRun {
pub fn is_chasing(&self, index: Option<PrimitiveIndex>) -> bool {
match index {
Some(id) if cfg!(debug_assertions) => {
self.base_prim_index <= id && id.0 < self.base_prim_index.0 + self.count
}
_ => false,
}
}
}
#[derive(Debug, Copy, Clone)]
pub struct PrimitiveOpacity {
pub is_opaque: bool,
@ -174,7 +185,11 @@ pub struct PrimitiveMetadata {
// storing them here.
pub local_rect: LayoutRect,
pub local_clip_rect: LayoutRect,
pub clip_chain_rect_index: ClipChainRectIndex,
// The current combined local clip for this primitive, from
// the primitive local clip above and the current clip chain.
pub combined_local_clip_rect: LayoutRect,
pub is_backface_visible: bool,
pub screen_rect: Option<ScreenRect>,
@ -234,11 +249,15 @@ pub struct VisibleImageTile {
pub tile_offset: TileOffset,
pub handle: GpuCacheHandle,
pub edge_flags: EdgeAaSegmentMask,
pub local_rect: LayoutRect,
pub local_clip_rect: LayoutRect,
}
#[derive(Debug)]
pub struct VisibleGradientTile {
pub handle: GpuCacheHandle,
pub local_rect: LayoutRect,
pub local_clip_rect: LayoutRect,
}
#[derive(Debug)]
@ -418,14 +437,14 @@ impl BrushPrimitive {
pub fn new(
kind: BrushKind,
segment_desc: Option<BrushSegmentDescriptor>,
) -> BrushPrimitive {
) -> Self {
BrushPrimitive {
kind,
segment_desc,
}
}
pub fn new_picture(pic_index: PictureIndex) -> BrushPrimitive {
pub fn new_picture(pic_index: PictureIndex) -> Self {
BrushPrimitive {
kind: BrushKind::Picture {
pic_index,
@ -719,7 +738,6 @@ impl<'a> GradientGpuBlockBuilder<'a> {
self.fill_colors(cur_idx, GRADIENT_DATA_TABLE_END, &PremultipliedColorF::WHITE, &cur_color, &mut entries);
}
// Fill in the last entry with the last color stop
self.fill_colors(
GRADIENT_DATA_LAST_STOP,
@ -739,12 +757,12 @@ impl<'a> GradientGpuBlockBuilder<'a> {
#[derive(Debug, Clone)]
pub struct TextRunPrimitiveCpu {
pub font: FontInstance,
pub specified_font: FontInstance,
pub used_font: FontInstance,
pub offset: LayoutVector2D,
pub glyph_range: ItemRange<GlyphInstance>,
pub glyph_keys: Vec<GlyphKey>,
pub glyph_gpu_blocks: Vec<GpuBlockData>,
pub glyph_transform: (DevicePixelScale, FontTransform),
pub shadow: bool,
pub glyph_raster_space: GlyphRasterSpace,
}
@ -759,60 +777,97 @@ impl TextRunPrimitiveCpu {
glyph_raster_space: GlyphRasterSpace,
) -> Self {
TextRunPrimitiveCpu {
font,
specified_font: font.clone(),
used_font: font,
offset,
glyph_range,
glyph_keys,
glyph_gpu_blocks: Vec::new(),
glyph_transform: (DevicePixelScale::new(1.0), FontTransform::identity()),
shadow,
glyph_raster_space,
}
}
pub fn get_font(
&self,
pub fn update_font_instance(
&mut self,
device_pixel_scale: DevicePixelScale,
transform: LayoutToWorldTransform,
) -> FontInstance {
let mut font = self.font.clone();
font.size = font.size.scale_by(device_pixel_scale.0);
transform: &LayoutToWorldTransform,
allow_subpixel_aa: bool,
) -> bool {
// Get the current font size in device pixels
let device_font_size = self.specified_font.size.scale_by(device_pixel_scale.0);
// Determine if rasterizing glyphs in local or screen space.
// Only support transforms that can be coerced to simple 2D transforms.
if transform.has_perspective_component() ||
let transform_glyphs = if transform.has_perspective_component() ||
!transform.has_2d_inverse() ||
// Font sizes larger than the limit need to be scaled, thus can't use subpixels.
transform.exceeds_2d_scale(FONT_SIZE_LIMIT / font.size.to_f64_px()) ||
transform.exceeds_2d_scale(FONT_SIZE_LIMIT / device_font_size.to_f64_px()) ||
// Otherwise, ensure the font is rasterized in screen-space.
self.glyph_raster_space != GlyphRasterSpace::Screen {
font.disable_subpixel_aa();
font.disable_subpixel_position();
false
} else {
true
};
// Get the font transform matrix (skew / scale) from the complete transform.
let font_transform = if transform_glyphs {
// Quantize the transform to minimize thrashing of the glyph cache.
font.transform = FontTransform::from(&transform).quantize();
FontTransform::from(transform).quantize()
} else {
FontTransform::identity()
};
// If the transform or device size is different, then the caller of
// this method needs to know to rebuild the glyphs.
let cache_dirty =
self.used_font.transform != font_transform ||
self.used_font.size != device_font_size;
// Construct used font instance from the specified font instance
self.used_font = FontInstance {
transform: font_transform,
size: device_font_size,
..self.specified_font.clone()
};
// If subpixel AA is disabled due to the backing surface the glyphs
// are being drawn onto, disable it (unless we are using the
// specifial subpixel mode that estimates background color).
if !allow_subpixel_aa && self.specified_font.bg_color.a == 0 {
self.used_font.disable_subpixel_aa();
}
font
// If using local space glyphs, we don't want subpixel AA
// or positioning.
if !transform_glyphs {
self.used_font.disable_subpixel_aa();
self.used_font.disable_subpixel_position();
}
cache_dirty
}
fn prepare_for_render(
&mut self,
device_pixel_scale: DevicePixelScale,
transform: LayoutToWorldTransform,
transform: &LayoutToWorldTransform,
allow_subpixel_aa: bool,
display_list: &BuiltDisplayList,
frame_building_state: &mut FrameBuildingState,
) {
if !allow_subpixel_aa && self.font.bg_color.a == 0 {
self.font.disable_subpixel_aa();
}
let font = self.get_font(device_pixel_scale, transform);
let cache_dirty = self.update_font_instance(
device_pixel_scale,
transform,
allow_subpixel_aa,
);
// Cache the glyph positions, if not in the cache already.
// TODO(gw): In the future, remove `glyph_instances`
// completely, and just reference the glyphs
// directly from the display list.
if self.glyph_keys.is_empty() || self.glyph_transform != (device_pixel_scale, font.transform) {
let subpx_dir = font.get_subpx_dir();
if self.glyph_keys.is_empty() || cache_dirty {
let subpx_dir = self.used_font.get_subpx_dir();
let src_glyphs = display_list.get(self.glyph_range);
// TODO(gw): If we support chunks() on AuxIter
@ -820,7 +875,7 @@ impl TextRunPrimitiveCpu {
// be much simpler...
let mut gpu_block = [0.0; 4];
for (i, src) in src_glyphs.enumerate() {
let world_offset = font.transform.transform(&src.point);
let world_offset = self.used_font.transform.transform(&src.point);
let device_offset = device_pixel_scale.transform_point(&world_offset);
let key = GlyphKey::new(src.index, device_offset, subpx_dir);
self.glyph_keys.push(key);
@ -842,12 +897,10 @@ impl TextRunPrimitiveCpu {
if (self.glyph_keys.len() & 1) != 0 {
self.glyph_gpu_blocks.push(gpu_block.into());
}
self.glyph_transform = (device_pixel_scale, font.transform);
}
frame_building_state.resource_cache
.request_glyphs(font,
.request_glyphs(self.used_font.clone(),
&self.glyph_keys,
frame_building_state.gpu_cache,
frame_building_state.render_tasks,
@ -855,9 +908,9 @@ impl TextRunPrimitiveCpu {
}
fn write_gpu_blocks(&self, request: &mut GpuDataRequest) {
request.push(ColorF::from(self.font.color).premultiplied());
request.push(ColorF::from(self.used_font.color).premultiplied());
// this is the only case where we need to provide plain color to GPU
let bg_color = ColorF::from(self.font.bg_color);
let bg_color = ColorF::from(self.used_font.bg_color);
request.push([bg_color.r, bg_color.g, bg_color.b, 1.0]);
request.push([
self.offset.x,
@ -1071,7 +1124,7 @@ impl PrimitiveContainer {
pub fn is_visible(&self) -> bool {
match *self {
PrimitiveContainer::TextRun(ref info) => {
info.font.color.a > 0
info.specified_font.color.a > 0
}
PrimitiveContainer::Brush(ref brush) => {
match brush.kind {
@ -1100,7 +1153,7 @@ impl PrimitiveContainer {
PrimitiveContainer::TextRun(ref info) => {
let mut font = FontInstance {
color: shadow.color.into(),
..info.font.clone()
..info.specified_font.clone()
};
if shadow.blur_radius > 0.0 {
font.disable_subpixel_aa();
@ -1146,6 +1199,9 @@ pub struct PrimitiveStore {
pub pictures: Vec<PicturePrimitive>,
next_picture_id: u64,
/// A primitive index to chase through debugging.
pub chase_id: Option<PrimitiveIndex>,
}
impl PrimitiveStore {
@ -1157,6 +1213,8 @@ impl PrimitiveStore {
pictures: Vec::new(),
next_picture_id: 0,
chase_id: None,
}
}
@ -1168,6 +1226,8 @@ impl PrimitiveStore {
pictures: recycle_vec(self.pictures),
next_picture_id: self.next_picture_id,
chase_id: self.chase_id,
}
}
@ -1213,7 +1273,7 @@ impl PrimitiveStore {
clip_task_id: None,
local_rect: *local_rect,
local_clip_rect: *local_clip_rect,
clip_chain_rect_index: ClipChainRectIndex(0),
combined_local_clip_rect: *local_clip_rect,
is_backface_visible,
screen_rect: None,
tag,
@ -1495,7 +1555,7 @@ impl PrimitiveStore {
let transform = prim_run_context.scroll_node.world_content_transform.into();
text.prepare_for_render(
frame_context.device_pixel_scale,
transform,
&transform,
pic_context.allow_subpixel_aa,
pic_context.display_list,
frame_state,
@ -1644,7 +1704,9 @@ impl PrimitiveStore {
// Tighten the clip rect because decomposing the repeated image can
// produce primitives that are partially covering the original image
// rect and we want to clip these extra parts out.
let tight_clip_rect = metadata.local_clip_rect.intersection(&metadata.local_rect).unwrap();
let tight_clip_rect = metadata
.combined_local_clip_rect
.intersection(&metadata.local_rect).unwrap();
let visible_rect = compute_conservative_visible_rect(
prim_run_context,
@ -1684,8 +1746,6 @@ impl PrimitiveStore {
let mut handle = GpuCacheHandle::new();
if let Some(mut request) = frame_state.gpu_cache.request(&mut handle) {
request.push(*tile_rect);
request.push(tight_clip_rect);
request.push(ColorF::new(1.0, 1.0, 1.0, opacity_binding.current).premultiplied());
request.push(PremultipliedColorF::WHITE);
request.push([tile_rect.size.width, tile_rect.size.height, 0.0, 0.0]);
@ -1696,6 +1756,8 @@ impl PrimitiveStore {
tile_offset,
handle,
edge_flags: tile_flags & edge_flags,
local_rect: *tile_rect,
local_clip_rect: tight_clip_rect,
});
}
);
@ -1789,9 +1851,7 @@ impl PrimitiveStore {
prim_run_context,
frame_context,
frame_state,
&mut |rect, clip_rect, mut request| {
request.push(*rect);
request.push(*clip_rect);
&mut |rect, mut request| {
request.push([
center.x,
center.y,
@ -1841,9 +1901,7 @@ impl PrimitiveStore {
prim_run_context,
frame_context,
frame_state,
&mut |rect, clip_rect, mut request| {
request.push(*rect);
request.push(*clip_rect);
&mut |rect, mut request| {
request.push([
start_point.x,
start_point.y,
@ -1895,10 +1953,6 @@ impl PrimitiveStore {
// Mark this GPU resource as required for this frame.
if let Some(mut request) = frame_state.gpu_cache.request(&mut metadata.gpu_location) {
// has to match VECS_PER_BRUSH_PRIM
request.push(metadata.local_rect);
request.push(metadata.local_clip_rect);
match metadata.prim_kind {
PrimitiveKind::TextRun => {
let text = &self.cpu_text_runs[metadata.cpu_prim_index.0];
@ -2042,11 +2096,11 @@ impl PrimitiveStore {
// node and the primitive, we need to get the clip rect in the
// local space of the primitive, in order to generate correct
// local segments.
let local_clip_rect = if clip_item.scroll_node_data_index == prim_run_context.scroll_node.node_data_index {
let local_clip_rect = if clip_item.transform_index == prim_run_context.scroll_node.transform_index {
local_clip_rect
} else {
let clip_transform = frame_context
.node_data[clip_item.scroll_node_data_index.0 as usize]
.transforms[clip_item.transform_index.0 as usize]
.transform;
let prim_transform = &prim_run_context.scroll_node.world_content_transform;
let relative_transform = prim_transform
@ -2193,12 +2247,19 @@ impl PrimitiveStore {
frame_context: &FrameBuildingContext,
frame_state: &mut FrameBuildingState,
) -> bool {
if cfg!(debug_assertions) && Some(prim_index) == self.chase_id {
println!("\tupdating clip task with screen rect {:?}", prim_screen_rect);
}
// Reset clips from previous frames since we may clip differently each frame.
self.reset_clip_task(prim_index);
let prim_screen_rect = match prim_screen_rect.intersection(&frame_context.screen_rect) {
Some(rect) => rect,
None => {
if cfg!(debug_assertions) && Some(prim_index) == self.chase_id {
println!("\tculled by the intersection with frame rect {:?}",
frame_context.screen_rect);
}
self.cpu_metadata[prim_index.0].screen_rect = None;
return false;
}
@ -2207,6 +2268,9 @@ impl PrimitiveStore {
let mut combined_outer_rect =
prim_screen_rect.intersection(&prim_run_context.clip_chain.combined_outer_screen_rect);
let clip_chain = prim_run_context.clip_chain.nodes.clone();
if cfg!(debug_assertions) && Some(prim_index) == self.chase_id {
println!("\tbase combined outer rect {:?}", combined_outer_rect);
}
let prim_coordinate_system_id = prim_run_context.scroll_node.coordinate_system_id;
let transform = &prim_run_context.scroll_node.world_content_transform;
@ -2228,10 +2292,13 @@ impl PrimitiveStore {
if let Some(outer) = screen_outer_rect {
combined_outer_rect = combined_outer_rect.and_then(|r| r.intersection(&outer));
}
if cfg!(debug_assertions) && Some(prim_index) == self.chase_id {
println!("\tfound extra clip with screen bounds {:?}", screen_outer_rect);
}
Arc::new(ClipChainNode {
work_item: ClipWorkItem {
scroll_node_data_index: prim_run_context.scroll_node.node_data_index,
transform_index: prim_run_context.scroll_node.transform_index,
clip_sources: clip_sources.weak(),
coordinate_system_id: prim_coordinate_system_id,
},
@ -2251,6 +2318,9 @@ impl PrimitiveStore {
let combined_outer_rect = match combined_outer_rect {
Some(rect) if !rect.is_empty() => rect,
_ => {
if cfg!(debug_assertions) && Some(prim_index) == self.chase_id {
println!("\tculled by the empty combined screen rect");
}
self.cpu_metadata[prim_index.0].screen_rect = None;
return false;
}
@ -2274,12 +2344,18 @@ impl PrimitiveStore {
// If we don't have any clips from other coordinate systems, the local clip
// calculated from the clip chain should be sufficient to ensure proper clipping.
if !has_clips_from_other_coordinate_systems {
if cfg!(debug_assertions) && Some(prim_index) == self.chase_id {
println!("\tneed no task: all clips are within the coordinate system");
}
return true;
}
// If we have filtered all clips and the screen rect isn't any smaller, we can just
// skip masking entirely.
if combined_outer_rect == prim_screen_rect {
if cfg!(debug_assertions) && Some(prim_index) == self.chase_id {
println!("\tneed no task: combined rect is not smaller");
}
return true;
}
// Otherwise we create an empty mask, but with an empty inner rect to avoid further
@ -2288,7 +2364,10 @@ impl PrimitiveStore {
}
if combined_inner_rect.contains_rect(&prim_screen_rect) {
return true;
if cfg!(debug_assertions) && Some(prim_index) == self.chase_id {
println!("\tneed no task: contained within the clip inner rect");
}
return true;
}
// First try to render this primitive's mask using optimized brush rendering.
@ -2302,6 +2381,9 @@ impl PrimitiveStore {
frame_context,
frame_state,
) {
if cfg!(debug_assertions) && Some(prim_index) == self.chase_id {
println!("\tsegment tasks have been created for clipping");
}
return true;
}
@ -2316,6 +2398,10 @@ impl PrimitiveStore {
);
let clip_task_id = frame_state.render_tasks.add(clip_task);
if cfg!(debug_assertions) && Some(prim_index) == self.chase_id {
println!("\tcreated task {:?} with combined outer rect {:?}",
clip_task_id, combined_outer_rect);
}
self.cpu_metadata[prim_index.0].clip_task_id = Some(clip_task_id);
pic_state.tasks.push(clip_task_id);
@ -2341,6 +2427,9 @@ impl PrimitiveStore {
if !metadata.is_backface_visible &&
prim_run_context.scroll_node.world_content_transform.is_backface_visible() {
if cfg!(debug_assertions) && Some(prim_index) == self.chase_id {
println!("\tculled for not having visible back faces");
}
return None;
}
@ -2358,6 +2447,9 @@ impl PrimitiveStore {
let pic = &mut self.pictures[pic_index.0];
if !pic.resolve_scene_properties(frame_context.scene_properties) {
if cfg!(debug_assertions) && Some(prim_index) == self.chase_id {
println!("\tculled for carrying an invisible composite filter");
}
return None;
}
@ -2429,7 +2521,9 @@ impl PrimitiveStore {
let metadata = &mut self.cpu_metadata[prim_index.0];
if metadata.local_rect.size.width <= 0.0 ||
metadata.local_rect.size.height <= 0.0 {
//warn!("invalid primitive rect {:?}", metadata.local_rect);
if cfg!(debug_assertions) && Some(prim_index) == self.chase_id {
println!("\tculled for zero local rectangle");
}
return None;
}
@ -2441,14 +2535,33 @@ impl PrimitiveStore {
// taken into account.
let local_rect = metadata.local_rect
.inflate(pic_context.inflation_factor, pic_context.inflation_factor)
.intersection(&metadata.local_clip_rect)?;
.intersection(&metadata.local_clip_rect);
let local_rect = match local_rect {
Some(local_rect) => local_rect,
None => {
if cfg!(debug_assertions) && Some(prim_index) == self.chase_id {
println!("\tculled for being out of the local clip rectangle: {:?}",
metadata.local_clip_rect);
}
return None
}
};
let unclipped = calculate_screen_bounding_rect(
let unclipped = match calculate_screen_bounding_rect(
&prim_run_context.scroll_node.world_content_transform,
&local_rect,
frame_context.device_pixel_scale,
None, //TODO: inflate `frame_context.screen_rect` appropriately
)?;
) {
Some(rect) => rect,
None => {
if cfg!(debug_assertions) && Some(prim_index) == self.chase_id {
println!("\tculled for being behind the near plane of transform: {:?}",
prim_run_context.scroll_node.world_content_transform);
}
return None
}
};
let clipped = unclipped
.intersection(&prim_run_context.clip_chain.combined_outer_screen_rect)?;
@ -2457,7 +2570,11 @@ impl PrimitiveStore {
clipped,
unclipped,
});
metadata.clip_chain_rect_index = prim_run_context.clip_chain_rect_index;
metadata.combined_local_clip_rect = prim_run_context
.local_clip_rect
.intersection(&metadata.local_clip_rect)
.unwrap_or(LayoutRect::zero());
(local_rect, unclipped)
};
@ -2480,6 +2597,10 @@ impl PrimitiveStore {
return None;
}
if cfg!(debug_assertions) && Some(prim_index) == self.chase_id {
println!("\tconsidered visible and ready with local rect {:?}", local_rect);
}
self.prepare_prim_for_render_inner(
prim_index,
prim_run_context,
@ -2514,6 +2635,10 @@ impl PrimitiveStore {
};
for run in &pic_context.prim_runs {
if run.is_chasing(self.chase_id) {
println!("\tpreparing a run of length {} in pipeline {:?}",
run.count, pic_context.pipeline_id);
}
// TODO(gw): Perhaps we can restructure this to not need to create
// a new primitive context for every run (if the hash
// lookups ever show up in a profile).
@ -2531,12 +2656,16 @@ impl PrimitiveStore {
pic_state.has_non_root_coord_system |= clip_chain.has_non_root_coord_system;
if !scroll_node.invertible {
debug!("{:?} {:?}: position not invertible", run.base_prim_index, pic_context.pipeline_id);
if run.is_chasing(self.chase_id) {
println!("\tculled for the scroll node transform being invertible");
}
continue;
}
if clip_chain.combined_outer_screen_rect.is_empty() {
debug!("{:?} {:?}: clipped out", run.base_prim_index, pic_context.pipeline_id);
if run.is_chasing(self.chase_id) {
println!("\tculled for out of screen bounds");
}
continue;
}
@ -2565,19 +2694,16 @@ impl PrimitiveStore {
None
};
let clip_chain_rect_index = match clip_chain_rect {
let local_clip_chain_rect = match clip_chain_rect {
Some(rect) if rect.is_empty() => continue,
Some(rect) => {
frame_state.local_clip_rects.push(rect);
ClipChainRectIndex(frame_state.local_clip_rects.len() - 1)
}
None => ClipChainRectIndex(0), // This is no clipping.
Some(rect) => rect,
None => frame_context.max_local_clip,
};
let child_prim_run_context = PrimitiveRunContext::new(
clip_chain,
scroll_node,
clip_chain_rect_index,
local_clip_chain_rect,
);
for i in 0 .. run.count {
@ -2611,6 +2737,12 @@ impl PrimitiveStore {
result.local_rect_in_original_parent_space =
result.local_rect_in_original_parent_space.union(&bounds);
}
if let Some(ref matrix) = parent_relative_transform {
let bounds = matrix.transform_rect(&prim_local_rect);
result.local_rect_in_actual_parent_space =
result.local_rect_in_actual_parent_space.union(&bounds);
}
}
}
}
@ -2646,14 +2778,16 @@ fn decompose_repeated_primitive(
prim_run_context: &PrimitiveRunContext,
frame_context: &FrameBuildingContext,
frame_state: &mut FrameBuildingState,
callback: &mut FnMut(&LayoutRect, &LayoutRect, GpuDataRequest),
callback: &mut FnMut(&LayoutRect, GpuDataRequest),
) {
visible_tiles.clear();
// Tighten the clip rect because decomposing the repeated image can
// produce primitives that are partially covering the original image
// rect and we want to clip these extra parts out.
let tight_clip_rect = metadata.local_clip_rect.intersection(&metadata.local_rect).unwrap();
let tight_clip_rect = metadata
.combined_local_clip_rect
.intersection(&metadata.local_rect).unwrap();
let visible_rect = compute_conservative_visible_rect(
prim_run_context,
@ -2669,16 +2803,19 @@ fn decompose_repeated_primitive(
&mut |origin, _| {
let mut handle = GpuCacheHandle::new();
let rect = LayoutRect {
origin: *origin,
size: *stretch_size,
};
if let Some(request) = frame_state.gpu_cache.request(&mut handle) {
let rect = LayoutRect {
origin: *origin,
size: *stretch_size,
};
callback(&rect, &tight_clip_rect, request);
callback(&rect, request);
}
visible_tiles.push(VisibleGradientTile { handle });
visible_tiles.push(VisibleGradientTile {
local_rect: rect,
local_clip_rect: tight_clip_rect,
handle
});
}
);
@ -2725,31 +2862,6 @@ fn edge_flags_for_tile_spacing(tile_spacing: &LayoutSize) -> EdgeAaSegmentMask {
flags
}
//Test for one clip region contains another
trait InsideTest<T> {
fn might_contain(&self, clip: &T) -> bool;
}
impl InsideTest<ComplexClipRegion> for ComplexClipRegion {
// Returns true if clip is inside self, can return false negative
fn might_contain(&self, clip: &ComplexClipRegion) -> bool {
let delta_left = clip.rect.origin.x - self.rect.origin.x;
let delta_top = clip.rect.origin.y - self.rect.origin.y;
let delta_right = self.rect.max_x() - clip.rect.max_x();
let delta_bottom = self.rect.max_y() - clip.rect.max_y();
delta_left >= 0f32 && delta_top >= 0f32 && delta_right >= 0f32 && delta_bottom >= 0f32 &&
clip.radii.top_left.width >= self.radii.top_left.width - delta_left &&
clip.radii.top_left.height >= self.radii.top_left.height - delta_top &&
clip.radii.top_right.width >= self.radii.top_right.width - delta_right &&
clip.radii.top_right.height >= self.radii.top_right.height - delta_top &&
clip.radii.bottom_left.width >= self.radii.bottom_left.width - delta_left &&
clip.radii.bottom_left.height >= self.radii.bottom_left.height - delta_bottom &&
clip.radii.bottom_right.width >= self.radii.bottom_right.width - delta_right &&
clip.radii.bottom_right.height >= self.radii.bottom_right.height - delta_bottom
}
}
fn convert_clip_chain_to_clip_vector(
clip_chain_nodes: ClipChainNodeRef,
extra_clip: ClipChainNodeRef,

View File

@ -1063,6 +1063,7 @@ impl RenderBackend {
debug_assert!(op.render || !op.composite);
let mut render_time = None;
if op.render && doc.has_pixels() {
profile_scope!("generate frame");
@ -1071,6 +1072,7 @@ impl RenderBackend {
// borrow ck hack for profile_counters
let (pending_update, rendered_document) = {
let _timer = profile_counters.total_time.timer();
let render_start_time = precise_time_ns();
let rendered_document = doc.render(
&mut self.resource_cache,
@ -1085,6 +1087,8 @@ impl RenderBackend {
let msg = ResultMsg::UpdateGpuCache(self.gpu_cache.extract_updates());
self.result_tx.send(msg).unwrap();
render_time = Some(precise_time_ns() - render_start_time);
let pending_update = self.resource_cache.pending_updates();
(pending_update, rendered_document)
};
@ -1111,7 +1115,7 @@ impl RenderBackend {
}
if transaction_msg.generate_frame {
self.notifier.new_frame_ready(document_id, op.scroll, op.composite);
self.notifier.new_frame_ready(document_id, op.scroll, op.composite, render_time);
}
}
@ -1410,7 +1414,7 @@ impl RenderBackend {
self.result_tx.send(msg_publish).unwrap();
profile_counters.reset();
self.notifier.new_frame_ready(id, false, true);
self.notifier.new_frame_ready(id, false, true, None);
self.documents.insert(id, doc);
}
}

View File

@ -26,7 +26,7 @@ use device::{FileWatcherHandler, ShaderError, TextureFilter,
VertexUsageHint, VAO, VBO, CustomVAO};
use device::{ProgramCache, ReadPixelsFormat};
use euclid::{rect, Transform3D};
use frame_builder::FrameBuilderConfig;
use frame_builder::{ChasePrimitive, FrameBuilderConfig};
use gleam::gl;
use glyph_rasterizer::{GlyphFormat, GlyphRasterizer};
use gpu_cache::{GpuBlockData, GpuCacheUpdate, GpuCacheUpdateList};
@ -270,14 +270,15 @@ pub(crate) enum TextureSampler {
CacheA8,
CacheRGBA8,
ResourceCache,
ClipScrollNodes,
TransformPalette,
RenderTasks,
Dither,
// A special sampler that is bound to the A8 output of
// the *first* pass. Items rendered in this target are
// available as inputs to tasks in any subsequent pass.
SharedCacheA8,
LocalClipRects
PrimitiveHeadersF,
PrimitiveHeadersI,
}
impl TextureSampler {
@ -302,11 +303,12 @@ impl Into<TextureSlot> for TextureSampler {
TextureSampler::CacheA8 => TextureSlot(3),
TextureSampler::CacheRGBA8 => TextureSlot(4),
TextureSampler::ResourceCache => TextureSlot(5),
TextureSampler::ClipScrollNodes => TextureSlot(6),
TextureSampler::TransformPalette => TextureSlot(6),
TextureSampler::RenderTasks => TextureSlot(7),
TextureSampler::Dither => TextureSlot(8),
TextureSampler::SharedCacheA8 => TextureSlot(9),
TextureSampler::LocalClipRects => TextureSlot(10),
TextureSampler::PrimitiveHeadersF => TextureSlot(10),
TextureSampler::PrimitiveHeadersI => TextureSlot(11),
}
}
}
@ -330,12 +332,7 @@ pub(crate) mod desc {
],
instance_attributes: &[
VertexAttribute {
name: "aData0",
count: 4,
kind: VertexAttributeKind::I32,
},
VertexAttribute {
name: "aData1",
name: "aData",
count: 4,
kind: VertexAttributeKind::I32,
},
@ -1215,8 +1212,14 @@ struct VertexDataTexture {
}
impl VertexDataTexture {
fn new(device: &mut Device) -> VertexDataTexture {
let texture = device.create_texture(TextureTarget::Default, ImageFormat::RGBAF32);
fn new(
device: &mut Device,
format: ImageFormat,
) -> VertexDataTexture {
let texture = device.create_texture(
TextureTarget::Default,
format,
);
let pbo = device.create_pbo();
VertexDataTexture { texture, pbo }
@ -1369,8 +1372,9 @@ pub struct Renderer {
pub gpu_profile: GpuProfiler<GpuProfileTag>,
vaos: RendererVAOs,
node_data_texture: VertexDataTexture,
local_clip_rects_texture: VertexDataTexture,
prim_header_f_texture: VertexDataTexture,
prim_header_i_texture: VertexDataTexture,
transforms_texture: VertexDataTexture,
render_task_texture: VertexDataTexture,
gpu_cache_texture: CacheTexture,
@ -1629,9 +1633,10 @@ impl Renderer {
let texture_resolver = SourceTextureResolver::new(&mut device);
let node_data_texture = VertexDataTexture::new(&mut device);
let local_clip_rects_texture = VertexDataTexture::new(&mut device);
let render_task_texture = VertexDataTexture::new(&mut device);
let prim_header_f_texture = VertexDataTexture::new(&mut device, ImageFormat::RGBAF32);
let prim_header_i_texture = VertexDataTexture::new(&mut device, ImageFormat::RGBAI32);
let transforms_texture = VertexDataTexture::new(&mut device, ImageFormat::RGBAF32);
let render_task_texture = VertexDataTexture::new(&mut device, ImageFormat::RGBAF32);
let gpu_cache_texture = CacheTexture::new(
&mut device,
@ -1653,6 +1658,7 @@ impl Renderer {
default_font_render_mode,
dual_source_blending_is_enabled: true,
dual_source_blending_is_supported: ext_dual_source_blending,
chase_primitive: options.chase_primitive,
};
let device_pixel_ratio = options.device_pixel_ratio;
@ -1784,8 +1790,9 @@ impl Renderer {
dash_and_dot_vao,
border_vao,
},
node_data_texture,
local_clip_rects_texture,
transforms_texture,
prim_header_i_texture,
prim_header_f_texture,
render_task_texture,
pipeline_info: PipelineInfo::default(),
dither_matrix_texture,
@ -3533,16 +3540,31 @@ impl Renderer {
let _timer = self.gpu_profile.start_timer(GPU_TAG_SETUP_DATA);
self.device.set_device_pixel_ratio(frame.device_pixel_ratio);
self.node_data_texture.update(&mut self.device, &mut frame.node_data);
self.device.bind_texture(TextureSampler::ClipScrollNodes, &self.node_data_texture.texture);
self.local_clip_rects_texture.update(
self.prim_header_f_texture.update(
&mut self.device,
&mut frame.clip_chain_local_clip_rects
&mut frame.prim_headers.headers_float,
);
self.device.bind_texture(
TextureSampler::LocalClipRects,
&self.local_clip_rects_texture.texture
TextureSampler::PrimitiveHeadersF,
&self.prim_header_f_texture.texture,
);
self.prim_header_i_texture.update(
&mut self.device,
&mut frame.prim_headers.headers_int,
);
self.device.bind_texture(
TextureSampler::PrimitiveHeadersI,
&self.prim_header_i_texture.texture,
);
self.transforms_texture.update(
&mut self.device,
&mut frame.transform_palette,
);
self.device.bind_texture(
TextureSampler::TransformPalette,
&self.transforms_texture.texture,
);
self.render_task_texture
@ -3929,8 +3951,9 @@ impl Renderer {
if let Some(dither_matrix_texture) = self.dither_matrix_texture {
self.device.delete_texture(dither_matrix_texture);
}
self.node_data_texture.deinit(&mut self.device);
self.local_clip_rects_texture.deinit(&mut self.device);
self.transforms_texture.deinit(&mut self.device);
self.prim_header_f_texture.deinit(&mut self.device);
self.prim_header_i_texture.deinit(&mut self.device);
self.render_task_texture.deinit(&mut self.device);
self.device.delete_pbo(self.texture_cache_upload_pbo);
self.texture_resolver.deinit(&mut self.device);
@ -4017,11 +4040,11 @@ pub trait SceneBuilderHooks {
/// and before it processes anything.
fn register(&self);
/// This is called before each scene swap occurs.
fn pre_scene_swap(&self);
fn pre_scene_swap(&self, scenebuild_time: u64);
/// This is called after each scene swap occurs. The PipelineInfo contains
/// the updated epochs and pipelines removed in the new scene compared to
/// the old scene.
fn post_scene_swap(&self, info: PipelineInfo);
fn post_scene_swap(&self, info: PipelineInfo, sceneswap_time: u64);
/// This is called after a resource update operation on the scene builder
/// thread, in the case where resource updates were applied without a scene
/// build.
@ -4078,6 +4101,7 @@ pub struct RendererOptions {
pub disable_dual_source_blending: bool,
pub scene_builder_hooks: Option<Box<SceneBuilderHooks + Send>>,
pub sampler: Option<Box<AsyncPropertySampler + Send>>,
pub chase_primitive: ChasePrimitive,
}
impl Default for RendererOptions {
@ -4111,6 +4135,7 @@ impl Default for RendererOptions {
disable_dual_source_blending: false,
scene_builder_hooks: None,
sampler: None,
chase_primitive: ChasePrimitive::Nothing,
}
}
}

View File

@ -641,13 +641,20 @@ impl ResourceCache {
return
};
if !self.pending_image_requests.insert(request) {
return
}
// If we are tiling, then we need to confirm the dirty rect intersects
// the tile before leaving the request in the pending queue.
//
// We can start a worker thread rasterizing right now, if:
// - The image is a blob.
// - The blob hasn't already been requested this frame.
if self.pending_image_requests.insert(request) && template.data.is_blob() {
let (offset, size) = match template.tiling {
Some(tile_size) => {
let tile_offset = request.tile.unwrap();
if template.data.is_blob() || dirty_rect.is_some() {
let (offset, size) = match request.tile {
Some(tile_offset) => {
let tile_size = template.tiling.unwrap();
let actual_size = compute_tile_size(
&template.descriptor,
tile_size,
@ -671,17 +678,19 @@ impl ResourceCache {
None => (DevicePoint::zero(), template.descriptor.size),
};
if let Some(ref mut renderer) = self.blob_image_renderer {
renderer.request(
&self.resources,
request.into(),
&BlobImageDescriptor {
size,
offset,
format: template.descriptor.format,
},
dirty_rect,
);
if template.data.is_blob() {
if let Some(ref mut renderer) = self.blob_image_renderer {
renderer.request(
&self.resources,
request.into(),
&BlobImageDescriptor {
size,
offset,
format: template.descriptor.format,
},
dirty_rect,
);
}
}
}
}
@ -923,6 +932,8 @@ impl ResourceCache {
}
fn update_texture_cache(&mut self, gpu_cache: &mut GpuCache) {
let mut keys_to_clear_dirty_rect = FastHashSet::default();
for request in self.pending_image_requests.drain() {
let image_template = self.resources.image_templates.get_mut(request.key).unwrap();
debug_assert!(image_template.data.uses_texture_cache());
@ -963,23 +974,24 @@ impl ResourceCache {
let entry = self.cached_images.get_mut(&request).as_mut().unwrap();
let mut descriptor = image_template.descriptor.clone();
//TODO: erasing the dirty rectangle here is incorrect for tiled images,
// since other tile requests may follow that depend on it
let mut local_dirty_rect = image_template.dirty_rect.take();
let local_dirty_rect;
if let Some(tile) = request.tile {
let tile_size = image_template.tiling.unwrap();
let clipped_tile_size = compute_tile_size(&descriptor, tile_size, tile);
if let Some(ref mut rect) = local_dirty_rect {
match intersect_for_tile(*rect, clipped_tile_size, tile_size, tile) {
Some(intersection) => *rect = intersection,
None => {
// if re-uploaded, the dirty rect is ignored anyway
debug_assert!(self.texture_cache.needs_upload(&entry.texture_cache_handle))
}
}
}
local_dirty_rect = if let Some(ref rect) = image_template.dirty_rect {
keys_to_clear_dirty_rect.insert(request.key.clone());
// We should either have a dirty rect, or we are re-uploading where the dirty
// rect is ignored anyway.
let intersection = intersect_for_tile(*rect, clipped_tile_size, tile_size, tile);
debug_assert!(intersection.is_some() ||
self.texture_cache.needs_upload(&entry.texture_cache_handle));
intersection
} else {
None
};
// The tiled image could be stored on the CPU as one large image or be
// already broken up into tiles. This affects the way we compute the stride
@ -995,6 +1007,8 @@ impl ResourceCache {
}
descriptor.size = clipped_tile_size;
} else {
local_dirty_rect = image_template.dirty_rect.take();
}
let filter = match request.rendering {
@ -1036,6 +1050,11 @@ impl ResourceCache {
UvRectKind::Rect,
);
}
for key in keys_to_clear_dirty_rect.drain() {
let image_template = self.resources.image_templates.get_mut(key).unwrap();
image_template.dirty_rect.take();
}
}
pub fn end_frame(&mut self) {

View File

@ -13,6 +13,7 @@ use render_backend::DocumentView;
use renderer::{PipelineInfo, SceneBuilderHooks};
use scene::Scene;
use std::sync::mpsc::{channel, Receiver, Sender};
use time::precise_time_ns;
// Message from render backend to scene builder.
pub enum SceneBuilderRequest {
@ -137,6 +138,7 @@ impl SceneBuilder {
frame_ops,
render,
} => {
let scenebuild_start_time = precise_time_ns();
let built_scene = scene.map(|request|{
build_scene(&self.config, request)
});
@ -156,13 +158,15 @@ impl SceneBuilder {
};
let (tx, rx) = channel();
hooks.pre_scene_swap();
let scenebuild_time = precise_time_ns() - scenebuild_start_time;
hooks.pre_scene_swap(scenebuild_time);
(Some(info), Some(tx), Some(rx))
}
_ => (None, None, None),
};
let sceneswap_start_time = precise_time_ns();
let has_resources_updates = !resource_updates.is_empty();
self.tx.send(SceneBuilderResult::Transaction {
document_id,
@ -178,7 +182,8 @@ impl SceneBuilder {
if let Some(pipeline_info) = pipeline_info {
// Block until the swap is done, then invoke the hook.
let swap_result = result_rx.unwrap().recv();
self.hooks.as_ref().unwrap().post_scene_swap(pipeline_info);
let sceneswap_time = precise_time_ns() - sceneswap_start_time;
self.hooks.as_ref().unwrap().post_scene_swap(pipeline_info, sceneswap_time);
// Once the hook is done, allow the RB thread to resume
match swap_result {
Ok(SceneSwapResult::Complete(resume_tx)) => {

View File

@ -351,11 +351,12 @@ fn create_prim_shader(
("sDither", TextureSampler::Dither),
("sCacheA8", TextureSampler::CacheA8),
("sCacheRGBA8", TextureSampler::CacheRGBA8),
("sClipScrollNodes", TextureSampler::ClipScrollNodes),
("sTransformPalette", TextureSampler::TransformPalette),
("sRenderTasks", TextureSampler::RenderTasks),
("sResourceCache", TextureSampler::ResourceCache),
("sSharedCacheA8", TextureSampler::SharedCacheA8),
("sLocalClipRects", TextureSampler::LocalClipRects),
("sPrimitiveHeadersF", TextureSampler::PrimitiveHeadersF),
("sPrimitiveHeadersI", TextureSampler::PrimitiveHeadersI),
],
);
}
@ -379,11 +380,12 @@ fn create_clip_shader(name: &'static str, device: &mut Device) -> Result<Program
program,
&[
("sColor0", TextureSampler::Color0),
("sClipScrollNodes", TextureSampler::ClipScrollNodes),
("sTransformPalette", TextureSampler::TransformPalette),
("sRenderTasks", TextureSampler::RenderTasks),
("sResourceCache", TextureSampler::ResourceCache),
("sSharedCacheA8", TextureSampler::SharedCacheA8),
("sLocalClipRects", TextureSampler::LocalClipRects),
("sPrimitiveHeadersF", TextureSampler::PrimitiveHeadersF),
("sPrimitiveHeadersI", TextureSampler::PrimitiveHeadersI),
],
);
}

View File

@ -501,6 +501,7 @@ impl TextureCache {
(ImageFormat::BGRA8, TextureFilter::Nearest) => &mut self.array_rgba8_nearest,
(ImageFormat::RGBAF32, _) |
(ImageFormat::RG8, _) |
(ImageFormat::RGBAI32, _) |
(ImageFormat::R8, TextureFilter::Nearest) |
(ImageFormat::R8, TextureFilter::Trilinear) |
(ImageFormat::BGRA8, TextureFilter::Trilinear) => unreachable!(),
@ -725,6 +726,7 @@ impl TextureCache {
(ImageFormat::BGRA8, TextureFilter::Linear) => &mut self.array_rgba8_linear,
(ImageFormat::BGRA8, TextureFilter::Nearest) => &mut self.array_rgba8_nearest,
(ImageFormat::RGBAF32, _) |
(ImageFormat::RGBAI32, _) |
(ImageFormat::R8, TextureFilter::Nearest) |
(ImageFormat::R8, TextureFilter::Trilinear) |
(ImageFormat::BGRA8, TextureFilter::Trilinear) |

View File

@ -12,8 +12,7 @@ use device::{FrameId, Texture};
#[cfg(feature = "pathfinder")]
use euclid::{TypedPoint2D, TypedVector2D};
use gpu_cache::{GpuCache};
use gpu_types::{BorderInstance, BlurDirection, BlurInstance};
use gpu_types::{ClipScrollNodeData, ZBufferIdGenerator};
use gpu_types::{BorderInstance, BlurDirection, BlurInstance, PrimitiveHeaders, TransformData, TransformPalette};
use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture};
#[cfg(feature = "pathfinder")]
use pathfinder_partitioner::mesh::Mesh;
@ -48,7 +47,7 @@ pub struct RenderTargetContext<'a, 'rc> {
pub resource_cache: &'rc mut ResourceCache,
pub clip_scroll_tree: &'a ClipScrollTree,
pub use_dual_source_blending: bool,
pub node_data: &'a [ClipScrollNodeData],
pub transforms: &'a TransformPalette,
}
#[cfg_attr(feature = "capture", derive(Serialize))]
@ -103,6 +102,7 @@ pub trait RenderTarget {
_gpu_cache: &mut GpuCache,
_render_tasks: &mut RenderTaskTree,
_deferred_resolves: &mut Vec<DeferredResolve>,
_prim_headers: &mut PrimitiveHeaders,
) {
}
// TODO(gw): It's a bit odd that we need the deferred resolves and mutable
@ -166,12 +166,19 @@ impl<T: RenderTarget> RenderTargetList<T> {
render_tasks: &mut RenderTaskTree,
deferred_resolves: &mut Vec<DeferredResolve>,
saved_index: Option<SavedTargetIndex>,
prim_headers: &mut PrimitiveHeaders,
) {
debug_assert_eq!(None, self.saved_index);
self.saved_index = saved_index;
for target in &mut self.targets {
target.build(ctx, gpu_cache, render_tasks, deferred_resolves);
target.build(
ctx,
gpu_cache,
render_tasks,
deferred_resolves,
prim_headers,
);
}
}
@ -333,9 +340,9 @@ impl RenderTarget for ColorRenderTarget {
gpu_cache: &mut GpuCache,
render_tasks: &mut RenderTaskTree,
deferred_resolves: &mut Vec<DeferredResolve>,
prim_headers: &mut PrimitiveHeaders,
) {
let mut merged_batches = AlphaBatchContainer::new(None);
let mut z_generator = ZBufferIdGenerator::new();
for task_id in &self.alpha_tasks {
let task = &render_tasks[*task_id];
@ -358,7 +365,7 @@ impl RenderTarget for ColorRenderTarget {
gpu_cache,
render_tasks,
deferred_resolves,
&mut z_generator,
prim_headers,
);
if let Some(batch_container) = batch_builder.build(&mut merged_batches) {
@ -594,6 +601,7 @@ impl RenderTarget for AlphaRenderTarget {
ctx.resource_cache,
gpu_cache,
clip_store,
ctx.transforms,
);
}
RenderTaskKind::ClipRegion(ref task) => {
@ -795,6 +803,7 @@ impl RenderPass {
render_tasks: &mut RenderTaskTree,
deferred_resolves: &mut Vec<DeferredResolve>,
clip_store: &ClipStore,
prim_headers: &mut PrimitiveHeaders,
) {
profile_scope!("RenderPass::build");
@ -811,7 +820,13 @@ impl RenderPass {
deferred_resolves,
);
}
target.build(ctx, gpu_cache, render_tasks, deferred_resolves);
target.build(
ctx,
gpu_cache,
render_tasks,
deferred_resolves,
prim_headers,
);
}
RenderPassKind::OffScreen { ref mut color, ref mut alpha, ref mut texture_cache } => {
let is_shared_alpha = self.tasks.iter().any(|&task_id| {
@ -911,8 +926,22 @@ impl RenderPass {
}
}
color.build(ctx, gpu_cache, render_tasks, deferred_resolves, saved_color);
alpha.build(ctx, gpu_cache, render_tasks, deferred_resolves, saved_alpha);
color.build(
ctx,
gpu_cache,
render_tasks,
deferred_resolves,
saved_color,
prim_headers,
);
alpha.build(
ctx,
gpu_cache,
render_tasks,
deferred_resolves,
saved_alpha,
prim_headers,
);
alpha.is_shared = is_shared_alpha;
}
}
@ -956,9 +985,9 @@ pub struct Frame {
#[cfg_attr(any(feature = "capture", feature = "replay"), serde(default = "FrameProfileCounters::new", skip))]
pub profile_counters: FrameProfileCounters,
pub node_data: Vec<ClipScrollNodeData>,
pub clip_chain_local_clip_rects: Vec<LayoutRect>,
pub transform_palette: Vec<TransformData>,
pub render_tasks: RenderTaskTree,
pub prim_headers: PrimitiveHeaders,
/// The GPU cache frame that the contents of Self depend on
pub gpu_cache_frame_id: FrameId,

View File

@ -1103,7 +1103,7 @@ pub struct DynamicProperties {
pub trait RenderNotifier: Send {
fn clone(&self) -> Box<RenderNotifier>;
fn wake_up(&self);
fn new_frame_ready(&self, DocumentId, scrolled: bool, composite_needed: bool);
fn new_frame_ready(&self, DocumentId, scrolled: bool, composite_needed: bool, render_time_ns: Option<u64>);
fn external_event(&self, _evt: ExternalEvent) {
unimplemented!()
}

View File

@ -115,7 +115,7 @@ pub struct ColorU {
impl ColorU {
/// Constructs a new additive `ColorU` from its components.
pub fn new(r: u8, g: u8, b: u8, a: u8) -> ColorU {
pub fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
ColorU { r, g, b, a }
}
}

View File

@ -273,22 +273,10 @@ pub enum RepeatMode {
}
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub struct GradientBorder {
pub gradient: Gradient,
pub outset: SideOffsets2D<f32>,
}
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub struct RadialGradientBorder {
pub gradient: RadialGradient,
pub outset: SideOffsets2D<f32>,
}
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
/// TODO(mrobinson): Currently only images are supported, but we will
/// eventually add support for Gradient and RadialGradient.
pub enum NinePatchBorderSource {
Image(ImageKey),
Gradient(Gradient),
RadialGradient(RadialGradient),
}
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
@ -333,8 +321,6 @@ pub struct NinePatchBorder {
pub enum BorderDetails {
Normal(NormalBorder),
NinePatch(NinePatchBorder),
Gradient(GradientBorder),
RadialGradient(RadialGradientBorder),
}
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]

View File

@ -27,8 +27,8 @@ use {StickyFrameDisplayItem, StickyOffsetBounds, TextDisplayItem, TransformStyle
use {YuvData, YuvImageDisplayItem};
// We don't want to push a long text-run. If a text-run is too long, split it into several parts.
// This needs to be set to (renderer::MAX_VERTEX_TEXTURE_WIDTH - VECS_PER_PRIM_HEADER - VECS_PER_TEXT_RUN) * 2
pub const MAX_TEXT_RUN_LENGTH: usize = 2038;
// This needs to be set to (renderer::MAX_VERTEX_TEXTURE_WIDTH - VECS_PER_TEXT_RUN) * 2
pub const MAX_TEXT_RUN_LENGTH: usize = 2040;
// We start at 2, because the root reference is always 0 and the root scroll node is always 1.
const FIRST_CLIP_ID: usize = 2;

View File

@ -59,6 +59,7 @@ pub enum ImageFormat {
BGRA8 = 3,
RGBAF32 = 4,
RG8 = 5,
RGBAI32 = 6,
}
impl ImageFormat {
@ -68,6 +69,7 @@ impl ImageFormat {
ImageFormat::BGRA8 => 4,
ImageFormat::RGBAF32 => 16,
ImageFormat::RG8 => 2,
ImageFormat::RGBAI32 => 16,
}
}
}

View File

@ -1 +1 @@
cf98ad4d63729c678a7575eb9bce36794da5e270
cdfaaeb5f74e416f39af1081c9a676c752d23896

View File

@ -93,6 +93,7 @@ enum class ImageFormat : uint32_t {
BGRA8 = 3,
RGBAF32 = 4,
RG8 = 5,
RGBAI32 = 6,
Sentinel /* this must be last for serialization purposes. */
};

View File

@ -14,7 +14,7 @@ euclid = "0.17"
gleam = "0.5"
glutin = "0.15"
app_units = "0.6"
image = "0.18"
image = "0.19"
clap = { version = "2", features = ["yaml"] }
lazy_static = "1"
log = "0.4"

View File

@ -67,6 +67,10 @@ args:
- no_batch:
long: no-batch
help: Disable batching of instanced draw calls
- chase:
long: chase
help: Chase a particular primitive matching the local rect
takes_value: true
subcommands:
- png:

View File

@ -257,7 +257,7 @@ fn make_window(
})
.with_vsync(vsync);
let window_builder = winit::WindowBuilder::new()
.with_title("WRech")
.with_title("WRench")
.with_multitouch()
.with_dimensions(size.width, size.height);
@ -351,7 +351,11 @@ impl RenderNotifier for Notifier {
self.tx.send(NotifierEvent::ShutDown).unwrap();
}
fn new_frame_ready(&self, _: DocumentId, _scrolled: bool, composite_needed: bool) {
fn new_frame_ready(&self,
_: DocumentId,
_scrolled: bool,
composite_needed: bool,
_render_time: Option<u64>) {
if composite_needed {
self.wake_up();
}
@ -424,6 +428,20 @@ fn main() {
})
.unwrap_or(DeviceUintSize::new(1920, 1080));
let zoom_factor = args.value_of("zoom").map(|z| z.parse::<f32>().unwrap());
let chase_primitive = match args.value_of("chase") {
Some(s) => {
let mut items = s
.split(',')
.map(|s| s.parse::<f32>().unwrap())
.collect::<Vec<_>>();
let rect = LayoutRect::new(
LayoutPoint::new(items[0], items[1]),
LayoutSize::new(items[2], items[3]),
);
webrender::ChasePrimitive::LocalRect(rect)
},
None => webrender::ChasePrimitive::Nothing,
};
let mut events_loop = if args.is_present("headless") {
None
@ -462,6 +480,7 @@ fn main() {
args.is_present("precache"),
args.is_present("slow_subpixel"),
zoom_factor.unwrap_or(1.0),
chase_primitive,
notifier,
);
@ -634,17 +653,30 @@ fn render<'a>(
let path = PathBuf::from("../captures/wrench");
wrench.api.save_capture(path, CaptureBits::all());
}
VirtualKeyCode::Up => {
VirtualKeyCode::Up | VirtualKeyCode::Down => {
let mut txn = Transaction::new();
let offset = match vk {
winit::VirtualKeyCode::Up => LayoutVector2D::new(0.0, 10.0),
winit::VirtualKeyCode::Down => LayoutVector2D::new(0.0, -10.0),
_ => unreachable!("Should not see non directional keys here.")
};
txn.scroll(ScrollLocation::Delta(offset), cursor_position);
txn.generate_frame();
wrench.api.send_transaction(wrench.document_id, txn);
do_frame = true;
}
VirtualKeyCode::Add => {
let current_zoom = wrench.get_page_zoom();
let new_zoom_factor = ZoomFactor::new(current_zoom.get() + 0.1);
wrench.set_page_zoom(new_zoom_factor);
do_frame = true;
}
VirtualKeyCode::Down => {
VirtualKeyCode::Subtract => {
let current_zoom = wrench.get_page_zoom();
let new_zoom_factor = ZoomFactor::new((current_zoom.get() - 0.1).max(0.1));
wrench.set_page_zoom(new_zoom_factor);
do_frame = true;
}

View File

@ -109,7 +109,10 @@ impl RenderNotifier for Notifier {
self.update(false);
}
fn new_frame_ready(&self, _: DocumentId, scrolled: bool, _composite_needed: bool) {
fn new_frame_ready(&self, _: DocumentId,
scrolled: bool,
_composite_needed: bool,
_render_time: Option<u64>) {
self.update(!scrolled);
}
}
@ -180,6 +183,7 @@ impl Wrench {
precache_shaders: bool,
disable_dual_source_blending: bool,
zoom_factor: f32,
chase_primitive: webrender::ChasePrimitive,
notifier: Option<Box<RenderNotifier>>,
) -> Self {
println!("Shader override path: {:?}", shader_override_path);
@ -212,6 +216,7 @@ impl Wrench {
precache_shaders,
blob_image_renderer: Some(Box::new(blob::CheckerboardRenderer::new(callbacks.clone()))),
disable_dual_source_blending,
chase_primitive,
..Default::default()
};

View File

@ -173,7 +173,8 @@ fn is_image_opaque(format: ImageFormat, bytes: &[u8]) -> bool {
}
ImageFormat::RG8 => true,
ImageFormat::R8 => false,
ImageFormat::RGBAF32 => unreachable!(),
ImageFormat::RGBAF32 |
ImageFormat::RGBAI32 => unreachable!(),
}
}
@ -880,26 +881,28 @@ impl YamlFrameReader {
radius,
}))
}
"image" => {
let file = rsrc_path(&item["image-source"], &self.aux_dir);
let (image_key, _) = self
.add_or_get_image(&file, None, wrench);
"image" | "gradient" | "radial-gradient" => {
let image_width = item["image-width"]
.as_i64()
.expect("border must have image-width");
.unwrap_or(info.rect.size.width as i64);
let image_height = item["image-height"]
.as_i64()
.expect("border must have image-height");
.unwrap_or(info.rect.size.height as i64);
let fill = item["fill"].as_bool().unwrap_or(false);
let slice = item["slice"].as_vec_u32().expect("border must have slice");
let slice = broadcast(&slice, 4);
let slice = item["slice"].as_vec_u32();
let slice = match slice {
Some(slice) => broadcast(&slice, 4),
None => vec![widths.top as u32, widths.left as u32, widths.bottom as u32, widths.right as u32],
};
let outset = item["outset"]
.as_vec_f32()
.expect("border must have outset");
let outset = broadcast(&outset, 4);
let repeat_horizontal = match item["repeat-horizontal"]
.as_str()
.expect("border must have repeat-horizontal")
.unwrap_or("stretch")
{
"stretch" => RepeatMode::Stretch,
"repeat" => RepeatMode::Repeat,
@ -909,7 +912,7 @@ impl YamlFrameReader {
};
let repeat_vertical = match item["repeat-vertical"]
.as_str()
.expect("border must have repeat-vertical")
.unwrap_or("stretch")
{
"stretch" => RepeatMode::Stretch,
"repeat" => RepeatMode::Repeat,
@ -917,8 +920,27 @@ impl YamlFrameReader {
"space" => RepeatMode::Space,
s => panic!("Unknown box border image repeat mode {}", s),
};
let source = match border_type {
"image" => {
let file = rsrc_path(&item["image-source"], &self.aux_dir);
let (image_key, _) = self
.add_or_get_image(&file, None, wrench);
NinePatchBorderSource::Image(image_key)
}
"gradient" => {
let gradient = self.to_gradient(dl, item);
NinePatchBorderSource::Gradient(gradient)
}
"radial-gradient" => {
let gradient = self.to_radial_gradient(dl, item);
NinePatchBorderSource::RadialGradient(gradient)
}
_ => unreachable!("Unexpected border type"),
};
Some(BorderDetails::NinePatch(NinePatchBorder {
source: NinePatchBorderSource::Image(image_key),
source,
width: image_width as u32,
height: image_height as u32,
slice: SideOffsets2D::new(slice[0], slice[1], slice[2], slice[3]),
@ -928,28 +950,6 @@ impl YamlFrameReader {
outset: SideOffsets2D::new(outset[0], outset[1], outset[2], outset[3]),
}))
}
"gradient" => {
let gradient = self.to_gradient(dl, item);
let outset = item["outset"]
.as_vec_f32()
.expect("borders must have outset");
let outset = broadcast(&outset, 4);
Some(BorderDetails::Gradient(GradientBorder {
gradient,
outset: SideOffsets2D::new(outset[0], outset[1], outset[2], outset[3]),
}))
}
"radial-gradient" => {
let gradient = self.to_radial_gradient(dl, item);
let outset = item["outset"]
.as_vec_f32()
.expect("borders must have outset");
let outset = broadcast(&outset, 4);
Some(BorderDetails::RadialGradient(RadialGradientBorder {
gradient,
outset: SideOffsets2D::new(outset[0], outset[1], outset[2], outset[3]),
}))
}
_ => {
println!("Unable to parse border {:?}", item);
None
@ -1391,7 +1391,7 @@ impl YamlFrameReader {
clip_rect,
complex_clips,
image_mask,
ScrollSensitivity::Script,
ScrollSensitivity::ScriptAndInputEvents,
);
if let Some(numeric_id) = numeric_id {
self.add_clip_id_mapping(numeric_id, real_id);

View File

@ -885,14 +885,35 @@ impl YamlFrameWriter {
details.outset.left,
];
yaml_node(&mut v, "width", f32_vec_yaml(&widths, true));
str_node(&mut v, "border-type", "image");
match details.source {
NinePatchBorderSource::Image(image_key) => {
str_node(&mut v, "border-type", "image");
if let Some(path) = self.path_for_image(image_key) {
path_node(&mut v, "image", &path);
}
}
NinePatchBorderSource::Gradient(gradient) => {
str_node(&mut v, "gradient", "image");
point_node(&mut v, "start", &gradient.start_point);
point_node(&mut v, "end", &gradient.end_point);
let mut stops = vec![];
for stop in display_list.get(base.gradient_stops()) {
stops.push(Yaml::Real(stop.offset.to_string()));
stops.push(Yaml::String(color_to_string(stop.color)));
}
yaml_node(&mut v, "stops", Yaml::Array(stops));
bool_node(&mut v, "repeat", gradient.extend_mode == ExtendMode::Repeat);
}
NinePatchBorderSource::RadialGradient(gradient) => {
str_node(&mut v, "border-type", "radial-gradient");
radial_gradient_to_yaml(
&mut v,
&gradient,
base.gradient_stops(),
display_list
);
}
}
u32_node(&mut v, "image-width", details.width);
@ -924,59 +945,6 @@ impl YamlFrameWriter {
RepeatMode::Space => str_node(&mut v, "repeat-vertical", "space"),
};
}
BorderDetails::Gradient(ref details) => {
let widths: Vec<f32> = vec![
item.widths.top,
item.widths.right,
item.widths.bottom,
item.widths.left,
];
let outset: Vec<f32> = vec![
details.outset.top,
details.outset.right,
details.outset.bottom,
details.outset.left,
];
yaml_node(&mut v, "width", f32_vec_yaml(&widths, true));
str_node(&mut v, "border-type", "gradient");
point_node(&mut v, "start", &details.gradient.start_point);
point_node(&mut v, "end", &details.gradient.end_point);
let mut stops = vec![];
for stop in display_list.get(base.gradient_stops()) {
stops.push(Yaml::Real(stop.offset.to_string()));
stops.push(Yaml::String(color_to_string(stop.color)));
}
yaml_node(&mut v, "stops", Yaml::Array(stops));
bool_node(
&mut v,
"repeat",
details.gradient.extend_mode == ExtendMode::Repeat,
);
yaml_node(&mut v, "outset", f32_vec_yaml(&outset, true));
}
BorderDetails::RadialGradient(ref details) => {
let widths: Vec<f32> = vec![
item.widths.top,
item.widths.right,
item.widths.bottom,
item.widths.left,
];
let outset: Vec<f32> = vec![
details.outset.top,
details.outset.right,
details.outset.bottom,
details.outset.left,
];
yaml_node(&mut v, "width", f32_vec_yaml(&widths, true));
str_node(&mut v, "border-type", "radial-gradient");
yaml_node(&mut v, "outset", f32_vec_yaml(&outset, true));
radial_gradient_to_yaml(
&mut v,
&details.gradient,
base.gradient_stops(),
display_list
);
}
}
}
BoxShadow(item) => {