diff --git a/res/blur.fs.glsl b/res/blur.fs.glsl index c20a774f48..2af2a7ebab 100644 --- a/res/blur.fs.glsl +++ b/res/blur.fs.glsl @@ -28,7 +28,7 @@ void main(void) { lColorTexCoord.x <= 1.0 && lColorTexCoord.y >= 0.0 && lColorTexCoord.y <= 1.0 ? - Texture(sDiffuse, lColorTexCoord * sourceTextureUvSize + sourceTextureUvOrigin) : + texture(sDiffuse, lColorTexCoord * sourceTextureUvSize + sourceTextureUvOrigin) : vec4(0.0); // Alpha must be premultiplied in order to properly blur the alpha channel. diff --git a/res/border.fs.glsl b/res/border.fs.glsl deleted file mode 100644 index 32d1930aa5..0000000000 --- a/res/border.fs.glsl +++ /dev/null @@ -1,41 +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/. */ - -/* - Ellipse equation: - - (x-h)^2 (y-k)^2 - ------- + ------- <= 1 - rx^2 ry^2 - - */ - -float Value(vec2 position) { - float outer_rx = vBorderRadii.x; - float outer_ry = vBorderRadii.y; - float outer_dx = position.x * position.x / (outer_rx * outer_rx); - float outer_dy = position.y * position.y / (outer_ry * outer_ry); - if (outer_dx + outer_dy > 1.0) - return 0.0; - - float inner_rx = vBorderRadii.z; - float inner_ry = vBorderRadii.w; - if (inner_rx == 0.0 || inner_ry == 0.0) - return 1.0; - - float inner_dx = position.x * position.x / (inner_rx * inner_rx); - float inner_dy = position.y * position.y / (inner_ry * inner_ry); - return inner_dx + inner_dy >= 1.0 ? 1.0 : 0.0; -} - -void main(void) -{ - vec2 position = vPosition - vBorderPosition.xy; - vec4 pixelBounds = vec4(floor(position.x), floor(position.y), - ceil(position.x), ceil(position.y)); - float value = (Value(pixelBounds.xy) + Value(pixelBounds.zy) + - Value(pixelBounds.xw) + Value(pixelBounds.zw)) / 4.0; - SetFragColor(vec4(vColor.rgb, mix(1.0 - vColor.a, vColor.a, value))); -} - diff --git a/res/border.vs.glsl b/res/border.vs.glsl deleted file mode 100644 index cbd9cdcd0c..0000000000 --- a/res/border.vs.glsl +++ /dev/null @@ -1,12 +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/. */ - -void main(void) -{ - vColor = aColorRectTL; - vPosition = aPosition.xy; - vBorderPosition = aBorderPosition; - vBorderRadii = aBorderRadii; - gl_Position = uTransform * vec4(aPosition, 1.0); -} diff --git a/res/debug_color.fs.glsl b/res/debug_color.fs.glsl index 658ac27488..e9aae64138 100644 --- a/res/debug_color.fs.glsl +++ b/res/debug_color.fs.glsl @@ -4,5 +4,5 @@ void main(void) { - SetFragColor(vColor); + oFragColor = vColor; } diff --git a/res/debug_font.fs.glsl b/res/debug_font.fs.glsl index 8b68e085cf..a10e0da4ce 100644 --- a/res/debug_font.fs.glsl +++ b/res/debug_font.fs.glsl @@ -5,9 +5,9 @@ void main(void) { #ifdef SERVO_ES2 - float alpha = Texture(sDiffuse, vColorTexCoord.xy).a; + float alpha = texture(sDiffuse, vColorTexCoord.xy).a; #else - float alpha = Texture(sDiffuse, vColorTexCoord.xy).r; + float alpha = texture(sDiffuse, vColorTexCoord.xy).r; #endif - SetFragColor(vec4(vColor.xyz, vColor.w * alpha)); + oFragColor = vec4(vColor.xyz, vColor.w * alpha); } diff --git a/res/prim_shared.glsl b/res/prim_shared.glsl new file mode 100644 index 0000000000..7c21cada7f --- /dev/null +++ b/res/prim_shared.glsl @@ -0,0 +1,180 @@ +#line 1 +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#define PST_INVALID uint(0) +#define PST_TOP_LEFT uint(1) +#define PST_TOP_RIGHT uint(2) +#define PST_BOTTOM_LEFT uint(3) +#define PST_BOTTOM_RIGHT uint(4) +#define PST_TOP uint(5) +#define PST_LEFT uint(6) +#define PST_BOTTOM uint(7) +#define PST_RIGHT uint(8) + +// Border styles as defined in webrender_traits/types.rs +#define BORDER_STYLE_NONE uint(0) +#define BORDER_STYLE_SOLID uint(1) +#define BORDER_STYLE_DOUBLE uint(2) +#define BORDER_STYLE_DOTTED uint(3) +#define BORDER_STYLE_DASHED uint(4) +#define BORDER_STYLE_HIDDEN uint(5) +#define BORDER_STYLE_GROOVE uint(6) +#define BORDER_STYLE_RIDGE uint(7) +#define BORDER_STYLE_INSET uint(8) +#define BORDER_STYLE_OUTSET uint(9) + +#ifdef WR_VERTEX_SHADER +struct Layer { + mat4 transform; + mat4 inv_transform; + ivec4 world_clip_rect; + vec4 screen_vertices[4]; +}; + +layout(std140) uniform Layers { + Layer layers[WR_MAX_PRIM_LAYERS]; +}; + +struct Tile { + uvec4 actual_rect; + uvec4 target_rect; +}; + +layout(std140) uniform Tiles { + Tile tiles[WR_MAX_PRIM_TILES]; +}; + +struct PrimitiveInfo { + uvec4 layer_tile_part; + vec4 local_clip_rect; + vec4 local_rect; +}; + +struct ClipCorner { + vec4 rect; + vec4 outer_inner_radius; +}; + +struct Clip { + vec4 rect; + ClipCorner top_left; + ClipCorner top_right; + ClipCorner bottom_left; + ClipCorner bottom_right; +}; + +bool ray_plane(vec3 normal, vec3 point, vec3 ray_origin, vec3 ray_dir, out float t) +{ + float denom = dot(normal, ray_dir); + if (denom > 1e-6) { + vec3 d = point - ray_origin; + t = dot(d, normal) / denom; + return t >= 0.0; + } + + return false; +} + +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; + ray_plane(n, a, p, d, t); + vec3 c = p + d * t; + + vec4 r = inv_transform * vec4(c, 1.0); + return r; +} + +vec3 get_layer_pos(vec2 pos, uint layer_index) { + Layer layer = layers[layer_index]; + vec3 a = layer.screen_vertices[0].xyz / layer.screen_vertices[0].w; + vec3 b = layer.screen_vertices[3].xyz / layer.screen_vertices[3].w; + vec3 c = layer.screen_vertices[2].xyz / layer.screen_vertices[2].w; + vec3 n = normalize(cross(b-a, c-a)); + vec4 local_pos = untransform(pos, n, a, layer.inv_transform); + return local_pos.xyw; +} + +struct Rect { + vec2 p0; + vec2 p1; +}; + +struct VertexInfo { + Rect local_rect; + vec2 local_clamped_pos; + vec2 global_clamped_pos; +}; + +VertexInfo write_vertex(PrimitiveInfo info) { + Layer layer = layers[info.layer_tile_part.x]; + Tile tile = tiles[info.layer_tile_part.y]; + + vec2 p0 = floor(0.5 + info.local_rect.xy * uDevicePixelRatio) / uDevicePixelRatio; + vec2 p1 = floor(0.5 + (info.local_rect.xy + info.local_rect.zw) * uDevicePixelRatio) / uDevicePixelRatio; + + vec2 local_pos = mix(p0, p1, aPosition.xy); + + vec2 cp0 = floor(0.5 + info.local_clip_rect.xy * uDevicePixelRatio) / uDevicePixelRatio; + vec2 cp1 = floor(0.5 + (info.local_clip_rect.xy + info.local_clip_rect.zw) * uDevicePixelRatio) / uDevicePixelRatio; + local_pos = clamp(local_pos, cp0, cp1); + + vec4 world_pos = layer.transform * vec4(local_pos, 0, 1); + world_pos.xyz /= world_pos.w; + + vec2 device_pos = world_pos.xy * uDevicePixelRatio; + + vec2 clamped_pos = clamp(device_pos, + vec2(tile.actual_rect.xy), + vec2(tile.actual_rect.xy + tile.actual_rect.zw)); + + clamped_pos = clamp(clamped_pos, + vec2(layer.world_clip_rect.xy), + vec2(layer.world_clip_rect.xy + layer.world_clip_rect.zw)); + + vec4 local_clamped_pos = layer.inv_transform * vec4(clamped_pos / uDevicePixelRatio, world_pos.z, 1); + local_clamped_pos.xyz /= local_clamped_pos.w; + + vec2 final_pos = clamped_pos + vec2(tile.target_rect.xy) - vec2(tile.actual_rect.xy); + + gl_Position = uTransform * vec4(final_pos, 0, 1); + + VertexInfo vi = VertexInfo(Rect(p0, p1), local_clamped_pos.xy, clamped_pos.xy); + return vi; +} +#endif + +#ifdef WR_FRAGMENT_SHADER +void do_clip(vec2 pos, vec4 clip_rect, vec4 radius) { + vec2 ref_tl = clip_rect.xy + vec2( radius.x, radius.x); + vec2 ref_tr = clip_rect.zy + vec2(-radius.y, radius.y); + vec2 ref_br = clip_rect.zw + vec2(-radius.z, -radius.z); + vec2 ref_bl = clip_rect.xw + vec2( radius.w, -radius.w); + + float d_tl = distance(pos, ref_tl); + float d_tr = distance(pos, ref_tr); + float d_br = distance(pos, ref_br); + float d_bl = distance(pos, ref_bl); + + bool out0 = pos.x < ref_tl.x && pos.y < ref_tl.y && d_tl > radius.x; + bool out1 = pos.x > ref_tr.x && pos.y < ref_tr.y && d_tr > radius.y; + bool out2 = pos.x > ref_br.x && pos.y > ref_br.y && d_br > radius.z; + bool out3 = pos.x < ref_bl.x && pos.y > ref_bl.y && d_bl > radius.w; + + // TODO(gw): Alpha anti-aliasing based on edge distance! + if (out0 || out1 || out2 || out3) { + discard; + } +} + +bool point_in_rect(vec2 p, vec2 p0, vec2 p1) { + return p.x >= p0.x && + p.y >= p0.y && + p.x <= p1.x && + p.y <= p1.y; +} +#endif diff --git a/res/ps_angle_gradient.fs.glsl b/res/ps_angle_gradient.fs.glsl new file mode 100644 index 0000000000..70f42a1955 --- /dev/null +++ b/res/ps_angle_gradient.fs.glsl @@ -0,0 +1,39 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +float offset(int index) { + return vOffsets[index / 4][index % 4]; +} + +float linearStep(float lo, float hi, float x) { + float d = hi - lo; + float v = x - lo; + if (d != 0.0) { + v /= d; + } + return clamp(v, 0.0, 1.0); +} + +void main(void) { + float angle = atan(-vEndPoint.y + vStartPoint.y, + vEndPoint.x - vStartPoint.x); + float sa = sin(angle); + float ca = cos(angle); + + float sx = vStartPoint.x * ca - vStartPoint.y * sa; + float ex = vEndPoint.x * ca - vEndPoint.y * sa; + float d = ex - sx; + + float x = vPos.x * ca - vPos.y * sa; + + oFragColor = mix(vColors[0], + vColors[1], + linearStep(sx + d * offset(0), sx + d * offset(1), x)); + + for (int i=1 ; i < vStopCount-1 ; ++i) { + oFragColor = mix(oFragColor, + vColors[i+1], + linearStep(sx + d * offset(i), sx + d * offset(i+1), x)); + } +} diff --git a/res/ps_angle_gradient.glsl b/res/ps_angle_gradient.glsl new file mode 100644 index 0000000000..fae2211af5 --- /dev/null +++ b/res/ps_angle_gradient.glsl @@ -0,0 +1,13 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#define MAX_STOPS_PER_ANGLE_GRADIENT 8 + +flat varying int vStopCount; +flat varying float vAngle; +flat varying vec2 vStartPoint; +flat varying vec2 vEndPoint; +varying vec2 vPos; +flat varying vec4 vColors[MAX_STOPS_PER_ANGLE_GRADIENT]; +flat varying vec4 vOffsets[MAX_STOPS_PER_ANGLE_GRADIENT/4]; diff --git a/res/ps_angle_gradient.vs.glsl b/res/ps_angle_gradient.vs.glsl new file mode 100644 index 0000000000..16296967bd --- /dev/null +++ b/res/ps_angle_gradient.vs.glsl @@ -0,0 +1,38 @@ +#line 1 +/* 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/. */ + +struct AngleGradient { + PrimitiveInfo info; + vec4 start_end_point; + uvec4 stop_count; + vec4 colors[MAX_STOPS_PER_ANGLE_GRADIENT]; + vec4 offsets[MAX_STOPS_PER_ANGLE_GRADIENT/4]; +}; + +layout(std140) uniform Items { + AngleGradient gradients[WR_MAX_PRIM_ITEMS]; +}; + +void main(void) { + AngleGradient gradient = gradients[gl_InstanceID]; + VertexInfo vi = write_vertex(gradient.info); + + vStopCount = int(gradient.stop_count.x); + vPos = vi.local_clamped_pos; + + // Snap the start/end points to device pixel units. + // I'm not sure this is entirely correct, but the + // old render path does this, and it is needed to + // make the angle gradient ref tests pass. It might + // be better to fix this higher up in DL construction + // and not snap here? + vStartPoint = floor(0.5 + gradient.start_end_point.xy * uDevicePixelRatio) / uDevicePixelRatio; + vEndPoint = floor(0.5 + gradient.start_end_point.zw * uDevicePixelRatio) / uDevicePixelRatio; + + for (int i=0 ; i < int(gradient.stop_count.x) ; ++i) { + vColors[i] = gradient.colors[i]; + vOffsets[i] = gradient.offsets[i]; + } +} diff --git a/res/ps_blend.fs.glsl b/res/ps_blend.fs.glsl new file mode 100644 index 0000000000..12ccbf0b6c --- /dev/null +++ b/res/ps_blend.fs.glsl @@ -0,0 +1,10 @@ +/* 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/. */ + +uniform sampler2D sCache; + +void main(void) { + vec4 color = texture(sCache, vUv); + oFragColor = vec4(color.rgb, color.a * vOpacity); +} diff --git a/res/clear.fs.glsl b/res/ps_blend.glsl similarity index 81% rename from res/clear.fs.glsl rename to res/ps_blend.glsl index 658ac27488..773cf91e27 100644 --- a/res/clear.fs.glsl +++ b/res/ps_blend.glsl @@ -2,7 +2,5 @@ * 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/. */ -void main(void) -{ - SetFragColor(vColor); -} +varying vec2 vUv; +varying float vOpacity; diff --git a/res/ps_blend.vs.glsl b/res/ps_blend.vs.glsl new file mode 100644 index 0000000000..eb61903b5c --- /dev/null +++ b/res/ps_blend.vs.glsl @@ -0,0 +1,29 @@ +#line 1 +/* 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/. */ + +struct Blend { + uvec4 target_rect; + uvec4 src_rect; + vec4 opacity; +}; + +layout(std140) uniform Items { + Blend blends[WR_MAX_PRIM_ITEMS]; +}; + +void main(void) { + Blend blend = blends[gl_InstanceID]; + + vec2 local_pos = mix(vec2(blend.target_rect.xy), + vec2(blend.target_rect.xy + blend.target_rect.zw), + aPosition.xy); + + vec2 st0 = vec2(blend.src_rect.xy) / 2048.0; + vec2 st1 = vec2(blend.src_rect.xy + blend.src_rect.zw) / 2048.0; + vUv = mix(st0, st1, aPosition.xy); + vOpacity = blend.opacity.x; + + gl_Position = uTransform * vec4(local_pos, 0, 1); +} diff --git a/res/ps_border.fs.glsl b/res/ps_border.fs.glsl new file mode 100644 index 0000000000..350b8db702 --- /dev/null +++ b/res/ps_border.fs.glsl @@ -0,0 +1,187 @@ +/* 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/. */ + +// draw a circle at position aDesiredPos with a aRadius +vec4 drawCircle(vec2 aPixel, vec2 aDesiredPos, float aRadius, vec3 aColor) { + float farFromCenter = length(aDesiredPos - aPixel) - aRadius; + float pixelInCircle = 1.00 - clamp(farFromCenter, 0.0, 1.0); + return vec4(aColor, pixelInCircle); +} + +// Draw a rectangle at aRect fill it with aColor. Only works on non-rotated +// rects. +vec4 drawRect(vec2 aPixel, vec4 aRect, vec3 aColor) { + // GLSL origin is bottom left, positive Y is up + bool inRect = (aRect.x <= aPixel.x) && (aPixel.x <= aRect.x + aRect.z) && + (aPixel.y >= aRect.y) && (aPixel.y <= aRect.y + aRect.w); + return vec4(aColor, float(inRect)); +} + +vec4 draw_dotted_edge() { + // Everything here should be in device pixels. + // We want the dot to be roughly the size of the whole border spacing + float border_spacing = min(vBorders.w, vBorders.z); + float radius = floor(border_spacing / 2.0); + float diameter = radius * 2.0; + // The amount of space between dots. 2.2 was chosen because it looks kind of + // like firefox. + float circleSpacing = diameter * 2.2; + + vec2 size = vBorders.zw; + // Get our position within this specific segment + vec2 position = vDevicePos - vBorders.xy; + + // Break our position into square tiles with circles in them. + vec2 circleCount = floor(size / circleSpacing); + circleCount = max(circleCount, 1.0); + + vec2 distBetweenCircles = size / circleCount; + vec2 circleCenter = distBetweenCircles / 2.0; + + // Find out which tile this pixel belongs to. + vec2 destTile = floor(position / distBetweenCircles); + destTile = destTile * distBetweenCircles; + + // Where we want to draw the actual circle. + vec2 tileCenter = destTile + circleCenter; + + // Find the position within the tile + vec2 positionInTile = mod(position, distBetweenCircles); + vec2 finalPosition = positionInTile + destTile; + + vec4 white = vec4(1.0, 1.0, 1.0, 1.0); + // See if we should draw a circle or not + vec4 circleColor = drawCircle(finalPosition, tileCenter, radius, vVerticalColor.xyz); + return mix(white, circleColor, circleColor.a); +} + +// Our current edge calculation is based only on +// the size of the border-size, but we need to draw +// the dashes in the center of the segment we're drawing. +// This calculates how much to nudge and which axis to nudge on. +vec2 get_dashed_nudge_factor(vec2 dash_size, bool is_corner) { + if (is_corner) { + return vec2(0.0, 0.0); + } + + bool xAxisFudge = vBorders.z > vBorders.w; + if (xAxisFudge) { + return vec2(dash_size.x / 2.0, 0); + } + + return vec2(0.0, dash_size.y / 2.0); +} + +vec4 draw_dashed_edge(bool is_corner) { + // Everything here should be in device pixels. + // We want the dot to be roughly the size of the whole border spacing + // 5.5 here isn't a magic number, it's just what mostly looks like FF/Chrome + // TODO: Investigate exactly what FF does. + float dash_interval = min(vBorders.w, vBorders.z) * 5.5; + vec2 edge_size = vec2(vBorders.z, vBorders.w); + vec2 dash_size = vec2(dash_interval / 2.0, dash_interval / 2.0); + vec2 position = vDevicePos - vBorders.xy; + + vec2 dash_count = floor(edge_size/ dash_interval); + vec2 dist_between_dashes = edge_size / dash_count; + + vec2 target_rect_index = floor(position / dist_between_dashes); + vec2 target_rect_loc = target_rect_index * dist_between_dashes; + target_rect_loc += get_dashed_nudge_factor(dash_size, is_corner); + vec4 target_rect = vec4(target_rect_loc, dash_size); + + vec4 white = vec4(1.0, 1.0, 1.0, 1.0); + vec4 target_colored_rect = drawRect(position, target_rect, vVerticalColor.xyz); + return mix(white, target_colored_rect, target_colored_rect.a); +} + +void draw_dotted_border(void) { + switch (vBorderPart) { + // These are the layer tile part PrimitivePart as uploaded by the tiling.rs + case PST_TOP_LEFT: + case PST_TOP_RIGHT: + case PST_BOTTOM_LEFT: + case PST_BOTTOM_RIGHT: + { + // TODO: Fix for corners with a border-radius + oFragColor = draw_dotted_edge(); + break; + } + case PST_BOTTOM: + case PST_TOP: + case PST_LEFT: + case PST_RIGHT: + { + oFragColor = draw_dotted_edge(); + break; + } + } +} + +void draw_dashed_border(void) { + switch (vBorderPart) { + // These are the layer tile part PrimitivePart as uploaded by the tiling.rs + case PST_TOP_LEFT: + case PST_TOP_RIGHT: + case PST_BOTTOM_LEFT: + case PST_BOTTOM_RIGHT: + { + // TODO: Fix for corners with a border-radius + bool is_corner = true; + oFragColor = draw_dashed_edge(is_corner); + break; + } + case PST_BOTTOM: + case PST_TOP: + case PST_LEFT: + case PST_RIGHT: + { + bool is_corner = false; + oFragColor = draw_dashed_edge(is_corner); + break; + } + } +} + +// TODO: Investigate performance of this shader and see +// if it's worthwhile splitting it / removing branches etc. +void main(void) { + if (vRadii.x > 0.0 && + (distance(vRefPoint, vLocalPos) > vRadii.x || + distance(vRefPoint, vLocalPos) < vRadii.z)) { + discard; + } + + switch (vBorderStyle) { + case BORDER_STYLE_DASHED: + { + draw_dashed_border(); + break; + } + case BORDER_STYLE_DOTTED: + { + draw_dotted_border(); + break; + } + case BORDER_STYLE_OUTSET: + case BORDER_STYLE_INSET: + { + float color = step(0.0, vF); + oFragColor = mix(vVerticalColor, vHorizontalColor, color); + break; + } + case BORDER_STYLE_NONE: + case BORDER_STYLE_SOLID: + { + float color = step(0.0, vF); + oFragColor = mix(vHorizontalColor, vVerticalColor, color); + break; + } + default: + { + discard; + break; + } + } +} diff --git a/res/ps_border.glsl b/res/ps_border.glsl new file mode 100644 index 0000000000..26a1a6601e --- /dev/null +++ b/res/ps_border.glsl @@ -0,0 +1,24 @@ +#line 1 + +/* 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/. */ + +// These two are interpolated +varying float vF; // This is a weighting as we get closer to the bottom right corner? + +// These are not changing. +flat varying vec4 vVerticalColor; // The vertical color, e.g. top/bottom +flat varying vec4 vHorizontalColor; // The horizontal color e.g. left/right +flat varying vec4 vRadii; // The border radius from CSS border-radius + +// These are in device space +varying vec2 vLocalPos; // The clamped position in local space. +varying vec2 vDevicePos; // The clamped position in device space. +flat varying vec4 vBorders; // the rect of the border in (x, y, width, height) form + +// for corners, this is the beginning of the corner. +// For the lines, this is the top left of the line. +flat varying vec2 vRefPoint; +flat varying uint vBorderStyle; +flat varying uint vBorderPart; // Which part of the border we're drawing. diff --git a/res/ps_border.vs.glsl b/res/ps_border.vs.glsl new file mode 100644 index 0000000000..ad34c02737 --- /dev/null +++ b/res/ps_border.vs.glsl @@ -0,0 +1,111 @@ +#line 1 +/* 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/. */ + +struct Border { + PrimitiveInfo info; + vec4 verticalColor; + vec4 horizontalColor; + vec4 radii; + uvec4 border_style_trbl; +}; + +layout(std140) uniform Items { + Border borders[WR_MAX_PRIM_ITEMS]; +}; + +uint get_border_style(Border a_border, uint a_edge) { + switch (a_edge) { + case PST_TOP: + case PST_TOP_LEFT: + return a_border.border_style_trbl.x; + case PST_BOTTOM_LEFT: + case PST_LEFT: + return a_border.border_style_trbl.z; + case PST_BOTTOM_RIGHT: + case PST_BOTTOM: + return a_border.border_style_trbl.w; + case PST_TOP_RIGHT: + case PST_RIGHT: + return a_border.border_style_trbl.y; + } +} + +void main(void) { + Border border = borders[gl_InstanceID]; + VertexInfo vi = write_vertex(border.info); + + // Just our boring radius position. + vRadii = border.radii; + + float x0, y0, x1, y1; + vBorderPart = border.info.layer_tile_part.z; + switch (vBorderPart) { + // These are the layer tile part PrimitivePart as uploaded by the tiling.rs + case PST_TOP_LEFT: + x0 = border.info.local_rect.x; + y0 = border.info.local_rect.y; + // These are width / heights + x1 = border.info.local_rect.x + border.info.local_rect.z; + y1 = border.info.local_rect.y + border.info.local_rect.w; + + // The radius here is the border-radius. This is 0, so vRefPoint will + // just be the top left (x,y) corner. + vRefPoint = vec2(x0, y0) + vRadii.xy; + break; + case PST_TOP_RIGHT: + x0 = border.info.local_rect.x + border.info.local_rect.z; + y0 = border.info.local_rect.y; + x1 = border.info.local_rect.x; + y1 = border.info.local_rect.y + border.info.local_rect.w; + vRefPoint = vec2(x0, y0) + vec2(-vRadii.x, vRadii.y); + break; + case PST_BOTTOM_LEFT: + x0 = border.info.local_rect.x; + y0 = border.info.local_rect.y + border.info.local_rect.w; + x1 = border.info.local_rect.x + border.info.local_rect.z; + y1 = border.info.local_rect.y; + vRefPoint = vec2(x0, y0) + vec2(vRadii.x, -vRadii.y); + break; + case PST_BOTTOM_RIGHT: + x0 = border.info.local_rect.x; + y0 = border.info.local_rect.y; + x1 = border.info.local_rect.x + border.info.local_rect.z; + y1 = border.info.local_rect.y + border.info.local_rect.w; + vRefPoint = vec2(x1, y1) + vec2(-vRadii.x, -vRadii.y); + break; + case PST_TOP: + case PST_LEFT: + case PST_BOTTOM: + case PST_RIGHT: + vRefPoint = border.info.local_rect.xy; + x0 = border.info.local_rect.x; + y0 = border.info.local_rect.y; + x1 = border.info.local_rect.x + border.info.local_rect.z; + y1 = border.info.local_rect.y + border.info.local_rect.w; + break; + } + + vBorderStyle = get_border_style(border, vBorderPart); + + // y1 - y0 is the height of the corner / line + // x1 - x0 is the width of the corner / line. + float width = x1 - x0; + float height = y1 - y0; + // This is just a weighting of the pixel colors it seems? + vF = (vi.local_clamped_pos.x - x0) * height - (vi.local_clamped_pos.y - y0) * width; + + // This is what was currently sent. + vVerticalColor = border.verticalColor; + vHorizontalColor = border.horizontalColor; + + // Local space + vLocalPos = vi.local_clamped_pos.xy; + + // These are in device space + vDevicePos = vi.global_clamped_pos; + + // These are in device space + vBorders = border.info.local_rect * uDevicePixelRatio; +} diff --git a/res/ps_box_shadow.fs.glsl b/res/ps_box_shadow.fs.glsl new file mode 100644 index 0000000000..9c485cba9f --- /dev/null +++ b/res/ps_box_shadow.fs.glsl @@ -0,0 +1,151 @@ +/* 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/. */ + +/* 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/. */ + +// See http://asciimath.org to render the equations here. + +// The Gaussian function used for blurring: +// +// G_sigma(x) = 1/sqrt(2 pi sigma^2) e^(-x^2/(2 sigma^2)) +float gauss(float x, float sigma) { + float sigmaPow2 = sigma * sigma; + return 1.0 / sqrt(6.283185307179586 * sigmaPow2) * exp(-(x * x) / (2.0 * sigmaPow2)); +} + +// An approximation of the error function, which is related to the integral of the Gaussian +// function: +// +// "erf"(x) = 2/sqrt(pi) int_0^x e^(-t^2) dt +// ~~ 1 - 1 / (1 + a_1 x + a_2 x^2 + a_3 x^3 + a_4 x^4)^4 +// +// where: +// +// a_1 = 0.278393, a_2 = 0.230389, a_3 = 0.000972, a_4 = 0.078108 +// +// This approximation is accurate to `5 xx 10^-4`, more than accurate enough for our purposes. +// +// See: https://en.wikipedia.org/wiki/Error_function#Approximation_with_elementary_functions +float erf(float x) { + bool negative = x < 0.0; + if (negative) + x = -x; + float x2 = x * x; + float x3 = x2 * x; + float x4 = x2 * x2; + float denom = 1.0 + 0.278393 * x + 0.230389 * x2 + 0.000972 * x3 + 0.078108 * x4; + float result = 1.0 - 1.0 / (denom * denom * denom * denom); + return negative ? -result : result; +} + +// A useful helper for calculating integrals of the Gaussian function via the error function: +// +// "erf"_sigma(x) = 2 int 1/sqrt(2 pi sigma^2) e^(-x^2/(2 sigma^2)) dx +// = "erf"(x/(sigma sqrt(2))) +float erfSigma(float x, float sigma) { + return erf(x / (sigma * 1.4142135623730951)); +} + +// Returns the blurred color value from the box itself (not counting any rounded corners). `p_0` is +// the vector distance to the top left corner of the box; `p_1` is the vector distance to its +// bottom right corner. +// +// "colorFromRect"_sigma(p_0, p_1) +// = int_{p_{0_y}}^{p_{1_y}} int_{p_{1_x}}^{p_{0_x}} G_sigma(y) G_sigma(x) dx dy +// = 1/4 ("erf"_sigma(p_{1_x}) - "erf"_sigma(p_{0_x})) +// ("erf"_sigma(p_{1_y}) - "erf"_sigma(p_{0_y})) +float colorFromRect(vec2 p0, vec2 p1, float sigma) { + return (erfSigma(p1.x, sigma) - erfSigma(p0.x, sigma)) * + (erfSigma(p1.y, sigma) - erfSigma(p0.y, sigma)) / 4.0; +} + +// Returns the `x` coordinate on the ellipse with the given radii for the given `y` coordinate: +// +// "ellipsePoint"(y, y_0, a, b) = a sqrt(1 - ((y - y_0) / b)^2) +float ellipsePoint(float y, float y0, vec2 radii) { + float bStep = (y - y0) / radii.y; + return radii.x * sqrt(1.0 - bStep * bStep); +} + +// A helper function to compute the value that needs to be subtracted to accommodate the border +// corners. +// +// "colorCutout"_sigma(x_{0_l}, x_{0_r}, y_0, y_{min}, y_{max}, a, b) +// = int_{y_{min}}^{y_{max}} +// int_{x_{0_r} + "ellipsePoint"(y, y_0, a, b)}^{x_{0_r} + a} G_sigma(y) G_sigma(x) dx +// + int_{x_{0_l} - a}^{x_{0_l} - "ellipsePoint"(y, y_0, a, b)} G_sigma(y) G_sigma(x) +// dx dy +// = int_{y_{min}}^{y_{max}} 1/2 G_sigma(y) +// ("erf"_sigma(x_{0_r} + a) - "erf"_sigma(x_{0_r} + "ellipsePoint"(y, y_0, a, b)) + +// "erf"_sigma(x_{0_l} - "ellipsePoint"(y, y_0, a, b)) - "erf"_sigma(x_{0_l} - a)) +// +// with the outer integral evaluated numerically. +float colorCutoutGeneral(float x0l, + float x0r, + float y0, + float yMin, + float yMax, + vec2 radii, + float sigma) { + float sum = 0.0; + for (float y = yMin; y <= yMax; y += 1.0) { + float xEllipsePoint = ellipsePoint(y, y0, radii); + sum += gauss(y, sigma) * + (erfSigma(x0r + radii.x, sigma) - erfSigma(x0r + xEllipsePoint, sigma) + + erfSigma(x0l - xEllipsePoint, sigma) - erfSigma(x0l - radii.x, sigma)); + } + return sum / 2.0; +} + +// The value that needs to be subtracted to accommodate the top border corners. +float colorCutoutTop(float x0l, float x0r, float y0, vec2 radii, float sigma) { + return colorCutoutGeneral(x0l, x0r, y0, y0, y0 + radii.y, radii, sigma); +} + +// The value that needs to be subtracted to accommodate the bottom border corners. +float colorCutoutBottom(float x0l, float x0r, float y0, vec2 radii, float sigma) { + return colorCutoutGeneral(x0l, x0r, y0, y0 - radii.y, y0, radii, sigma); +} + +// The blurred color value for the point at `pos` with the top left corner of the box at +// `p_{0_"rect"}` and the bottom right corner of the box at `p_{1_"rect"}`. +float color(vec2 pos, vec2 p0Rect, vec2 p1Rect, vec2 radii, float sigma) { + // Compute the vector distances `p_0` and `p_1`. + vec2 p0 = p0Rect - pos, p1 = p1Rect - pos; + + // Compute the basic color `"colorFromRect"_sigma(p_0, p_1)`. This is all we have to do if + // the box is unrounded. + float cRect = colorFromRect(p0, p1, sigma); + if (radii.x == 0.0 || radii.y == 0.0) + return cRect; + + // Compute the inner corners of the box, taking border radii into account: `x_{0_l}`, + // `y_{0_t}`, `x_{0_r}`, and `y_{0_b}`. + float x0l = p0.x + radii.x; + float y0t = p1.y - radii.y; + float x0r = p1.x - radii.x; + float y0b = p0.y + radii.y; + + // Compute the final color: + // + // "colorFromRect"_sigma(p_0, p_1) - + // ("colorCutoutTop"_sigma(x_{0_l}, x_{0_r}, y_{0_t}, a, b) + + // "colorCutoutBottom"_sigma(x_{0_l}, x_{0_r}, y_{0_b}, a, b)) + float cCutoutTop = colorCutoutTop(x0l, x0r, y0t, radii, sigma); + float cCutoutBottom = colorCutoutBottom(x0l, x0r, y0b, radii, sigma); + return cRect - (cCutoutTop + cCutoutBottom); +} + +void main(void) { + vec2 pos = vPos.xy; + vec2 p0Rect = vBoxShadowRect.xy, p1Rect = vBoxShadowRect.zw; + vec2 radii = vBorderRadii.xy; + float sigma = vBlurRadius / 2.0; + float value = color(pos, p0Rect, p1Rect, radii, sigma); + + value = max(value, 0.0); + oFragColor = vColor * vec4(1.0, 1.0, 1.0, vInverted == 1.0 ? 1.0 - value : value); +} diff --git a/res/ps_box_shadow.glsl b/res/ps_box_shadow.glsl new file mode 100644 index 0000000000..26fdba588f --- /dev/null +++ b/res/ps_box_shadow.glsl @@ -0,0 +1,11 @@ +/* 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/. */ + +varying vec2 vPos; +flat varying vec4 vColor; +flat varying vec2 vBorderRadii; +flat varying float vBlurRadius; +flat varying vec4 vBoxShadowRect; +flat varying vec4 vSrcRect; +flat varying float vInverted; diff --git a/res/ps_box_shadow.vs.glsl b/res/ps_box_shadow.vs.glsl new file mode 100644 index 0000000000..9662502e14 --- /dev/null +++ b/res/ps_box_shadow.vs.glsl @@ -0,0 +1,29 @@ +#line 1 +/* 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/. */ + +struct BoxShadow { + PrimitiveInfo info; + vec4 color; + vec4 border_radii_blur_radius_inverted; + vec4 bs_rect; + vec4 src_rect; +}; + +layout(std140) uniform Items { + BoxShadow boxshadows[WR_MAX_PRIM_ITEMS]; +}; + +void main(void) { + BoxShadow bs = boxshadows[gl_InstanceID]; + VertexInfo vi = write_vertex(bs.info); + + vPos = vi.local_clamped_pos; + vColor = bs.color; + vBorderRadii = bs.border_radii_blur_radius_inverted.xy; + vBlurRadius = bs.border_radii_blur_radius_inverted.z; + vBoxShadowRect = vec4(bs.bs_rect.xy, bs.bs_rect.xy + bs.bs_rect.zw); + vSrcRect = vec4(bs.src_rect.xy, bs.src_rect.xy + bs.src_rect.zw); + vInverted = bs.border_radii_blur_radius_inverted.w; +} diff --git a/res/mask.vs.glsl b/res/ps_clear.fs.glsl similarity index 66% rename from res/mask.vs.glsl rename to res/ps_clear.fs.glsl index 491f3ea386..5ad3065f78 100644 --- a/res/mask.vs.glsl +++ b/res/ps_clear.fs.glsl @@ -2,8 +2,6 @@ * 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/. */ -void main(void) -{ - vColor = aColorRectTL / 255.0; - gl_Position = uTransform * vec4(aPosition, 1.0); +void main(void) { + oFragColor = vec4(1, 1, 1, 1); } diff --git a/res/ps_clear.glsl b/res/ps_clear.glsl new file mode 100644 index 0000000000..e0032240a4 --- /dev/null +++ b/res/ps_clear.glsl @@ -0,0 +1,3 @@ +/* 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/. */ diff --git a/res/ps_clear.vs.glsl b/res/ps_clear.vs.glsl new file mode 100644 index 0000000000..5d3012fe46 --- /dev/null +++ b/res/ps_clear.vs.glsl @@ -0,0 +1,23 @@ +#line 1 + +/* 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/. */ + +struct ClearTile { + uvec4 rect; +}; + +layout(std140) uniform Tiles { + ClearTile tiles[WR_MAX_CLEAR_TILES]; +}; + + +void main() { + ClearTile tile = tiles[gl_InstanceID]; + + vec4 rect = vec4(tile.rect); + + vec4 pos = vec4(mix(rect.xy, rect.xy + rect.zw, aPosition.xy), 0, 1); + gl_Position = uTransform * pos; +} diff --git a/res/ps_composite.fs.glsl b/res/ps_composite.fs.glsl new file mode 100644 index 0000000000..c4e4099655 --- /dev/null +++ b/res/ps_composite.fs.glsl @@ -0,0 +1,320 @@ +#line 1 + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#define COMPOSITE_KIND_MIX_BLEND_MODE 0 +#define COMPOSITE_KIND_FILTER 1 + +uniform sampler2D sCache; + +vec3 rgbToHsv(vec3 c) { + float value = max(max(c.r, c.g), c.b); + + float chroma = value - min(min(c.r, c.g), c.b); + if (chroma == 0.0) { + return vec3(0.0); + } + float saturation = chroma / value; + + float hue; + if (c.r == value) + hue = (c.g - c.b) / chroma; + else if (c.g == value) + hue = 2.0 + (c.b - c.r) / chroma; + else // if (c.b == value) + hue = 4.0 + (c.r - c.g) / chroma; + + hue *= 1.0/6.0; + if (hue < 0.0) + hue += 1.0; + return vec3(hue, saturation, value); +} + +vec3 hsvToRgb(vec3 c) { + if (c.s == 0.0) { + return vec3(c.z); + } + + float hue = c.x * 6.0; + int sector = int(hue); + float residualHue = hue - float(sector); + + vec3 pqt = c.z * vec3(1.0 - c.y, 1.0 - c.y * residualHue, 1.0 - c.y * (1.0 - residualHue)); + if (sector == 0) + return vec3(c.z, pqt.z, pqt.x); + if (sector == 1) + return vec3(pqt.y, c.z, pqt.x); + if (sector == 2) + return vec3(pqt.x, c.z, pqt.z); + if (sector == 3) + return vec3(pqt.x, pqt.y, c.z); + if (sector == 4) + return vec3(pqt.z, pqt.x, c.z); + return vec3(c.z, pqt.x, pqt.y); +} + +float gauss(float x, float sigma) { + if (sigma == 0.0) + return 1.0; + return (1.0 / sqrt(6.283185307179586 * sigma * sigma)) * exp(-(x * x) / (2.0 * sigma * sigma)); +} + +vec4 Blur(float radius, vec2 direction) { + // TODO(gw): Support blur in WR2! + return vec4(1, 1, 1, 1); +} + +vec4 Contrast(vec4 Cs, float amount) { + return vec4(Cs.rgb * amount - 0.5 * amount + 0.5, 1.0); +} + +vec4 Grayscale(vec4 Cs, float amount) { + float ia = 1.0 - amount; + return mat4(vec4(0.2126 + 0.7874 * ia, 0.2126 - 0.2126 * ia, 0.2126 - 0.2126 * ia, 0.0), + vec4(0.7152 - 0.7152 * ia, 0.7152 + 0.2848 * ia, 0.7152 - 0.7152 * ia, 0.0), + vec4(0.0722 - 0.0722 * ia, 0.0722 - 0.0722 * ia, 0.0722 + 0.9278 * ia, 0.0), + vec4(0.0, 0.0, 0.0, 1.0)) * Cs; +} + +vec4 HueRotate(vec4 Cs, float amount) { + vec3 CsHsv = rgbToHsv(Cs.rgb); + CsHsv.x = mod(CsHsv.x + amount / 6.283185307179586, 1.0); + return vec4(hsvToRgb(CsHsv), Cs.a); +} + +vec4 Invert(vec4 Cs, float amount) { + return mix(Cs, vec4(1.0, 1.0, 1.0, Cs.a) - vec4(Cs.rgb, 0.0), amount); +} + +vec4 Saturate(vec4 Cs, float amount) { + return vec4(hsvToRgb(min(vec3(1.0, amount, 1.0) * rgbToHsv(Cs.rgb), vec3(1.0))), Cs.a); +} + +vec4 Sepia(vec4 Cs, float amount) { + float ia = 1.0 - amount; + return mat4(vec4(0.393 + 0.607 * ia, 0.349 - 0.349 * ia, 0.272 - 0.272 * ia, 0.0), + vec4(0.769 - 0.769 * ia, 0.686 + 0.314 * ia, 0.534 - 0.534 * ia, 0.0), + vec4(0.189 - 0.189 * ia, 0.168 - 0.168 * ia, 0.131 + 0.869 * ia, 0.0), + vec4(0.0, 0.0, 0.0, 1.0)) * Cs; +} + +vec3 Multiply(vec3 Cb, vec3 Cs) { + return Cb * Cs; +} + +vec3 Screen(vec3 Cb, vec3 Cs) { + return Cb + Cs - (Cb * Cs); +} + +vec3 HardLight(vec3 Cb, vec3 Cs) { + vec3 m = Multiply(Cb, 2.0 * Cs); + vec3 s = Screen(Cb, 2.0 * Cs - 1.0); + vec3 edge = vec3(0.5, 0.5, 0.5); + return mix(m, s, step(edge, Cs)); +} + +// TODO: Worth doing with mix/step? Check GLSL output. +float ColorDodge(float Cb, float Cs) { + if (Cb == 0.0) + return 0.0; + else if (Cs == 1.0) + return 1.0; + else + return min(1.0, Cb / (1.0 - Cs)); +} + +// TODO: Worth doing with mix/step? Check GLSL output. +float ColorBurn(float Cb, float Cs) { + if (Cb == 1.0) + return 1.0; + else if (Cs == 0.0) + return 0.0; + else + return 1.0 - min(1.0, (1.0 - Cb) / Cs); +} + +float SoftLight(float Cb, float Cs) { + if (Cs <= 0.5) { + return Cb - (1.0 - 2.0 * Cs) * Cb * (1.0 - Cb); + } else { + float D; + + if (Cb <= 0.25) + D = ((16.0 * Cb - 12.0) * Cb + 4.0) * Cb; + else + D = sqrt(Cb); + + return Cb + (2.0 * Cs - 1.0) * (D - Cb); + } +} + +vec3 Difference(vec3 Cb, vec3 Cs) { + return abs(Cb - Cs); +} + +vec3 Exclusion(vec3 Cb, vec3 Cs) { + return Cb + Cs - 2.0 * Cb * Cs; +} + +// These functions below are taken from the spec. +// There's probably a much quicker way to implement +// them in GLSL... +float Sat(vec3 c) { + return max(c.r, max(c.g, c.b)) - min(c.r, min(c.g, c.b)); +} + +float Lum(vec3 c) { + vec3 f = vec3(0.3, 0.59, 0.11); + return dot(c, f); +} + +vec3 ClipColor(vec3 C) { + float L = Lum(C); + float n = min(C.r, min(C.g, C.b)); + float x = max(C.r, max(C.g, C.b)); + + if (n < 0.0) + C = L + (((C - L) * L) / (L - n)); + + if (x > 1.0) + C = L + (((C - L) * (1.0 - L)) / (x - L)); + + return C; +} + +vec3 SetLum(vec3 C, float l) { + float d = l - Lum(C); + return ClipColor(C + d); +} + +void SetSatInner(inout float Cmin, inout float Cmid, inout float Cmax, float s) { + if (Cmax > Cmin) { + Cmid = (((Cmid - Cmin) * s) / (Cmax - Cmin)); + Cmax = s; + } else { + Cmid = 0.0; + Cmax = 0.0; + } + Cmin = 0.0; +} + +vec3 SetSat(vec3 C, float s) { + if (C.r <= C.g) { + if (C.g <= C.b) { + SetSatInner(C.r, C.g, C.b, s); + } else { + if (C.r <= C.b) { + SetSatInner(C.r, C.b, C.g, s); + } else { + SetSatInner(C.b, C.r, C.g, s); + } + } + } else { + if (C.r <= C.b) { + SetSatInner(C.g, C.r, C.b, s); + } else { + if (C.g <= C.b) { + SetSatInner(C.g, C.b, C.r, s); + } else { + SetSatInner(C.b, C.g, C.r, s); + } + } + } + return C; +} + +vec3 Hue(vec3 Cb, vec3 Cs) { + return SetLum(SetSat(Cs, Sat(Cb)), Lum(Cb)); +} + +vec3 Saturation(vec3 Cb, vec3 Cs) { + return SetLum(SetSat(Cb, Sat(Cs)), Lum(Cb)); +} + +vec3 Color(vec3 Cb, vec3 Cs) { + return SetLum(Cs, Lum(Cb)); +} + +vec3 Luminosity(vec3 Cb, vec3 Cs) { + return SetLum(Cb, Lum(Cs)); +} + +void main(void) { + vec4 Cs = texture(sCache, vUv1); + vec4 Cb = texture(sCache, vUv0); + + // TODO(gw): This is a hack that's (probably) wrong. + // Instead of drawing the tile rect, draw the + // stacking context bounds instead? + if (Cs.a == 0.0) { + oFragColor = Cb; + return; + } + + int kind = vInfo.x; + int op = vInfo.y; + float amount = vAmount; + + // Return yellow if none of the branches match (shouldn't happen). + vec4 result = vec4(1.0, 1.0, 0.0, 1.0); + + switch (kind) { + case COMPOSITE_KIND_MIX_BLEND_MODE: + if (op == 2) { + result.rgb = Screen(Cb.rgb, Cs.rgb); + } else if (op == 3) { + result.rgb = HardLight(Cs.rgb, Cb.rgb); // Overlay is inverse of Hardlight + } else if (op == 6) { + result.r = ColorDodge(Cb.r, Cs.r); + result.g = ColorDodge(Cb.g, Cs.g); + result.b = ColorDodge(Cb.b, Cs.b); + } else if (op == 7) { + result.r = ColorBurn(Cb.r, Cs.r); + result.g = ColorBurn(Cb.g, Cs.g); + result.b = ColorBurn(Cb.b, Cs.b); + } else if (op == 8) { + result.rgb = HardLight(Cb.rgb, Cs.rgb); + } else if (op == 9) { + result.r = SoftLight(Cb.r, Cs.r); + result.g = SoftLight(Cb.g, Cs.g); + result.b = SoftLight(Cb.b, Cs.b); + } else if (op == 10) { + result.rgb = Difference(Cb.rgb, Cs.rgb); + } else if (op == 11) { + result.rgb = Exclusion(Cb.rgb, Cs.rgb); + } else if (op == 12) { + result.rgb = Hue(Cb.rgb, Cs.rgb); + } else if (op == 13) { + result.rgb = Saturation(Cb.rgb, Cs.rgb); + } else if (op == 14) { + result.rgb = Color(Cb.rgb, Cs.rgb); + } else if (op == 15) { + result.rgb = Luminosity(Cb.rgb, Cs.rgb); + } + break; + case COMPOSITE_KIND_FILTER: + if (op == 0) { + // Gaussian blur is specially handled: + result = Cs;// Blur(amount, vec2(0,0)); + } else { + if (op == 1) { + result = Contrast(Cs, amount); + } else if (op == 2) { + result = Grayscale(Cs, amount); + } else if (op == 3) { + result = HueRotate(Cs, amount); + } else if (op == 4) { + result = Invert(Cs, amount); + } else if (op == 5) { + result = Saturate(Cs, amount); + } else if (op == 6) { + result = Sepia(Cs, amount); + } + } + break; + } + + oFragColor = result; +} diff --git a/res/ps_composite.glsl b/res/ps_composite.glsl new file mode 100644 index 0000000000..68a8f97810 --- /dev/null +++ b/res/ps_composite.glsl @@ -0,0 +1,8 @@ +/* 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/. */ + +varying vec2 vUv0; +varying vec2 vUv1; +flat varying ivec2 vInfo; +flat varying float vAmount; diff --git a/res/ps_composite.vs.glsl b/res/ps_composite.vs.glsl new file mode 100644 index 0000000000..c7f9cd0553 --- /dev/null +++ b/res/ps_composite.vs.glsl @@ -0,0 +1,37 @@ +#line 1 +/* 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/. */ + +struct Composite { + uvec4 src0; + uvec4 src1; + uvec4 target_rect; + ivec4 info; + vec4 amount; +}; + +layout(std140) uniform Items { + Composite composites[WR_MAX_PRIM_ITEMS]; +}; + +void main(void) { + Composite composite = composites[gl_InstanceID]; + + vec2 local_pos = mix(vec2(composite.target_rect.xy), + vec2(composite.target_rect.xy + composite.target_rect.zw), + aPosition.xy); + + vec2 st0 = vec2(composite.src0.xy) / 2048.0; + vec2 st1 = vec2(composite.src0.xy + composite.src0.zw) / 2048.0; + vUv0 = mix(st0, st1, aPosition.xy); + + st0 = vec2(composite.src1.xy) / 2048.0; + st1 = vec2(composite.src1.xy + composite.src1.zw) / 2048.0; + vUv1 = mix(st0, st1, aPosition.xy); + + vInfo = composite.info.xy; + vAmount = composite.amount.x; + + gl_Position = uTransform * vec4(local_pos, 0, 1); +} diff --git a/res/clear.vs.glsl b/res/ps_gradient.fs.glsl similarity index 66% rename from res/clear.vs.glsl rename to res/ps_gradient.fs.glsl index 491f3ea386..b25faec6f5 100644 --- a/res/clear.vs.glsl +++ b/res/ps_gradient.fs.glsl @@ -2,8 +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/. */ -void main(void) -{ - vColor = aColorRectTL / 255.0; - gl_Position = uTransform * vec4(aPosition, 1.0); +void main(void) { + do_clip(vPos, vClipRect, vClipRadius); + oFragColor = mix(vColor0, vColor1, vF); } diff --git a/res/ps_gradient.glsl b/res/ps_gradient.glsl new file mode 100644 index 0000000000..4b1efa7644 --- /dev/null +++ b/res/ps_gradient.glsl @@ -0,0 +1,10 @@ +/* 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/. */ + +varying float vF; +varying vec2 vPos; +flat varying vec4 vColor0; +flat varying vec4 vColor1; +flat varying vec4 vClipRect; +flat varying vec4 vClipRadius; diff --git a/res/ps_gradient.vs.glsl b/res/ps_gradient.vs.glsl new file mode 100644 index 0000000000..cee41fdf5d --- /dev/null +++ b/res/ps_gradient.vs.glsl @@ -0,0 +1,45 @@ +#line 1 +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#define DIR_HORIZONTAL uint(0) +#define DIR_VERTICAL uint(1) + +struct Gradient { + PrimitiveInfo info; + vec4 color0; + vec4 color1; + uvec4 dir; + Clip clip; +}; + +layout(std140) uniform Items { + Gradient gradients[WR_MAX_PRIM_ITEMS]; +}; + +void main(void) { + Gradient gradient = gradients[gl_InstanceID]; + VertexInfo vi = write_vertex(gradient.info); + + vec2 f = (vi.local_clamped_pos - gradient.info.local_rect.xy) / gradient.info.local_rect.zw; + + switch (gradient.dir.x) { + case DIR_HORIZONTAL: + vF = f.x; + break; + case DIR_VERTICAL: + vF = f.y; + break; + } + + vClipRect = vec4(gradient.clip.rect.xy, gradient.clip.rect.xy + gradient.clip.rect.zw); + vClipRadius = vec4(gradient.clip.top_left.outer_inner_radius.x, + gradient.clip.top_right.outer_inner_radius.x, + gradient.clip.bottom_right.outer_inner_radius.x, + gradient.clip.bottom_left.outer_inner_radius.x); + vPos = vi.local_clamped_pos; + + vColor0 = gradient.color0; + vColor1 = gradient.color1; +} diff --git a/res/ps_image.fs.glsl b/res/ps_image.fs.glsl new file mode 100644 index 0000000000..a7d9685005 --- /dev/null +++ b/res/ps_image.fs.glsl @@ -0,0 +1,8 @@ +/* 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/. */ + +void main(void) { + vec2 st = vTextureOffset + vTextureSize * fract(vUv); + oFragColor = texture(sDiffuse, st); +} diff --git a/res/ps_image.glsl b/res/ps_image.glsl new file mode 100644 index 0000000000..e2c59d9ff6 --- /dev/null +++ b/res/ps_image.glsl @@ -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/. */ + +varying vec2 vUv; // Location within the CSS box to draw. +flat varying vec2 vTextureOffset; // Offset of this image into the texture atlas. +flat varying vec2 vTextureSize; // Size of the image in the texture atlas. diff --git a/res/ps_image.vs.glsl b/res/ps_image.vs.glsl new file mode 100644 index 0000000000..0f6bcba6eb --- /dev/null +++ b/res/ps_image.vs.glsl @@ -0,0 +1,24 @@ +#line 1 +/* 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/. */ + +struct Image { + PrimitiveInfo info; + vec4 st_rect; // Location of the image texture in the texture atlas. + vec4 stretch_size; // Size of the actual image. +}; + +layout(std140) uniform Items { + Image images[WR_MAX_PRIM_ITEMS]; +}; + +void main(void) { + Image image = images[gl_InstanceID]; + VertexInfo vi = write_vertex(image.info); + + // vUv will contain how many times this image has wrapped around the image size. + vUv = (vi.local_clamped_pos - vi.local_rect.p0) / image.stretch_size.xy; + vTextureSize = image.st_rect.zw - image.st_rect.xy; + vTextureOffset = image.st_rect.xy; +} diff --git a/res/ps_image_clip.fs.glsl b/res/ps_image_clip.fs.glsl new file mode 100644 index 0000000000..2ec824e441 --- /dev/null +++ b/res/ps_image_clip.fs.glsl @@ -0,0 +1,9 @@ +/* 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/. */ + +void main(void) { + do_clip(vPos, vClipRect, vClipRadius); + vec2 st = vTextureOffset + vTextureSize * fract(vUv); + oFragColor = texture(sDiffuse, st); +} diff --git a/res/ps_image_clip.glsl b/res/ps_image_clip.glsl new file mode 100644 index 0000000000..4ddd1a5329 --- /dev/null +++ b/res/ps_image_clip.glsl @@ -0,0 +1,10 @@ +/* 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/. */ + +varying vec2 vUv; // Location within the CSS box to draw. +varying vec2 vPos; +flat varying vec2 vTextureOffset; // Offset of this image into the texture atlas. +flat varying vec2 vTextureSize; // Size of the image in the texture atlas. +flat varying vec4 vClipRect; +flat varying vec4 vClipRadius; diff --git a/res/ps_image_clip.vs.glsl b/res/ps_image_clip.vs.glsl new file mode 100644 index 0000000000..89c6198f99 --- /dev/null +++ b/res/ps_image_clip.vs.glsl @@ -0,0 +1,32 @@ +#line 1 +/* 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/. */ + +struct Image { + PrimitiveInfo info; + vec4 st_rect; // Location of the image texture in the texture atlas. + vec4 stretch_size; // Size of the actual image. + Clip clip; +}; + +layout(std140) uniform Items { + Image images[WR_MAX_PRIM_ITEMS]; +}; + +void main(void) { + Image image = images[gl_InstanceID]; + VertexInfo vi = write_vertex(image.info); + + vClipRect = vec4(image.clip.rect.xy, image.clip.rect.xy + image.clip.rect.zw); + vClipRadius = vec4(image.clip.top_left.outer_inner_radius.x, + image.clip.top_right.outer_inner_radius.x, + image.clip.bottom_right.outer_inner_radius.x, + image.clip.bottom_left.outer_inner_radius.x); + vPos = vi.local_clamped_pos; + + // vUv will contain how many times this image has wrapped around the image size. + vUv = (vi.local_clamped_pos - image.info.local_rect.xy) / image.stretch_size.xy; + vTextureSize = image.st_rect.zw - image.st_rect.xy; + vTextureOffset = image.st_rect.xy; +} diff --git a/res/ps_image_transform.fs.glsl b/res/ps_image_transform.fs.glsl new file mode 100644 index 0000000000..57fdc563ad --- /dev/null +++ b/res/ps_image_transform.fs.glsl @@ -0,0 +1,13 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +void main(void) { + vec2 pos = vPos.xy / vPos.z; + + if (!point_in_rect(pos, vRect.xy, vRect.xy + vRect.zw)) { + discard; + } + + oFragColor = texture(sDiffuse, vUv / vPos.z); +} diff --git a/res/ps_image_transform.glsl b/res/ps_image_transform.glsl new file mode 100644 index 0000000000..77c0eb2071 --- /dev/null +++ b/res/ps_image_transform.glsl @@ -0,0 +1,8 @@ +/* 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/. */ + +varying vec2 vUv; + +varying vec3 vPos; +flat varying vec4 vRect; diff --git a/res/ps_image_transform.vs.glsl b/res/ps_image_transform.vs.glsl new file mode 100644 index 0000000000..b9fcf7c24c --- /dev/null +++ b/res/ps_image_transform.vs.glsl @@ -0,0 +1,65 @@ +#line 1 +/* 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/. */ + +struct Image { + PrimitiveInfo info; + vec4 st_rect; + vec4 stretch_size; // Size of the actual image. +}; + +layout(std140) uniform Items { + Image images[WR_MAX_PRIM_ITEMS]; +}; + +void main(void) { + Image image = images[gl_InstanceID]; + Layer layer = layers[image.info.layer_tile_part.x]; + Tile tile = tiles[image.info.layer_tile_part.y]; + + vec2 p0 = image.info.local_rect.xy; + vec2 p1 = image.info.local_rect.xy + vec2(image.info.local_rect.z, 0.0); + vec2 p2 = image.info.local_rect.xy + vec2(0.0, image.info.local_rect.w); + vec2 p3 = image.info.local_rect.xy + image.info.local_rect.zw; + + vec4 t0 = layer.transform * vec4(p0, 0, 1); + vec4 t1 = layer.transform * vec4(p1, 0, 1); + vec4 t2 = layer.transform * vec4(p2, 0, 1); + vec4 t3 = layer.transform * vec4(p3, 0, 1); + + vec2 tp0 = t0.xy / t0.w; + vec2 tp1 = t1.xy / t1.w; + vec2 tp2 = t2.xy / t2.w; + vec2 tp3 = t3.xy / t3.w; + + vec2 min_pos = min(tp0.xy, min(tp1.xy, min(tp2.xy, tp3.xy))); + vec2 max_pos = max(tp0.xy, max(tp1.xy, max(tp2.xy, tp3.xy))); + + vec2 min_pos_clamped = clamp(min_pos * uDevicePixelRatio, + vec2(tile.actual_rect.xy), + vec2(tile.actual_rect.xy + tile.actual_rect.zw)); + + vec2 max_pos_clamped = clamp(max_pos * uDevicePixelRatio, + vec2(tile.actual_rect.xy), + vec2(tile.actual_rect.xy + tile.actual_rect.zw)); + + vec2 clamped_pos = mix(min_pos_clamped, + max_pos_clamped, + aPosition.xy); + + vec3 layer_pos = get_layer_pos(clamped_pos / uDevicePixelRatio, image.info.layer_tile_part.x); + + vRect = image.info.local_rect; + vPos = layer_pos; + + vec2 f = (layer_pos.xy - image.info.local_rect.xy) / image.info.local_rect.zw; + + vUv = mix(image.st_rect.xy, + image.st_rect.zw, + f); + + vec2 final_pos = clamped_pos + vec2(tile.target_rect.xy) - vec2(tile.actual_rect.xy); + + gl_Position = uTransform * vec4(final_pos, 0, 1); +} diff --git a/res/mask.fs.glsl b/res/ps_rectangle.fs.glsl similarity index 82% rename from res/mask.fs.glsl rename to res/ps_rectangle.fs.glsl index 658ac27488..61837732d3 100644 --- a/res/mask.fs.glsl +++ b/res/ps_rectangle.fs.glsl @@ -2,7 +2,6 @@ * 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/. */ -void main(void) -{ - SetFragColor(vColor); +void main(void) { + oFragColor = vColor; } diff --git a/res/ps_rectangle.glsl b/res/ps_rectangle.glsl new file mode 100644 index 0000000000..6fcfc4255b --- /dev/null +++ b/res/ps_rectangle.glsl @@ -0,0 +1,5 @@ +/* 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/. */ + +varying vec4 vColor; diff --git a/res/ps_rectangle.vs.glsl b/res/ps_rectangle.vs.glsl new file mode 100644 index 0000000000..01a4e5dc18 --- /dev/null +++ b/res/ps_rectangle.vs.glsl @@ -0,0 +1,19 @@ +#line 1 +/* 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/. */ + +struct Rectangle { + PrimitiveInfo info; + vec4 color; +}; + +layout(std140) uniform Items { + Rectangle rects[WR_MAX_PRIM_ITEMS]; +}; + +void main(void) { + Rectangle rect = rects[gl_InstanceID]; + write_vertex(rect.info); + vColor = rect.color; +} diff --git a/res/ps_rectangle_clip.fs.glsl b/res/ps_rectangle_clip.fs.glsl new file mode 100644 index 0000000000..fe3af9162a --- /dev/null +++ b/res/ps_rectangle_clip.fs.glsl @@ -0,0 +1,9 @@ +/* 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/. */ + +void main(void) { + do_clip(vPos, vClipRect, vClipRadius); + + oFragColor = vColor; +} diff --git a/res/ps_rectangle_clip.glsl b/res/ps_rectangle_clip.glsl new file mode 100644 index 0000000000..3d50dd162a --- /dev/null +++ b/res/ps_rectangle_clip.glsl @@ -0,0 +1,10 @@ +#line 1 + +/* 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/. */ + +varying vec4 vColor; +varying vec2 vPos; +flat varying vec4 vClipRect; +flat varying vec4 vClipRadius; diff --git a/res/ps_rectangle_clip.vs.glsl b/res/ps_rectangle_clip.vs.glsl new file mode 100644 index 0000000000..b181638d01 --- /dev/null +++ b/res/ps_rectangle_clip.vs.glsl @@ -0,0 +1,28 @@ +#line 1 +/* 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/. */ + +struct Rectangle { + PrimitiveInfo info; + vec4 color; + Clip clip; +}; + +layout(std140) uniform Items { + Rectangle rects[WR_MAX_PRIM_ITEMS]; +}; + +void main(void) { + Rectangle rect = rects[gl_InstanceID]; + VertexInfo vi = write_vertex(rect.info); + + vClipRect = vec4(rect.clip.rect.xy, rect.clip.rect.xy + rect.clip.rect.zw); + vClipRadius = vec4(rect.clip.top_left.outer_inner_radius.x, + rect.clip.top_right.outer_inner_radius.x, + rect.clip.bottom_right.outer_inner_radius.x, + rect.clip.bottom_left.outer_inner_radius.x); + vPos = vi.local_clamped_pos; + + vColor = rect.color; +} diff --git a/res/tile.vs.glsl b/res/ps_rectangle_transform.fs.glsl similarity index 57% rename from res/tile.vs.glsl rename to res/ps_rectangle_transform.fs.glsl index 7f57bf1e36..a8ac40a845 100644 --- a/res/tile.vs.glsl +++ b/res/ps_rectangle_transform.fs.glsl @@ -2,10 +2,12 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -void main(void) -{ - vColorTexCoord = aBorderRadii.xy; - vBorderPosition = aBorderPosition; - gl_Position = uTransform * vec4(aPosition, 1.0); -} +void main(void) { + vec2 pos = vPos.xy / vPos.z; + + if (!point_in_rect(pos, vRect.xy, vRect.xy + vRect.zw)) { + discard; + } + oFragColor = vColor; +} diff --git a/res/ps_rectangle_transform.glsl b/res/ps_rectangle_transform.glsl new file mode 100644 index 0000000000..0ae7f839aa --- /dev/null +++ b/res/ps_rectangle_transform.glsl @@ -0,0 +1,8 @@ +/* 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/. */ + +varying vec4 vColor; + +varying vec3 vPos; +flat varying vec4 vRect; diff --git a/res/ps_rectangle_transform.vs.glsl b/res/ps_rectangle_transform.vs.glsl new file mode 100644 index 0000000000..0df4671049 --- /dev/null +++ b/res/ps_rectangle_transform.vs.glsl @@ -0,0 +1,59 @@ +#line 1 +/* 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/. */ + +struct Rectangle { + PrimitiveInfo info; + vec4 color; +}; + +layout(std140) uniform Items { + Rectangle rects[WR_MAX_PRIM_ITEMS]; +}; + +void main(void) { + Rectangle rect = rects[gl_InstanceID]; + Layer layer = layers[rect.info.layer_tile_part.x]; + Tile tile = tiles[rect.info.layer_tile_part.y]; + + vec2 p0 = rect.info.local_rect.xy; + vec2 p1 = rect.info.local_rect.xy + vec2(rect.info.local_rect.z, 0.0); + vec2 p2 = rect.info.local_rect.xy + vec2(0.0, rect.info.local_rect.w); + vec2 p3 = rect.info.local_rect.xy + rect.info.local_rect.zw; + + vec4 t0 = layer.transform * vec4(p0, 0, 1); + vec4 t1 = layer.transform * vec4(p1, 0, 1); + vec4 t2 = layer.transform * vec4(p2, 0, 1); + vec4 t3 = layer.transform * vec4(p3, 0, 1); + + vec2 tp0 = t0.xy / t0.w; + vec2 tp1 = t1.xy / t1.w; + vec2 tp2 = t2.xy / t2.w; + vec2 tp3 = t3.xy / t3.w; + + vec2 min_pos = min(tp0.xy, min(tp1.xy, min(tp2.xy, tp3.xy))); + vec2 max_pos = max(tp0.xy, max(tp1.xy, max(tp2.xy, tp3.xy))); + + vec2 min_pos_clamped = clamp(min_pos * uDevicePixelRatio, + vec2(tile.actual_rect.xy), + vec2(tile.actual_rect.xy + tile.actual_rect.zw)); + + vec2 max_pos_clamped = clamp(max_pos * uDevicePixelRatio, + vec2(tile.actual_rect.xy), + vec2(tile.actual_rect.xy + tile.actual_rect.zw)); + + vec2 clamped_pos = mix(min_pos_clamped, + max_pos_clamped, + aPosition.xy); + + vec3 layer_pos = get_layer_pos(clamped_pos / uDevicePixelRatio, rect.info.layer_tile_part.x); + + vRect = rect.info.local_rect; + vPos = layer_pos; + vColor = rect.color; + + vec2 final_pos = clamped_pos + vec2(tile.target_rect.xy) - vec2(tile.actual_rect.xy); + + gl_Position = uTransform * vec4(final_pos, 0, 1); +} diff --git a/res/ps_text.fs.glsl b/res/ps_text.fs.glsl new file mode 100644 index 0000000000..175102f790 --- /dev/null +++ b/res/ps_text.fs.glsl @@ -0,0 +1,8 @@ +/* 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/. */ + +void main(void) { + float a = texture(sDiffuse, vUv).a; + oFragColor = vec4(vColor.rgb, vColor.a * a); +} diff --git a/res/ps_text.glsl b/res/ps_text.glsl new file mode 100644 index 0000000000..dad284fb0a --- /dev/null +++ b/res/ps_text.glsl @@ -0,0 +1,6 @@ +/* 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/. */ + +flat varying vec4 vColor; +varying vec2 vUv; diff --git a/res/ps_text.vs.glsl b/res/ps_text.vs.glsl new file mode 100644 index 0000000000..39e72bf868 --- /dev/null +++ b/res/ps_text.vs.glsl @@ -0,0 +1,26 @@ +#line 1 +/* 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/. */ + +struct Glyph { + PrimitiveInfo info; + vec4 color; + vec4 st_rect; +}; + +layout(std140) uniform Items { + Glyph glyphs[WR_MAX_PRIM_ITEMS]; +}; + +void main(void) { + Glyph glyph = glyphs[gl_InstanceID]; + VertexInfo vi = write_vertex(glyph.info); + + vec2 f = (vi.local_clamped_pos - vi.local_rect.p0) / (vi.local_rect.p1 - vi.local_rect.p0); + + vColor = glyph.color; + vUv = mix(glyph.st_rect.xy, + glyph.st_rect.zw, + f); +} diff --git a/res/quad.fs.glsl b/res/quad.fs.glsl deleted file mode 100644 index 8909cd0755..0000000000 --- a/res/quad.fs.glsl +++ /dev/null @@ -1,38 +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/. */ - -// GLSL point in rect test. -// See: https://stackoverflow.com/questions/12751080/glsl-point-inside-box-test -bool PointInRect(vec2 p, vec2 p0, vec2 p1) -{ - vec2 s = step(p0, p) - step(p1, p); - return s.x * s.y != 0.0; -} - -void main(void) -{ - // Clip out. - if (PointInRect(vPosition, vClipOutRect.xy, vClipOutRect.zw)) { - discard; - } - - // Clip in. - if (!PointInRect(vPosition, vClipInRect.xy, vClipInRect.zw)) { - discard; - } - - // Apply image tiling parameters (offset and scale) to color UVs. - vec2 colorTexCoord = vTileParams.xy + fract(vColorTexCoord.xy) * vTileParams.zw; - vec2 maskTexCoord = vMaskTexCoord.xy; - - // Fetch the diffuse and mask texels. - vec4 diffuse = Texture(sDiffuse, colorTexCoord); - vec4 mask = Texture(sMask, maskTexCoord); - - // Extract alpha from the mask (component depends on platform) - float alpha = GetAlphaFromMask(mask); - - // Write the final fragment color. - SetFragColor(diffuse * vec4(vColor.rgb, vColor.a * alpha)); -} diff --git a/res/quad.vs.glsl b/res/quad.vs.glsl deleted file mode 100644 index 5b43fc539b..0000000000 --- a/res/quad.vs.glsl +++ /dev/null @@ -1,112 +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/. */ - -vec2 Bilerp2(vec2 tl, vec2 tr, vec2 br, vec2 bl, vec2 st) { - return mix(mix(tl, bl, st.y), mix(tr, br, st.y), st.x); -} - -vec4 Bilerp4(vec4 tl, vec4 tr, vec4 br, vec4 bl, vec2 st) { - return mix(mix(tl, bl, st.y), mix(tr, br, st.y), st.x); -} - -void main(void) -{ - // Extract the image tiling parameters. - // These are passed to the fragment shader, since - // the uv interpolation must be done per-fragment. - vTileParams = uTileParams[Bottom7Bits(int(aMisc.w))]; - - // Determine clip rects. - vClipOutRect = uClipRects[int(aMisc.z)]; - vec4 clipInRect = uClipRects[int(aMisc.y)]; - - // Extract the complete (stacking context + css transform) transform - // for this vertex. Transform the position by it. - vec2 offsetParams = uOffsets[Bottom7Bits(int(aMisc.x))].xy; - mat4 matrix = uMatrixPalette[Bottom7Bits(int(aMisc.x))]; - - vec2 p0 = aPositionRect.xy + offsetParams; - vec2 p1 = p0 + aPositionRect.zw; - - vec2 rect_origin = SnapToPixels(p0); - vec2 rect_size = SnapToPixels(p1) - rect_origin; - - // Determine the position, color, and mask texture coordinates of this vertex. - vec4 localPos = vec4(0.0, 0.0, 0.0, 1.0); - bool isBorderCorner = int(aMisc.w) >= 0x80; - bool isBottomTriangle = IsBottomTriangle(); - if (aPosition.y == 0.0) { - localPos.y = rect_origin.y; - if (aPosition.x == 0.0) { - localPos.x = rect_origin.x; - if (isBorderCorner) { - vColor = isBottomTriangle ? aColorRectBL : aColorRectTR; - } - } else { - localPos.x = rect_origin.x + rect_size.x; - if (isBorderCorner) { - vColor = aColorRectTR; - } - } - } else { - localPos.y = rect_origin.y + rect_size.y; - if (aPosition.x == 0.0) { - localPos.x = rect_origin.x; - if (isBorderCorner) { - vColor = aColorRectBL; - } - } else { - localPos.x = rect_origin.x + rect_size.x; - if (isBorderCorner) { - vColor = isBottomTriangle ? aColorRectBL : aColorRectTR; - } - } - } - - // Rotate or clip as necessary. If there is no rotation, we can clip here in the vertex shader - // and save a whole bunch of fragment shader invocations. If there is a rotation, we fall back - // to FS clipping. - // - // The rotation angle is encoded as a negative bottom left u coordinate. (uv coordinates should - // always be nonnegative normally, and gradients don't use color textures, so this is fine.) - vec4 colorTexCoordRectBottom = aColorTexCoordRectBottom; - vec2 localST; - if (colorTexCoordRectBottom.z < 0.0) { - float angle = -colorTexCoordRectBottom.z; - vec2 center = rect_origin + rect_size / 2.0; - vec2 translatedPos = localPos.xy - center; - localST = (localPos.xy - rect_origin) / rect_size; - localPos.xy = vec2(translatedPos.x * cos(angle) - translatedPos.y * sin(angle), - translatedPos.x * sin(angle) + translatedPos.y * cos(angle)) + center; - colorTexCoordRectBottom.z = aColorTexCoordRectTop.x; - vClipInRect = clipInRect; - } else { - localPos.x = clamp(localPos.x, clipInRect.x, clipInRect.z); - localPos.y = clamp(localPos.y, clipInRect.y, clipInRect.w); - localST = (localPos.xy - rect_origin) / rect_size; - vClipInRect = vec4(-1e37, -1e37, 1e38, 1e38); - } - - vColorTexCoord = Bilerp2(aColorTexCoordRectTop.xy, aColorTexCoordRectTop.zw, - colorTexCoordRectBottom.xy, colorTexCoordRectBottom.zw, - localST); - vMaskTexCoord = Bilerp2(aMaskTexCoordRectTop.xy, aMaskTexCoordRectTop.zw, - aMaskTexCoordRectBottom.xy, aMaskTexCoordRectBottom.zw, - localST); - if (!isBorderCorner) { - vColor = Bilerp4(aColorRectTL, aColorRectTR, aColorRectBR, aColorRectBL, localST); - } - - // Normalize the vertex color and mask texture coordinates. - vColor /= 255.0; - vMaskTexCoord /= uAtlasParams.zw; - - vPosition = localPos.xy; - - vec4 worldPos = matrix * localPos; - - // Transform by the orthographic projection into clip space. - gl_Position = uTransform * worldPos; -} - diff --git a/res/shared.glsl b/res/shared.glsl new file mode 100644 index 0000000000..2eb73d6b46 --- /dev/null +++ b/res/shared.glsl @@ -0,0 +1,45 @@ +/* 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/. */ + +//====================================================================================== +// Vertex shader attributes and uniforms +//====================================================================================== +#ifdef WR_VERTEX_SHADER + #define varying out + + // Uniform inputs + uniform mat4 uTransform; // Orthographic projection + uniform float uDevicePixelRatio; + + // Attribute inputs + in vec3 aPosition; +#endif + +//====================================================================================== +// Fragment shader attributes and uniforms +//====================================================================================== +#ifdef WR_FRAGMENT_SHADER + precision highp float; + + #define varying in + + // Uniform inputs + uniform sampler2D sDiffuse; + uniform sampler2D sMask; + + // Fragment shader outputs + out vec4 oFragColor; +#endif + +//====================================================================================== +// Interpolator definitions +//====================================================================================== + +//====================================================================================== +// VS only types and UBOs +//====================================================================================== + +//====================================================================================== +// VS only functions +//====================================================================================== diff --git a/res/shared_other.glsl b/res/shared_other.glsl new file mode 100644 index 0000000000..c6f8286b6f --- /dev/null +++ b/res/shared_other.glsl @@ -0,0 +1,64 @@ +/* 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/. */ + +//====================================================================================== +// Vertex shader attributes and uniforms +//====================================================================================== +#ifdef WR_VERTEX_SHADER + in vec4 aColorTexCoordRectTop; + in vec4 aColorRectTL; + + // box-shadow + in vec4 aBorderPosition; + in vec4 aBorderRadii; + in float aBlurRadius; + + // blur + in vec2 aDestTextureSize; + in vec2 aSourceTextureSize; +#endif + +//====================================================================================== +// Fragment shader attributes and uniforms +//====================================================================================== +#ifdef WR_FRAGMENT_SHADER + uniform vec2 uDirection; +#endif + +//====================================================================================== +// Interpolator definitions +//====================================================================================== + +// Hacks to be removed (needed for text etc) +varying vec2 vColorTexCoord; +varying vec4 vColor; + +// box_shadow +varying vec2 vPosition; +varying vec4 vBorderPosition; +varying vec4 vBorderRadii; +varying float vBlurRadius; + +// blur +varying vec2 vSourceTextureSize; +varying vec2 vDestTextureSize; + +//====================================================================================== +// VS only types and UBOs +//====================================================================================== + +//====================================================================================== +// VS only functions +//====================================================================================== + +//====================================================================================== +// FS only functions +//====================================================================================== +#ifdef WR_FRAGMENT_SHADER + +void SetFragColor(vec4 color) { + oFragColor = color; +} + +#endif diff --git a/res/tile.fs.glsl b/res/tile.fs.glsl deleted file mode 100644 index 3355ee4646..0000000000 --- a/res/tile.fs.glsl +++ /dev/null @@ -1,11 +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/. */ - -void main(void) { - vec2 textureSize = vBorderPosition.zw - vBorderPosition.xy; - vec2 colorTexCoord = vBorderPosition.xy + mod(vColorTexCoord.xy, 1.0) * textureSize; - vec4 diffuse = Texture(sDiffuse, colorTexCoord); - SetFragColor(diffuse); -} - diff --git a/src/aabbtree.rs b/src/aabbtree.rs deleted file mode 100644 index 33ff4789d8..0000000000 --- a/src/aabbtree.rs +++ /dev/null @@ -1,311 +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/. */ - -use euclid::{Point2D, Rect, Size2D}; -use internal_types::{CompiledNode, DrawListGroupId, DrawListId, DrawListItemIndex}; -use resource_list::ResourceList; -use util; - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct NodeIndex(pub u32); - -pub struct DrawListIndexBuffer { - pub draw_list_id: DrawListId, - pub indices: Vec, -} - -pub struct DrawListGroupSegment { - pub draw_list_group_id: DrawListGroupId, - pub index_buffers: Vec, -} - -#[derive(Debug, Copy, Clone, PartialEq)] -enum TreeState { - Building, - Finalized, -} - -pub struct AABBTreeNode { - pub split_rect: Rect, - pub actual_rect: Rect, - - // TODO: Use Option + NonZero here - pub children: Option, - - pub is_visible: bool, - - pub draw_list_group_segments: Vec, - - pub resource_list: Option, - pub compiled_node: Option, -} - -impl AABBTreeNode { - fn new(split_rect: &Rect) -> AABBTreeNode { - AABBTreeNode { - split_rect: split_rect.clone(), - actual_rect: Rect::zero(), - children: None, - is_visible: false, - resource_list: None, - draw_list_group_segments: Vec::new(), - compiled_node: None, - } - } - - #[inline] - fn append_item(&mut self, - draw_list_group_id: DrawListGroupId, - draw_list_id: DrawListId, - item_index: DrawListItemIndex, - rect: &Rect) { - self.actual_rect = self.actual_rect.union(rect); - - let need_new_group = match self.draw_list_group_segments.last() { - Some(group) => { - group.draw_list_group_id != draw_list_group_id - } - None => { - true - } - }; - - if need_new_group { - self.draw_list_group_segments.push(DrawListGroupSegment { - draw_list_group_id: draw_list_group_id, - index_buffers: Vec::new(), - }) - } - - let need_new_list = match self.draw_list_group_segments.last().unwrap().index_buffers.last() { - Some(draw_list) => { - draw_list.draw_list_id != draw_list_id - } - None => { - true - } - }; - - if need_new_list { - self.draw_list_group_segments.last_mut().unwrap().index_buffers.push(DrawListIndexBuffer { - draw_list_id: draw_list_id, - indices: Vec::new(), - }); - } - - self.draw_list_group_segments - .last_mut() - .unwrap() - .index_buffers - .last_mut() - .unwrap() - .indices - .push(item_index); - } - - fn item_count(&self) -> usize { - let mut count = 0; - for group in &self.draw_list_group_segments { - for list in &group.index_buffers { - count += list.indices.len(); - } - } - count - } -} - -pub struct AABBTree { - pub nodes: Vec, - pub split_size: f32, - - work_node_indices: Vec, - - state: TreeState, -} - -impl AABBTree { - pub fn new(split_size: f32, - local_bounds: &Rect) -> AABBTree { - let mut tree = AABBTree { - nodes: Vec::new(), - split_size: split_size, - work_node_indices: Vec::new(), - state: TreeState::Building, - }; - - let root_node = AABBTreeNode::new(local_bounds); - tree.nodes.push(root_node); - - tree - } - - pub fn finalize(&mut self) { - debug_assert!(self.state == TreeState::Building); - self.state = TreeState::Finalized; - } - - #[allow(dead_code)] - pub fn print(&self, node_index: NodeIndex, level: u32) { - let mut indent = String::new(); - for _ in 0..level { - indent.push_str(" "); - } - - let node = self.node(node_index); - println!("{}n={:?} sr={:?} ar={:?} c={:?} lists={} segments={}", - indent, - node_index, - node.split_rect, - node.actual_rect, - node.children, - node.draw_list_group_segments.len(), - node.item_count()); - - if let Some(child_index) = node.children { - let NodeIndex(child_index) = child_index; - self.print(NodeIndex(child_index+0), level+1); - self.print(NodeIndex(child_index+1), level+1); - } - } - - #[inline(always)] - pub fn node(&self, index: NodeIndex) -> &AABBTreeNode { - let NodeIndex(index) = index; - &self.nodes[index as usize] - } - - #[inline(always)] - pub fn node_mut(&mut self, index: NodeIndex) -> &mut AABBTreeNode { - let NodeIndex(index) = index; - &mut self.nodes[index as usize] - } - - #[inline] - fn find_best_nodes(&mut self, - node_index: NodeIndex, - rect: &Rect) { - self.split_if_needed(node_index); - - if let Some(child_node_index) = self.node(node_index).children { - let NodeIndex(child_node_index) = child_node_index; - let left_node_index = NodeIndex(child_node_index + 0); - let right_node_index = NodeIndex(child_node_index + 1); - - let left_intersect = self.node(left_node_index).split_rect.intersects(rect); - if left_intersect { - self.find_best_nodes(left_node_index, rect); - } - - let right_intersect = self.node(right_node_index).split_rect.intersects(rect); - if right_intersect { - self.find_best_nodes(right_node_index, rect); - } - } else { - self.work_node_indices.push(node_index); - } - } - - #[inline] - pub fn insert(&mut self, - rect: Rect, - draw_list_group_id: DrawListGroupId, - draw_list_id: DrawListId, - item_index: DrawListItemIndex) { - debug_assert!(self.state == TreeState::Building); - - self.find_best_nodes(NodeIndex(0), &rect); - if self.work_node_indices.is_empty() { - // TODO(gw): If this happens, it it probably caused by items having - // transforms that move them outside the local overflow. According - // to the transforms spec, the stacking context overflow should - // include transformed elements, however this isn't currently - // handled by the layout code! If it's not that, this is an - // unexpected condition and should be investigated! - debug!("WARNING: insert rect {:?} outside bounds, dropped.", rect); - } else { - for node_index in self.work_node_indices.drain(..) { - let NodeIndex(node_index) = node_index; - let node = &mut self.nodes[node_index as usize]; - node.append_item(draw_list_group_id, - draw_list_id, - item_index, - &rect); - } - } - } - - fn split_if_needed(&mut self, node_index: NodeIndex) { - if self.node(node_index).children.is_none() { - let rect = self.node(node_index).split_rect.clone(); - - let child_rects = if rect.size.width > self.split_size && - rect.size.width > rect.size.height { - let new_width = rect.size.width * 0.5; - - let left = Rect::new(rect.origin, Size2D::new(new_width, rect.size.height)); - let right = Rect::new(rect.origin + Point2D::new(new_width, 0.0), - Size2D::new(rect.size.width - new_width, rect.size.height)); - - Some((left, right)) - } else if rect.size.height > self.split_size { - let new_height = rect.size.height * 0.5; - - let left = Rect::new(rect.origin, Size2D::new(rect.size.width, new_height)); - let right = Rect::new(rect.origin + Point2D::new(0.0, new_height), - Size2D::new(rect.size.width, rect.size.height - new_height)); - - Some((left, right)) - } else { - None - }; - - if let Some((left_rect, right_rect)) = child_rects { - let child_node_index = self.nodes.len() as u32; - - let left_node = AABBTreeNode::new(&left_rect); - self.nodes.push(left_node); - - let right_node = AABBTreeNode::new(&right_rect); - self.nodes.push(right_node); - - self.node_mut(node_index).children = Some(NodeIndex(child_node_index)); - } - } - } - - fn check_node_visibility(&mut self, - node_index: NodeIndex, - rect: &Rect) { - let children = { - let node = self.node_mut(node_index); - if node.split_rect.intersects(rect) { - if !node.draw_list_group_segments.is_empty() && - node.actual_rect.intersects(rect) { - debug_assert!(node.children.is_none()); - node.is_visible = true; - } - node.children - } else { - return; - } - }; - - if let Some(child_index) = children { - let NodeIndex(child_index) = child_index; - self.check_node_visibility(NodeIndex(child_index+0), rect); - self.check_node_visibility(NodeIndex(child_index+1), rect); - } - } - - pub fn cull(&mut self, rect: &Rect) { - let _pf = util::ProfileScope::new(" cull"); - debug_assert!(self.state == TreeState::Finalized); - for node in &mut self.nodes { - node.is_visible = false; - } - if !self.nodes.is_empty() { - self.check_node_visibility(NodeIndex(0), &rect); - } - } -} diff --git a/src/batch.rs b/src/batch.rs index 67676500da..c158d47871 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -4,18 +4,11 @@ use device::{ProgramId, TextureId}; use euclid::{Point2D, Rect, Size2D}; -use internal_types::{MAX_RECT, AxisDirection, PackedVertexColorMode, PackedVertexForQuad}; -use internal_types::{PackedVertexForTextureCacheUpdate, RectUv, DevicePixel}; +use internal_types::{AxisDirection}; +use internal_types::{PackedVertexForTextureCacheUpdate}; use std::sync::atomic::Ordering::SeqCst; use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT}; use texture_cache::{BorderType, TexturePage}; -use webrender_traits::{ColorF, ComplexClipRegion}; - -pub const MAX_MATRICES_PER_BATCH: usize = 32; -pub const MAX_CLIP_RECTS_PER_BATCH: usize = 64; -pub const MAX_TILE_PARAMS_PER_BATCH: usize = 64; // TODO(gw): Constrain to max FS uniform vectors... -pub const INVALID_TILE_PARAM: u8 = 0; -pub const INVALID_CLIP_RECT_PARAM: usize = 0; static ID_COUNTER: AtomicUsize = ATOMIC_USIZE_INIT; @@ -30,25 +23,6 @@ pub struct VertexBufferId(pub usize); #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct MatrixIndex(pub u8); -#[derive(Clone, Debug)] -pub struct OffsetParams { - pub stacking_context_x0: f32, - pub stacking_context_y0: f32, - pub render_target_x0: f32, - pub render_target_y0: f32, -} - -impl OffsetParams { - pub fn identity() -> OffsetParams { - OffsetParams { - stacking_context_x0: 0.0, - stacking_context_y0: 0.0, - render_target_x0: 0.0, - render_target_y0: 0.0, - } - } -} - #[derive(Clone, Debug)] pub struct TileParams { pub u0: f32, @@ -57,307 +31,6 @@ pub struct TileParams { pub v_size: f32, } -impl VertexBufferId { - fn new() -> VertexBufferId { - VertexBufferId(new_id()) - } -} - -pub struct VertexBuffer { - pub id: VertexBufferId, - pub instance_count: u32, - pub vertices: Vec, -} - -impl VertexBuffer { - pub fn new() -> VertexBuffer { - VertexBuffer { - id: VertexBufferId::new(), - instance_count: 0, - vertices: vec![], - } - } -} - -#[derive(Debug)] -pub struct Batch { - pub color_texture_id: TextureId, - pub mask_texture_id: TextureId, - pub first_instance: u32, - pub instance_count: u32, - pub tile_params: Vec, - pub clip_rects: Vec>, -} - -impl Batch { - pub fn new(color_texture_id: TextureId, mask_texture_id: TextureId, first_instance: u32) - -> Batch { - let default_tile_params = vec![ - TileParams { - u0: 0.0, - v0: 0.0, - u_size: 1.0, - v_size: 1.0, - } - ]; - - let default_clip_rects = vec![ - Rect::new(Point2D::new(0.0, 0.0), Size2D::new(0.0, 0.0)), - ]; - - Batch { - color_texture_id: color_texture_id, - mask_texture_id: mask_texture_id, - first_instance: first_instance, - instance_count: 0, - tile_params: default_tile_params, - clip_rects: default_clip_rects, - } - } - - // TODO: This is quite inefficient - perhaps have a hashmap in addition to the vec... - fn clip_rect_index(&self, clip_rect: &Rect) -> Option { - self.clip_rects.iter().rposition(|existing_rect| { - existing_rect.origin.x == clip_rect.origin.x && - existing_rect.origin.y == clip_rect.origin.y && - existing_rect.size.width == clip_rect.size.width && - existing_rect.size.height == clip_rect.size.height - }) - } - - pub fn can_add_to_batch(&self, - color_texture_id: TextureId, - mask_texture_id: TextureId, - needs_tile_params: bool, - clip_in_rect: &Rect, - clip_out_rect: &Option>) -> bool { - let color_texture_ok = color_texture_id == self.color_texture_id; - let mask_texture_ok = mask_texture_id == self.mask_texture_id; - let tile_params_ok = !needs_tile_params || - self.tile_params.len() < MAX_TILE_PARAMS_PER_BATCH; - - let used_clip_count = self.clip_rects.len(); - - let clip_rects_ok = if used_clip_count + 2 < MAX_CLIP_RECTS_PER_BATCH { - true - } else { - let mut new_clip_count = 0; - - if self.clip_rect_index(clip_in_rect).is_none() { - new_clip_count += 1; - } - - if let &Some(ref clip_out_rect) = clip_out_rect { - if self.clip_rect_index(clip_out_rect).is_none() { - new_clip_count += 1; - } - } - - used_clip_count + new_clip_count < MAX_CLIP_RECTS_PER_BATCH - }; - - color_texture_ok && - mask_texture_ok && - tile_params_ok && - clip_rects_ok - } - - pub fn add_draw_item(&mut self, - tile_params: Option, - clip_in_rect: &Rect, - clip_out_rect: &Option>) -> (u8, u8, u8) { - self.instance_count += 1; - - let tile_params_index = tile_params.map_or(INVALID_TILE_PARAM, |tile_params| { - let index = self.tile_params.len(); - debug_assert!(index < MAX_TILE_PARAMS_PER_BATCH); - self.tile_params.push(tile_params); - index as u8 - }); - - let clip_in_rect_index = match self.clip_rect_index(clip_in_rect) { - Some(clip_in_rect_index) => { - clip_in_rect_index - } - None => { - let new_index = self.clip_rects.len(); - debug_assert!(new_index < MAX_CLIP_RECTS_PER_BATCH); - self.clip_rects.push(*clip_in_rect); - new_index - } - } as u8; - - let clip_out_rect_index = match clip_out_rect { - &Some(ref clip_out_rect) => { - match self.clip_rect_index(clip_out_rect) { - Some(clip_out_rect_index) => { - clip_out_rect_index - } - None => { - let new_index = self.clip_rects.len(); - debug_assert!(new_index < MAX_CLIP_RECTS_PER_BATCH); - self.clip_rects.push(*clip_out_rect); - new_index - } - } - } - &None => { - INVALID_CLIP_RECT_PARAM - } - } as u8; - - (tile_params_index, clip_in_rect_index, clip_out_rect_index) - } -} - -pub struct BatchBuilder<'a> { - vertex_buffer: &'a mut VertexBuffer, - batches: Vec, - current_matrix_index: u8, - - clip_offset: Point2D, - - clip_in_rect_stack: Vec>, - cached_clip_in_rect: Option>, - - clip_out_rect: Option>, - - // TODO(gw): Support nested complex clip regions! - pub complex_clip: Option, - - pub device_pixel_ratio: f32, -} - -impl<'a> BatchBuilder<'a> { - pub fn new(vertex_buffer: &mut VertexBuffer, - device_pixel_ratio: f32) -> BatchBuilder { - BatchBuilder { - vertex_buffer: vertex_buffer, - batches: Vec::new(), - current_matrix_index: 0, - clip_in_rect_stack: Vec::new(), - cached_clip_in_rect: Some(MAX_RECT), - clip_out_rect: None, - complex_clip: None, - clip_offset: Point2D::zero(), - device_pixel_ratio: device_pixel_ratio, - } - } - - pub fn finalize(self) -> Vec { - self.batches - } - - pub fn set_current_clip_rect_offset(&mut self, offset: Point2D) { - self.clip_offset = offset; - } - - pub fn next_draw_list(&mut self) { - debug_assert!((self.current_matrix_index as usize) < MAX_MATRICES_PER_BATCH); - self.current_matrix_index += 1; - } - - // TODO(gw): This is really inefficient to call this every push/pop... - fn update_clip_in_rect(&mut self) { - self.cached_clip_in_rect = Some(MAX_RECT); - - for rect in &self.clip_in_rect_stack { - self.cached_clip_in_rect = self.cached_clip_in_rect.unwrap().intersection(rect); - if self.cached_clip_in_rect.is_none() { - return; - } - } - } - - pub fn push_clip_in_rect(&mut self, rect: &Rect) { - let rect = rect.translate(&self.clip_offset); - self.clip_in_rect_stack.push(rect); - self.update_clip_in_rect(); - } - - pub fn pop_clip_in_rect(&mut self) { - self.clip_in_rect_stack.pop(); - self.update_clip_in_rect(); - } - - pub fn set_clip_out_rect(&mut self, rect: Option>) -> Option> { - let rect = rect.map(|rect| { - rect.translate(&self.clip_offset) - }); - let old_rect = self.clip_out_rect.take(); - self.clip_out_rect = rect; - old_rect - } - - pub fn push_complex_clip(&mut self, clip: &[ComplexClipRegion]) { - // TODO(gw): Handle nested complex clips! - if clip.len() > 0 { - self.complex_clip = Some(clip[0]); - } else { - self.complex_clip = None; - } - } - - pub fn pop_complex_clip(&mut self) { - self.complex_clip = None; - } - - // Colors are in the order: top left, top right, bottom right, bottom left. - pub fn add_rectangle(&mut self, - color_texture_id: TextureId, - mask_texture_id: TextureId, - pos_rect: &Rect, - uv_rect: &RectUv, - muv_rect: &RectUv, - colors: &[ColorF; 4], - color_mode: PackedVertexColorMode, - tile_params: Option) { - let (tile_params_index, - clip_in_rect_index, - clip_out_rect_index) = match self.cached_clip_in_rect { - None => return, - Some(ref clip_in_rect) => { - let need_new_batch = match self.batches.last_mut() { - Some(batch) => { - !batch.can_add_to_batch(color_texture_id, - mask_texture_id, - tile_params.is_some(), - clip_in_rect, - &self.clip_out_rect) - } - None => { - true - } - }; - - if need_new_batch { - self.batches.push(Batch::new(color_texture_id, - mask_texture_id, - self.vertex_buffer.instance_count)); - } - - self.batches.last_mut().unwrap().add_draw_item(tile_params, - clip_in_rect, - &self.clip_out_rect) - } - }; - - let mut vertex = PackedVertexForQuad::new(pos_rect, colors, uv_rect, muv_rect, color_mode); - vertex.matrix_index = self.current_matrix_index; - vertex.tile_params_index = vertex.tile_params_index | tile_params_index; - vertex.clip_in_rect_index = clip_in_rect_index; - vertex.clip_out_rect_index = clip_out_rect_index; - - self.push_vertex_for_rectangle(vertex); - - self.vertex_buffer.instance_count += 1 - } - - fn push_vertex_for_rectangle(&mut self, vertex: PackedVertexForQuad) { - self.vertex_buffer.vertices.push(vertex); - } -} - // Information needed to blit an item from a raster op batch target to final destination. pub struct BlitJob { pub dest_texture_id: TextureId, diff --git a/src/batch_builder.rs b/src/batch_builder.rs index cc81b8ba52..5ac3c135c3 100644 --- a/src/batch_builder.rs +++ b/src/batch_builder.rs @@ -2,1844 +2,14 @@ * 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 app_units::Au; -use batch::{BatchBuilder, TileParams}; -use device::TextureId; -use euclid::{Rect, Point2D, Size2D}; -use fnv::FnvHasher; -use frame::FrameId; -use internal_types::{AxisDirection, BasicRotationAngle, BorderRadiusRasterOp, BoxShadowRasterOp}; -use internal_types::{GlyphKey, PackedVertexColorMode, RasterItem, RectColors, RectPolygon}; -use internal_types::{RectSide, RectUv, DevicePixel}; -use num_traits::Zero; -use renderer::BLUR_INFLATION_FACTOR; -use resource_cache::ResourceCache; -use std::cmp::Ordering; -use std::collections::HashMap; -use std::collections::hash_map::Entry::{Occupied, Vacant}; +use euclid::{Rect, Point2D}; use std::f32; -use std::hash::BuildHasherDefault; -use tessellator::{self, BorderCornerTessellation}; -use texture_cache::{TextureCacheItem}; -use util; -use util::RectVaryings; -use webrender_traits::{AuxiliaryLists, ColorF, ImageFormat, BorderStyle, BoxShadowClipMode}; -use webrender_traits::{BorderSide, FontKey, GlyphInstance, ImageKey}; -use webrender_traits::{BorderDisplayItem, GradientStop, ImageRendering}; -use webrender_traits::{WebGLContextId, ItemRange}; +use webrender_traits::{ColorF, BorderStyle}; +use webrender_traits::{BorderSide}; -const BORDER_DASH_SIZE: f32 = 3.0; +//const BORDER_DASH_SIZE: f32 = 3.0; -enum ClipState { - None, - ClipIn, - ClipOut(Option>) -} - -#[derive(Debug)] -struct GradientRect { - rect: Rect, - rect_uv: RectUv, - color0: ColorF, - color1: ColorF, -} - -impl<'a> BatchBuilder<'a> { - - // Colors are in the order: top left, top right, bottom right, bottom left. - pub fn add_simple_rectangle(&mut self, - color_texture_id: TextureId, - pos_rect: &Rect, - uv_rect: &RectUv, - mask_texture_id: TextureId, - muv_rect: &RectUv, - colors: &[ColorF; 4], - tile_params: Option) { - if pos_rect.size.width == 0.0 || pos_rect.size.height == 0.0 { - return - } - - self.add_rectangle(color_texture_id, - mask_texture_id, - pos_rect, - uv_rect, - muv_rect, - colors, - PackedVertexColorMode::Gradient, - tile_params); - } - - #[inline] - fn snap_value_to_device_pixel(&self, value: &mut f32) { - *value = (*value * self.device_pixel_ratio).round() / self.device_pixel_ratio; - } - - // Colors are in the order: top left, top right, bottom right, bottom left. - pub fn add_complex_clipped_rectangle(&mut self, - color_texture_id: TextureId, - pos_rect: &Rect, - uv_rect: &RectUv, - colors: &[ColorF; 4], - tile_params: Option, - resource_cache: &ResourceCache, - frame_id: FrameId) { - if pos_rect.size.width == 0.0 || pos_rect.size.height == 0.0 { - return - } - - match self.complex_clip { - Some(complex_clip) => { - // Get clip rect in local space of the display rect - let complex_clip_origin = complex_clip.rect.origin - pos_rect.origin; - let mut local_pos_rect = Rect::new(Point2D::zero(), pos_rect.size); - - // TODO(gw): Perhaps this should be caught higher up, or perhaps we - // can even remove this requirement. - self.snap_value_to_device_pixel(&mut local_pos_rect.size.width); - self.snap_value_to_device_pixel(&mut local_pos_rect.size.height); - - let mut tl_xr = complex_clip.radii.top_left.width; - let mut tl_yr = complex_clip.radii.top_left.height; - let mut tr_xr = complex_clip.radii.top_right.width; - let mut tr_yr = complex_clip.radii.top_right.height; - - let mut bl_xr = complex_clip.radii.bottom_left.width; - let mut bl_yr = complex_clip.radii.bottom_left.height; - let mut br_xr = complex_clip.radii.bottom_right.width; - let mut br_yr = complex_clip.radii.bottom_right.height; - - self.snap_value_to_device_pixel(&mut tl_xr); - self.snap_value_to_device_pixel(&mut tl_yr); - self.snap_value_to_device_pixel(&mut tr_xr); - self.snap_value_to_device_pixel(&mut tr_yr); - self.snap_value_to_device_pixel(&mut bl_xr); - self.snap_value_to_device_pixel(&mut bl_yr); - self.snap_value_to_device_pixel(&mut br_xr); - self.snap_value_to_device_pixel(&mut br_yr); - - // Generate all vertices for each line - let mut x_points = [ - complex_clip_origin.x + 0.0, - complex_clip_origin.x + complex_clip.radii.top_left.width, - complex_clip_origin.x + complex_clip.rect.size.width - complex_clip.radii.top_right.width, - complex_clip_origin.x + complex_clip.radii.bottom_left.width, - complex_clip_origin.x + complex_clip.rect.size.width - complex_clip.radii.bottom_right.width, - complex_clip_origin.x + complex_clip.rect.size.width, - ]; - - // Generate all vertices for each line - let mut y_points = [ - complex_clip_origin.y + 0.0, - complex_clip_origin.y + complex_clip.radii.top_left.height, - complex_clip_origin.y + complex_clip.radii.top_right.height, - complex_clip_origin.y + complex_clip.rect.size.height - complex_clip.radii.bottom_left.height, - complex_clip_origin.y + complex_clip.rect.size.height - complex_clip.radii.bottom_right.height, - complex_clip_origin.y + complex_clip.rect.size.height, - ]; - - for x_point in &mut x_points { - self.snap_value_to_device_pixel(x_point); - } - - for y_point in &mut y_points { - self.snap_value_to_device_pixel(y_point); - } - - let tl_clip = Rect::new(Point2D::new(x_points[0], y_points[0]), - Size2D::new(tl_xr, tl_yr)); - - let tr_clip = Rect::new(Point2D::new(x_points[2], y_points[0]), - Size2D::new(tr_xr, tr_yr)); - - let bl_clip = Rect::new(Point2D::new(x_points[0], y_points[3]), - Size2D::new(bl_xr, bl_yr)); - - let br_clip = Rect::new(Point2D::new(x_points[4], y_points[4]), - Size2D::new(br_xr, br_yr)); - - x_points.sort_by(|a, b| { - a.partial_cmp(b).unwrap() - }); - y_points.sort_by(|a, b| { - a.partial_cmp(b).unwrap() - }); - - for xi in 0..x_points.len()-1 { - for yi in 0..y_points.len()-1 { - let x0 = x_points[xi+0]; - let y0 = y_points[yi+0]; - let x1 = x_points[xi+1]; - let y1 = y_points[yi+1]; - - if x0 != x1 && y0 != y1 { - let sub_clip_rect = Rect::new(Point2D::new(x0, y0), - Size2D::new(x1-x0, y1-y0)); - - if let Some(clipped_pos_rect) = sub_clip_rect.intersection(&local_pos_rect) { - debug_assert!((clipped_pos_rect.origin.x * self.device_pixel_ratio).fract() == 0.0); - debug_assert!((clipped_pos_rect.origin.y * self.device_pixel_ratio).fract() == 0.0); - debug_assert!((clipped_pos_rect.size.width * self.device_pixel_ratio).fract() == 0.0); - debug_assert!((clipped_pos_rect.size.height * self.device_pixel_ratio).fract() == 0.0); - - let u0_f = clipped_pos_rect.origin.x / local_pos_rect.size.width; - let v0_f = clipped_pos_rect.origin.y / local_pos_rect.size.height; - let u1_f = (clipped_pos_rect.origin.x + clipped_pos_rect.size.width) / local_pos_rect.size.width; - let v1_f = (clipped_pos_rect.origin.y + clipped_pos_rect.size.height) / local_pos_rect.size.height; - - let u_size = uv_rect.top_right.x - uv_rect.top_left.x; - let v_size = uv_rect.bottom_right.y - uv_rect.top_left.y; - - let u0 = uv_rect.top_left.x + u0_f * u_size; - let v0 = uv_rect.top_left.y + v0_f * v_size; - let u1 = uv_rect.top_left.x + u1_f * u_size; - let v1 = uv_rect.top_left.y + v1_f * v_size; - - let uv_rect = RectUv { - top_left: Point2D::new(u0, v0), - top_right: Point2D::new(u1, v0), - bottom_left: Point2D::new(u0, v1), - bottom_right: Point2D::new(u1, v1), - }; - - // TODO(gw): There must be a more efficient way to to - // this (classifying which clip mask we need). - let (mask_info, angle) = if sub_clip_rect.intersects(&tl_clip) { - (Some(&tl_clip), BasicRotationAngle::Upright) - } else if sub_clip_rect.intersects(&tr_clip) { - (Some(&tr_clip), BasicRotationAngle::Clockwise90) - } else if sub_clip_rect.intersects(&bl_clip) { - (Some(&bl_clip), BasicRotationAngle::Clockwise270) - } else if sub_clip_rect.intersects(&br_clip) { - (Some(&br_clip), BasicRotationAngle::Clockwise180) - } else { - (None, BasicRotationAngle::Upright) - }; - - let (mask_texture_id, muv_rect) = match mask_info { - Some(clip_rect) => { - let op = RasterItem::BorderRadius(BorderRadiusRasterOp { - outer_radius_x: - DevicePixel::new(clip_rect.size.width, - self.device_pixel_ratio), - outer_radius_y: - DevicePixel::new(clip_rect.size.height, - self.device_pixel_ratio), - inner_radius_x: DevicePixel::zero(), - inner_radius_y: DevicePixel::zero(), - inverted: false, - index: None, - image_format: ImageFormat::A8, - }); - - let mask_image = resource_cache.get_raster(&op, frame_id); - - let mut x0_f = (x0 - clip_rect.origin.x) / clip_rect.size.width; - let mut x1_f = (x1 - clip_rect.origin.x) / clip_rect.size.width; - let mut y0_f = (y0 - clip_rect.origin.y) / clip_rect.size.height; - let mut y1_f = (y1 - clip_rect.origin.y) / clip_rect.size.height; - - match angle { - BasicRotationAngle::Upright => {} - BasicRotationAngle::Clockwise90 => { - x0_f = 1.0 - x0_f; - x1_f = 1.0 - x1_f; - } - BasicRotationAngle::Clockwise180 => { - x0_f = 1.0 - x0_f; - x1_f = 1.0 - x1_f; - y0_f = 1.0 - y0_f; - y1_f = 1.0 - y1_f; - } - BasicRotationAngle::Clockwise270 => { - y0_f = 1.0 - y0_f; - y1_f = 1.0 - y1_f; - } - } - - let mu0 = mask_image.pixel_rect.top_left.x.as_f32(); - let mu1 = mask_image.pixel_rect.top_right.x.as_f32(); - let mv0 = mask_image.pixel_rect.top_left.y.as_f32(); - let mv1 = mask_image.pixel_rect.bottom_left.y.as_f32(); - - let mu_size = mu1 - mu0; - let mv_size = mv1 - mv0; - let mu1 = mu0 + x1_f * mu_size; - let mu0 = mu0 + x0_f * mu_size; - let mv1 = mv0 + y1_f * mv_size; - let mv0 = mv0 + y0_f * mv_size; - - let mu0 = DevicePixel::from_f32(mu0); - let mv0 = DevicePixel::from_f32(mv0); - let mu1 = DevicePixel::from_f32(mu1); - let mv1 = DevicePixel::from_f32(mv1); - - let muv_rect = RectUv { - top_left: Point2D::new(mu0, mv0), - top_right: Point2D::new(mu1, mv0), - bottom_left: Point2D::new(mu0, mv1), - bottom_right: Point2D::new(mu1, mv1), - }; - - (mask_image.texture_id, muv_rect) - } - None => { - let mask_image = resource_cache.get_dummy_mask_image(); - (mask_image.texture_id, mask_image.pixel_rect) - } - }; - - // TODO(gw): Needless conversions here - just to make it - // easier to operate with existing bilerp code - clean this up! - let world_clipped_pos_rect = clipped_pos_rect.translate(&pos_rect.origin); - - let rect_colors = RectColors::from_elements(colors); - let rect_colors = util::bilerp_rect(&world_clipped_pos_rect, - &pos_rect, - &rect_colors); - - self.add_simple_rectangle(color_texture_id, - &world_clipped_pos_rect, - &uv_rect, - mask_texture_id, - &muv_rect, - &[rect_colors.top_left, - rect_colors.top_right, - rect_colors.bottom_right, - rect_colors.bottom_left, - ], - tile_params.clone()); - } - } - } - } - } - None => { - let dummy_mask_image = resource_cache.get_dummy_mask_image(); - - self.add_simple_rectangle(color_texture_id, - pos_rect, - uv_rect, - dummy_mask_image.texture_id, - &dummy_mask_image.pixel_rect, - colors, - tile_params); - } - } - } - - #[inline] - pub fn add_color_rectangle(&mut self, - rect: &Rect, - color: &ColorF, - resource_cache: &ResourceCache, - frame_id: FrameId) { - let white_image = resource_cache.get_dummy_color_image(); - self.add_complex_clipped_rectangle(white_image.texture_id, - rect, - &white_image.uv_rect(), - &[*color, *color, *color, *color], - None, - resource_cache, - frame_id); - } - - pub fn add_webgl_rectangle(&mut self, - rect: &Rect, - resource_cache: &ResourceCache, - webgl_context_id: &WebGLContextId, - frame_id: FrameId) { - let texture_id = resource_cache.get_webgl_texture(webgl_context_id); - let color = ColorF::new(1.0, 1.0, 1.0, 1.0); - - let uv = RectUv { - top_left: Point2D::new(0.0, 1.0), - top_right: Point2D::new(1.0, 1.0), - bottom_left: Point2D::zero(), - bottom_right: Point2D::new(1.0, 0.0), - }; - - self.add_complex_clipped_rectangle(texture_id, - rect, - &uv, - &[color, color, color, color], - None, - resource_cache, - frame_id); - } - - pub fn add_image(&mut self, - rect: &Rect, - stretch_size: &Size2D, - image_key: ImageKey, - image_rendering: ImageRendering, - resource_cache: &ResourceCache, - frame_id: FrameId) { - // Should be caught higher up - debug_assert!(stretch_size.width > 0.0 && stretch_size.height > 0.0); - let image_info = resource_cache.get_image(image_key, image_rendering, frame_id); - - let u1 = rect.size.width / stretch_size.width; - let v1 = rect.size.height / stretch_size.height; - - let uv = RectUv { - top_left: Point2D::zero(), - top_right: Point2D::new(u1, 0.0), - bottom_left: Point2D::new(0.0, v1), - bottom_right: Point2D::new(u1, v1), - }; - - let uv_rect = image_info.uv_rect(); - let uv_size = uv_rect.bottom_right - uv_rect.top_left; - - let tile_params = TileParams { - u0: image_info.uv_rect().top_left.x, - v0: image_info.uv_rect().top_left.y, - u_size: uv_size.x, - v_size: uv_size.y, - }; - - let color = ColorF::new(1.0, 1.0, 1.0, 1.0); - - self.add_complex_clipped_rectangle(image_info.texture_id, - rect, - &uv, - &[color, color, color, color], - Some(tile_params), - resource_cache, - frame_id); - } - - pub fn add_text(&mut self, - _rect: &Rect, - font_key: FontKey, - size: Au, - blur_radius: Au, - color: &ColorF, - glyphs: &[GlyphInstance], - resource_cache: &ResourceCache, - frame_id: FrameId, - device_pixel_ratio: f32) { - let dummy_mask_image = resource_cache.get_dummy_mask_image(); - - // Logic below to pick the primary render item depends on len > 0! - assert!(glyphs.len() > 0); - - let mut glyph_key = GlyphKey::new(font_key, size, blur_radius, glyphs[0].index); - let blur_offset = blur_radius.to_f32_px() * (BLUR_INFLATION_FACTOR as f32) / 2.0; - - let mut text_batches: HashMap>>, - BuildHasherDefault> = - HashMap::with_hasher(Default::default()); - - for glyph in glyphs { - glyph_key.index = glyph.index; - let image_info = resource_cache.get_glyph(&glyph_key, frame_id); - if let Some(image_info) = image_info { - let x = glyph.x + image_info.user_data.x0 as f32 / device_pixel_ratio - blur_offset; - let y = glyph.y - image_info.user_data.y0 as f32 / device_pixel_ratio - blur_offset; - - let width = image_info.requested_rect.size.width as f32 / device_pixel_ratio; - let height = image_info.requested_rect.size.height as f32 / device_pixel_ratio; - - let rect = RectPolygon { - pos: Rect::new(Point2D::new(x, y), - Size2D::new(width, height)), - varyings: image_info.uv_rect(), - }; - - let rect_buffer = match text_batches.entry(image_info.texture_id) { - Occupied(entry) => entry.into_mut(), - Vacant(entry) => entry.insert(Vec::new()), - }; - - rect_buffer.push(rect); - } - } - - for (texture_id, rect_buffer) in text_batches { - for rect in rect_buffer { - self.add_rectangle(texture_id, - dummy_mask_image.texture_id, - &rect.pos, - &rect.varyings, - &dummy_mask_image.pixel_rect, - &[*color, *color, *color, *color], - PackedVertexColorMode::Gradient, - None); - } - - } - } - - fn add_axis_aligned_gradient_with_stops(&mut self, - rect: &Rect, - direction: AxisDirection, - stops: &[GradientStop], - resource_cache: &ResourceCache, - frame_id: FrameId) { - let white_image = resource_cache.get_dummy_color_image(); - - for i in 0..(stops.len() - 1) { - let (prev_stop, next_stop) = (&stops[i], &stops[i + 1]); - let piece_rect; - let piece_colors; - match direction { - AxisDirection::Horizontal => { - let prev_x = util::lerp(rect.origin.x, rect.max_x(), prev_stop.offset); - let next_x = util::lerp(rect.origin.x, rect.max_x(), next_stop.offset); - piece_rect = Rect::new(Point2D::new(prev_x, rect.origin.y), - Size2D::new(next_x - prev_x, rect.size.height)); - piece_colors = [ - prev_stop.color, - next_stop.color, - next_stop.color, - prev_stop.color - ]; - } - AxisDirection::Vertical => { - let prev_y = util::lerp(rect.origin.y, rect.max_y(), prev_stop.offset); - let next_y = util::lerp(rect.origin.y, rect.max_y(), next_stop.offset); - piece_rect = Rect::new(Point2D::new(rect.origin.x, prev_y), - Size2D::new(rect.size.width, next_y - prev_y)); - piece_colors = [ - prev_stop.color, - prev_stop.color, - next_stop.color, - next_stop.color - ]; - } - } - - self.add_complex_clipped_rectangle(white_image.texture_id, - &piece_rect, - &white_image.uv_rect(), - &piece_colors, - None, - resource_cache, - frame_id); - } - } - - pub fn add_gradient(&mut self, - rect: &Rect, - start_point: &Point2D, - end_point: &Point2D, - stops: &ItemRange, - auxiliary_lists: &AuxiliaryLists, - resource_cache: &ResourceCache, - frame_id: FrameId) { - let stops = auxiliary_lists.gradient_stops(stops); - - // Fast paths for axis-aligned gradients: - if start_point.x == end_point.x { - let rect = Rect::new(Point2D::new(rect.origin.x, start_point.y), - Size2D::new(rect.size.width, end_point.y - start_point.y)); - self.add_axis_aligned_gradient_with_stops(&rect, - AxisDirection::Vertical, - stops, - resource_cache, - frame_id); - return - } - if start_point.y == end_point.y { - let rect = Rect::new(Point2D::new(start_point.x, rect.origin.y), - Size2D::new(end_point.x - start_point.x, rect.size.height)); - self.add_axis_aligned_gradient_with_stops(&rect, - AxisDirection::Horizontal, - stops, - resource_cache, - frame_id); - return - } - - let white_image = resource_cache.get_dummy_color_image(); - let dummy_mask_image = resource_cache.get_dummy_mask_image(); - - debug_assert!(stops.len() >= 2); - - let mut angle = ((end_point.y - start_point.y) / (end_point.x - start_point.x)).atan() + - f32::consts::FRAC_PI_2; - if angle < 0.0 { - angle += 2.0 * f32::consts::PI - } - - // A simple way to estimate the length of each strip we'll need. Providing a good estimate - // saves fragment shader invocations. - let length_0 = (rect.size.width * angle.sin()).abs() + - (rect.size.height * angle.cos()).abs(); - let length_1 = (rect.size.width * angle.cos()).abs() + - (rect.size.height * angle.sin()).abs(); - let length = if length_0 > length_1 { - length_0 - } else { - length_1 - }; - - let mut rectangles = Vec::new(); - let mut prev_color = stops[0].color; - let mut prev_offset = 0.0; - for next in &stops[..] { - let prev_point = util::lerp_points(start_point, end_point, prev_offset); - let next_point = util::lerp_points(start_point, end_point, next.offset); - let midpoint = util::lerp_points(&prev_point, &next_point, 0.5); - - let height = util::distance(&prev_point, &next_point); - let rect = - Rect::new(Point2D::new(-length / 2.0 + midpoint.x, midpoint.y - height / 2.0), - Size2D::new(length, height)); - let mut rect_uv = white_image.uv_rect(); - rect_uv.bottom_left.x = -angle; - - rectangles.push(GradientRect { - rect: rect, - rect_uv: rect_uv, - color0: next.color, - color1: prev_color - }); - - prev_color = next.color; - prev_offset = next.offset - } - - // This is a bit of a hack to pass reftests. In some cases of angled gradients, - // there is a one pixel overlap along the stop where two rectangles are drawn. - // To ensure that these are drawn the same in reftests, ensure the rectangles - // are always submitted in the same order no matter what angle they were - // generated from. - // TODO(gw): This probably deserves a bit more investigation to see if there - // is a better solution! - rectangles.sort_by(|a, b| { - match a.rect.origin.x.partial_cmp(&b.rect.origin.x).unwrap() { - Ordering::Equal => a.rect.origin.y.partial_cmp(&b.rect.origin.y).unwrap(), - ordering => ordering, - } - }); - - for gradient_rect in rectangles { - self.add_rectangle(white_image.texture_id, - dummy_mask_image.texture_id, - &gradient_rect.rect, - &gradient_rect.rect_uv, - &dummy_mask_image.pixel_rect, - &[gradient_rect.color0, - gradient_rect.color0, - gradient_rect.color1, - gradient_rect.color1 - ], - PackedVertexColorMode::Gradient, - None); - } - } - - pub fn add_box_shadow(&mut self, - box_bounds: &Rect, - box_offset: &Point2D, - color: &ColorF, - blur_radius: f32, - spread_radius: f32, - border_radius: f32, - clip_mode: BoxShadowClipMode, - resource_cache: &ResourceCache, - frame_id: FrameId) { - let rect = compute_box_shadow_rect(box_bounds, box_offset, spread_radius, clip_mode); - - // Fast path. - if blur_radius == 0.0 && spread_radius == 0.0 && clip_mode == BoxShadowClipMode::None { - self.add_color_rectangle(&rect, color, resource_cache, frame_id); - return; - } - - // Draw the corners. - self.add_box_shadow_corners(box_bounds, - box_offset, - color, - blur_radius, - spread_radius, - border_radius, - clip_mode, - resource_cache, - frame_id); - - // Draw the sides. - self.add_box_shadow_sides(box_bounds, - box_offset, - color, - blur_radius, - spread_radius, - border_radius, - clip_mode, - resource_cache, - frame_id); - - match clip_mode { - BoxShadowClipMode::None => { - // Fill the center area. - self.add_color_rectangle(box_bounds, color, resource_cache, frame_id); - } - BoxShadowClipMode::Outset => { - // Fill the center area. - let metrics = BoxShadowMetrics::new(&rect, border_radius, blur_radius); - if metrics.br_inner.x > metrics.tl_inner.x && - metrics.br_inner.y > metrics.tl_inner.y { - let center_rect = - Rect::new(metrics.tl_inner, - Size2D::new(metrics.br_inner.x - metrics.tl_inner.x, - metrics.br_inner.y - metrics.tl_inner.y)); - - // FIXME(pcwalton): This assumes the border radius is zero. That is not always - // the case! - let old_clip_out_rect = self.set_clip_out_rect(Some(*box_bounds)); - - self.add_color_rectangle(¢er_rect, color, resource_cache, frame_id); - - self.set_clip_out_rect(old_clip_out_rect); - } - } - BoxShadowClipMode::Inset => { - // Fill in the outsides. - self.fill_outside_area_of_inset_box_shadow(box_bounds, - box_offset, - color, - blur_radius, - spread_radius, - border_radius, - resource_cache, - frame_id); - } - } - } - - fn add_box_shadow_corners(&mut self, - box_bounds: &Rect, - box_offset: &Point2D, - color: &ColorF, - blur_radius: f32, - spread_radius: f32, - border_radius: f32, - clip_mode: BoxShadowClipMode, - resource_cache: &ResourceCache, - frame_id: FrameId) { - // Draw the corners. - // - // +--+------------------+--+ - // |##| |##| - // +--+------------------+--+ - // | | | | - // | | | | - // | | | | - // +--+------------------+--+ - // |##| |##| - // +--+------------------+--+ - - let rect = compute_box_shadow_rect(box_bounds, box_offset, spread_radius, clip_mode); - let metrics = BoxShadowMetrics::new(&rect, border_radius, blur_radius); - - let clip_state = self.adjust_clip_for_box_shadow_clip_mode(box_bounds, - border_radius, - clip_mode); - - // Prevent overlap of the box shadow corners when the size of the blur is larger than the - // size of the box. - let center = Point2D::new(rect.origin.x + rect.size.width / 2.0, - rect.origin.y + rect.size.height / 2.0); - - self.add_box_shadow_corner(&metrics.tl_outer, - &Point2D::new(metrics.tl_outer.x + metrics.edge_size, - metrics.tl_outer.y + metrics.edge_size), - &metrics.tl_outer, - ¢er, - &rect, - &color, - blur_radius, - border_radius, - clip_mode, - resource_cache, - frame_id, - BasicRotationAngle::Upright); - self.add_box_shadow_corner(&Point2D::new(metrics.tr_outer.x - metrics.edge_size, - metrics.tr_outer.y), - &Point2D::new(metrics.tr_outer.x, - metrics.tr_outer.y + metrics.edge_size), - &Point2D::new(center.x, metrics.tr_outer.y), - &Point2D::new(metrics.tr_outer.x, center.y), - &rect, - &color, - blur_radius, - border_radius, - clip_mode, - resource_cache, - frame_id, - BasicRotationAngle::Clockwise90); - self.add_box_shadow_corner(&Point2D::new(metrics.br_outer.x - metrics.edge_size, - metrics.br_outer.y - metrics.edge_size), - &Point2D::new(metrics.br_outer.x, metrics.br_outer.y), - ¢er, - &metrics.br_outer, - &rect, - &color, - blur_radius, - border_radius, - clip_mode, - resource_cache, - frame_id, - BasicRotationAngle::Clockwise180); - self.add_box_shadow_corner(&Point2D::new(metrics.bl_outer.x, - metrics.bl_outer.y - metrics.edge_size), - &Point2D::new(metrics.bl_outer.x + metrics.edge_size, - metrics.bl_outer.y), - &Point2D::new(metrics.bl_outer.x, center.y), - &Point2D::new(center.x, metrics.bl_outer.y), - &rect, - &color, - blur_radius, - border_radius, - clip_mode, - resource_cache, - frame_id, - BasicRotationAngle::Clockwise270); - - self.undo_clip_state(clip_state); - } - - fn add_box_shadow_sides(&mut self, - box_bounds: &Rect, - box_offset: &Point2D, - color: &ColorF, - blur_radius: f32, - spread_radius: f32, - border_radius: f32, - clip_mode: BoxShadowClipMode, - resource_cache: &ResourceCache, - frame_id: FrameId) { - let rect = compute_box_shadow_rect(box_bounds, box_offset, spread_radius, clip_mode); - let metrics = BoxShadowMetrics::new(&rect, border_radius, blur_radius); - - let clip_state = self.adjust_clip_for_box_shadow_clip_mode(box_bounds, - border_radius, - clip_mode); - - if clip_mode == BoxShadowClipMode::Inset && blur_radius == 0.0 { - return; - } - - // Draw the sides. - // - // +--+------------------+--+ - // | |##################| | - // +--+------------------+--+ - // |##| |##| - // |##| |##| - // |##| |##| - // +--+------------------+--+ - // | |##################| | - // +--+------------------+--+ - - let horizontal_size = Size2D::new(metrics.br_inner.x - metrics.tl_inner.x, - metrics.edge_size); - let vertical_size = Size2D::new(metrics.edge_size, - metrics.br_inner.y - metrics.tl_inner.y); - let top_rect = Rect::new(metrics.tl_outer + Point2D::new(metrics.edge_size, 0.0), - horizontal_size); - let right_rect = - Rect::new(metrics.tr_outer + Point2D::new(-metrics.edge_size, metrics.edge_size), - vertical_size); - let bottom_rect = - Rect::new(metrics.bl_outer + Point2D::new(metrics.edge_size, -metrics.edge_size), - horizontal_size); - let left_rect = Rect::new(metrics.tl_outer + Point2D::new(0.0, metrics.edge_size), - vertical_size); - - // Prevent overlap of the box shadow edges when the size of the blur is larger than the - // size of the box. - let center = Point2D::new(rect.origin.x + rect.size.width / 2.0, - rect.origin.y + rect.size.height / 2.0); - - self.add_box_shadow_edge(&top_rect.origin, - &top_rect.bottom_right(), - &top_rect.origin, - &Point2D::new(metrics.tr_inner.x, center.y), - &rect, - color, - blur_radius, - border_radius, - clip_mode, - resource_cache, - frame_id, - BasicRotationAngle::Clockwise90); - self.add_box_shadow_edge(&right_rect.origin, - &right_rect.bottom_right(), - &Point2D::new(center.x, metrics.tr_inner.y), - &right_rect.bottom_right(), - &rect, - color, - blur_radius, - border_radius, - clip_mode, - resource_cache, - frame_id, - BasicRotationAngle::Clockwise180); - self.add_box_shadow_edge(&bottom_rect.origin, - &bottom_rect.bottom_right(), - &Point2D::new(metrics.bl_inner.x, center.y), - &bottom_rect.bottom_right(), - &rect, - color, - blur_radius, - border_radius, - clip_mode, - resource_cache, - frame_id, - BasicRotationAngle::Clockwise270); - self.add_box_shadow_edge(&left_rect.origin, - &left_rect.bottom_right(), - &left_rect.origin, - &Point2D::new(center.x, metrics.bl_inner.y), - &rect, - color, - blur_radius, - border_radius, - clip_mode, - resource_cache, - frame_id, - BasicRotationAngle::Upright); - - self.undo_clip_state(clip_state); - } - - fn fill_outside_area_of_inset_box_shadow(&mut self, - box_bounds: &Rect, - box_offset: &Point2D, - color: &ColorF, - blur_radius: f32, - spread_radius: f32, - border_radius: f32, - resource_cache: &ResourceCache, - frame_id: FrameId) { - let rect = compute_box_shadow_rect(box_bounds, box_offset, spread_radius, BoxShadowClipMode::Inset); - let metrics = BoxShadowMetrics::new(&rect, border_radius, blur_radius); - - let clip_state = self.adjust_clip_for_box_shadow_clip_mode(box_bounds, - border_radius, - BoxShadowClipMode::Inset); - - // Fill in the outside area of the box. - // - // +------------------------------+ - // A --> |##############################| - // +--+--+------------------+--+--+ - // |##| | | |##| - // |##+--+------------------+--+##| - // |##| | | |##| - // D --> |##| | | |##| <-- B - // |##| | | |##| - // |##+--+------------------+--+##| - // |##| | | |##| - // +--+--+------------------+--+--+ - // C --> |##############################| - // +------------------------------+ - - // A: - self.add_color_rectangle(&Rect::new(box_bounds.origin, - Size2D::new(box_bounds.size.width, - metrics.tl_outer.y - box_bounds.origin.y)), - color, - resource_cache, - frame_id); - - // B: - self.add_color_rectangle(&Rect::new(metrics.tr_outer, - Size2D::new(box_bounds.max_x() - metrics.tr_outer.x, - metrics.br_outer.y - metrics.tr_outer.y)), - color, - resource_cache, - frame_id); - - // C: - self.add_color_rectangle(&Rect::new(Point2D::new(box_bounds.origin.x, metrics.bl_outer.y), - Size2D::new(box_bounds.size.width, - box_bounds.max_y() - metrics.br_outer.y)), - color, - resource_cache, - frame_id); - - // D: - self.add_color_rectangle(&Rect::new(Point2D::new(box_bounds.origin.x, metrics.tl_outer.y), - Size2D::new(metrics.tl_outer.x - box_bounds.origin.x, - metrics.bl_outer.y - metrics.tl_outer.y)), - color, - resource_cache, - frame_id); - - self.undo_clip_state(clip_state); - } - - fn undo_clip_state(&mut self, clip_state: ClipState) { - match clip_state { - ClipState::None => {} - ClipState::ClipIn => { - self.pop_clip_in_rect(); - } - ClipState::ClipOut(old_rect) => { - self.set_clip_out_rect(old_rect); - } - } - } - - fn adjust_clip_for_box_shadow_clip_mode(&mut self, - box_bounds: &Rect, - _border_radius: f32, - clip_mode: BoxShadowClipMode) -> ClipState { - //debug_assert!(border_radius == 0.0); // TODO(gw): !!! - - match clip_mode { - BoxShadowClipMode::None => { - ClipState::None - } - BoxShadowClipMode::Inset => { - self.push_clip_in_rect(box_bounds); - ClipState::ClipIn - } - BoxShadowClipMode::Outset => { - let old_clip_out_rect = self.set_clip_out_rect(Some(*box_bounds)); - ClipState::ClipOut(old_clip_out_rect) - } - } - } - - #[inline] - fn add_border_edge(&mut self, - rect: &Rect, - side: RectSide, - color: &ColorF, - border_style: BorderStyle, - resource_cache: &ResourceCache, - frame_id: FrameId) { - if color.a <= 0.0 { - return - } - if rect.size.width <= 0.0 || rect.size.height <= 0.0 { - return - } - - let dummy_mask_image = resource_cache.get_dummy_mask_image(); - let colors = [*color, *color, *color, *color]; - - match border_style { - BorderStyle::Dashed => { - let (extent, step) = match side { - RectSide::Top | RectSide::Bottom => { - (rect.size.width, rect.size.height * BORDER_DASH_SIZE) - } - RectSide::Left | RectSide::Right => { - (rect.size.height, rect.size.width * BORDER_DASH_SIZE) - } - }; - let mut origin = 0.0; - while origin < extent { - let dash_rect = match side { - RectSide::Top | RectSide::Bottom => { - Rect::new(Point2D::new(rect.origin.x + origin, rect.origin.y), - Size2D::new(f32::min(step, extent - origin), - rect.size.height)) - } - RectSide::Left | RectSide::Right => { - Rect::new(Point2D::new(rect.origin.x, rect.origin.y + origin), - Size2D::new(rect.size.width, - f32::min(step, extent - origin))) - } - }; - - self.add_color_rectangle(&dash_rect, color, resource_cache, frame_id); - - origin += step + step; - } - } - BorderStyle::Dotted => { - let (extent, step) = match side { - RectSide::Top | RectSide::Bottom => (rect.size.width, rect.size.height), - RectSide::Left | RectSide::Right => (rect.size.height, rect.size.width), - }; - let mut origin = 0.0; - while origin < extent { - let (dot_rect, mask_radius) = match side { - RectSide::Top | RectSide::Bottom => { - (Rect::new(Point2D::new(rect.origin.x + origin, rect.origin.y), - Size2D::new(f32::min(step, extent - origin), - rect.size.height)), - rect.size.height / 2.0) - } - RectSide::Left | RectSide::Right => { - (Rect::new(Point2D::new(rect.origin.x, rect.origin.y + origin), - Size2D::new(rect.size.width, - f32::min(step, extent - origin))), - rect.size.width / 2.0) - } - }; - - let mask_radius = DevicePixel::new(mask_radius, self.device_pixel_ratio); - - let raster_op = - BorderRadiusRasterOp::create(mask_radius, - mask_radius, - DevicePixel::zero(), - DevicePixel::zero(), - false, - None, - ImageFormat::RGBA8).expect( - "Didn't find border radius mask for dashed border!"); - let raster_item = RasterItem::BorderRadius(raster_op); - let color_image = resource_cache.get_raster(&raster_item, frame_id); - - // Top left: - self.add_simple_rectangle(color_image.texture_id, - &Rect::new(dot_rect.origin, - Size2D::new(dot_rect.size.width / 2.0, - dot_rect.size.height / 2.0)), - &color_image.uv_rect(), - dummy_mask_image.texture_id, - &dummy_mask_image.pixel_rect, - &colors, - None); - - // Top right: - self.add_simple_rectangle(color_image.texture_id, - &Rect::new(dot_rect.top_right(), - Size2D::new(-dot_rect.size.width / 2.0, - dot_rect.size.height / 2.0)), - &color_image.uv_rect(), - dummy_mask_image.texture_id, - &dummy_mask_image.pixel_rect, - &colors, - None); - - // Bottom right: - self.add_simple_rectangle(color_image.texture_id, - &Rect::new(dot_rect.bottom_right(), - Size2D::new(-dot_rect.size.width / 2.0, - -dot_rect.size.height / 2.0)), - &color_image.uv_rect(), - dummy_mask_image.texture_id, - &dummy_mask_image.pixel_rect, - &colors, - None); - - // Bottom left: - self.add_simple_rectangle(color_image.texture_id, - &Rect::new(dot_rect.bottom_left(), - Size2D::new(dot_rect.size.width / 2.0, - -dot_rect.size.height / 2.0)), - &color_image.uv_rect(), - dummy_mask_image.texture_id, - &dummy_mask_image.pixel_rect, - &colors, - None); - - origin += step + step; - } - } - BorderStyle::Double => { - let (outer_rect, inner_rect) = match side { - RectSide::Top | RectSide::Bottom => { - (Rect::new(rect.origin, - Size2D::new(rect.size.width, rect.size.height / 3.0)), - Rect::new(Point2D::new(rect.origin.x, - rect.origin.y + rect.size.height * 2.0 / 3.0), - Size2D::new(rect.size.width, rect.size.height / 3.0))) - } - RectSide::Left | RectSide::Right => { - (Rect::new(rect.origin, - Size2D::new(rect.size.width / 3.0, rect.size.height)), - Rect::new(Point2D::new(rect.origin.x + rect.size.width * 2.0 / 3.0, - rect.origin.y), - Size2D::new(rect.size.width / 3.0, rect.size.height))) - } - }; - self.add_color_rectangle(&outer_rect, color, resource_cache, frame_id); - self.add_color_rectangle(&inner_rect, color, resource_cache, frame_id); - } - BorderStyle::Groove | BorderStyle::Ridge => { - let (tl_rect, br_rect) = match side { - RectSide::Top | RectSide::Bottom => { - (Rect::new(rect.origin, - Size2D::new(rect.size.width, rect.size.height / 2.0)), - Rect::new(Point2D::new(rect.origin.x, - rect.origin.y + rect.size.height / 2.0), - Size2D::new(rect.size.width, rect.size.height / 2.0))) - } - RectSide::Left | RectSide::Right => { - (Rect::new(rect.origin, - Size2D::new(rect.size.width / 2.0, rect.size.height)), - Rect::new(Point2D::new(rect.origin.x + rect.size.width / 2.0, - rect.origin.y), - Size2D::new(rect.size.width / 2.0, rect.size.height))) - } - }; - let (tl_color, br_color) = groove_ridge_border_colors(color, border_style); - self.add_color_rectangle(&tl_rect, &tl_color, resource_cache, frame_id); - self.add_color_rectangle(&br_rect, &br_color, resource_cache, frame_id); - } - _ => { - self.add_color_rectangle(rect, color, resource_cache, frame_id); - } - } - } - - /// Draws a border corner. - /// - /// The following diagram attempts to explain the parameters to this function. It's an enlarged - /// version of a border corner that looks like this: - /// - /// ```notrust - /// ╭─ - /// │ - /// ``` - /// - /// The parameters are as follows: - /// - /// ```notrust - /// ⤹ corner_bounds.origin - /// ∙┈┈┈┈┈┬┈┈┈┈┈┬┈┈┈┈┈ - /// ┊ ╱ ┊ ┊ - /// ┊ ╱ ┊ ┊ - /// ┊ ╱╲ ┊ ←─┼── color1 - /// ┊╱ ╲ ┊ ┊ - /// ┊ ╲┊ ┊ - /// ├┈┈┈┈┈∙←────┼── radius_extent - /// ┊ ┊╲ ┊ - /// ┊ ┊ ╲ ┊ - /// ┊ ┊ ╲ ┊ - /// ┊ ↑ ┊ ╲ ┊ - /// ┊ │ ┊ ╲┊ - /// ├┈┈┼┈┈┴┈┈┈┈┈∙┈┈┈┈┈ - /// ┊ │ ┊↖︎ - /// ┊ ┊ corner_bounds.bottom_right() - /// ┊color0 ┊ - /// ``` - fn add_border_corner(&mut self, - border_style: BorderStyle, - corner_bounds: &Rect, - radius_extent: &Point2D, - color0: &ColorF, - color1: &ColorF, - outer_radius: &Size2D, - inner_radius: &Size2D, - resource_cache: &ResourceCache, - frame_id: FrameId, - can_tessellate: bool, - rotation_angle: BasicRotationAngle, - device_pixel_ratio: f32) { - if color0.a <= 0.0 && color1.a <= 0.0 { - return - } - - match border_style { - BorderStyle::Ridge | BorderStyle::Groove => { - let corner_center = util::rect_center(corner_bounds); - let (outer_corner_rect, inner_corner_rect, color1_rect, color0_rect) = - subdivide_border_corner(corner_bounds, &corner_center, rotation_angle); - - let (tl_color, br_color) = groove_ridge_border_colors(color0, border_style); - let (color0_outer, color1_outer, color0_inner, color1_inner) = - match rotation_angle { - BasicRotationAngle::Upright => { - (&tl_color, &tl_color, &br_color, &br_color) - } - BasicRotationAngle::Clockwise90 => { - (&br_color, &tl_color, &tl_color, &br_color) - } - BasicRotationAngle::Clockwise180 => { - (&br_color, &br_color, &tl_color, &tl_color) - } - BasicRotationAngle::Clockwise270 => { - (&tl_color, &br_color, &br_color, &tl_color) - } - }; - - // Draw the corner parts: - self.add_solid_border_corner(&outer_corner_rect, - radius_extent, - &color0_outer, - &color1_outer, - outer_radius, - inner_radius, - resource_cache, - frame_id, - can_tessellate, - rotation_angle, - device_pixel_ratio); - self.add_solid_border_corner(&inner_corner_rect, - radius_extent, - &color0_inner, - &color1_inner, - outer_radius, - inner_radius, - resource_cache, - frame_id, - can_tessellate, - rotation_angle, - device_pixel_ratio); - - // Draw the solid parts: - if util::rect_is_well_formed_and_nonempty(&color0_rect) { - self.add_color_rectangle(&color0_rect, &color0_outer, resource_cache, frame_id) - } - if util::rect_is_well_formed_and_nonempty(&color1_rect) { - self.add_color_rectangle(&color1_rect, &color1_outer, resource_cache, frame_id) - } - } - BorderStyle::Double => { - // ∙┈┈┈┈┈∙┈┈┈┈┈∙┈┈┈┈┈┐ - // ┊0 ┊1 ┊2 ┊ - // ┊ ┊ ┊ ┊ - // ┊ ┊ ┊ ┊ - // ∙┈┈┈┈┈∙┈┈┈┈┈∙┈┈┈┈┈┤ - // ┊3 ┊4 ┊5 ┊ - // ┊ ┊ ┊ ┊ - // ┊ ┊ ┊ ┊ - // ∙┈┈┈┈┈∙┈┈┈┈┈∙┈┈┈┈┈┤ - // ┊6 ┊7 ┊8 ┊ - // ┊ ┊ ┊ ┊ - // ┊ ┊ ┊ ┊ - // └┈┈┈┈┈┴┈┈┈┈┈┴┈┈┈┈┈┘ - - let width_1_3 = corner_bounds.size.width / 3.0; - let height_1_3 = corner_bounds.size.height / 3.0; - let width_2_3 = width_1_3 * 2.0; - let height_2_3 = height_1_3 * 2.0; - let size_1_3 = Size2D::new(width_1_3, height_1_3); - let size_width_2_3_height_1_3 = Size2D::new(width_2_3, height_1_3); - let size_width_1_3_height_2_3 = Size2D::new(width_1_3, height_2_3); - - let p0 = corner_bounds.origin; - let p1 = Point2D::new(corner_bounds.origin.x + width_1_3, corner_bounds.origin.y); - let p2 = Point2D::new(corner_bounds.origin.x + width_2_3, corner_bounds.origin.y); - let p3 = Point2D::new(corner_bounds.origin.x, corner_bounds.origin.y + height_1_3); - let p5 = Point2D::new(corner_bounds.origin.x + width_2_3, - corner_bounds.origin.y + height_1_3); - let p6 = Point2D::new(corner_bounds.origin.x, corner_bounds.origin.y + height_2_3); - let p7 = Point2D::new(corner_bounds.origin.x + width_1_3, - corner_bounds.origin.y + height_2_3); - let p8 = Point2D::new(corner_bounds.origin.x + width_2_3, - corner_bounds.origin.y + height_2_3); - - let outer_corner_rect; - let inner_corner_rect; - let outer_side_rect_0; - let outer_side_rect_1; - match rotation_angle { - BasicRotationAngle::Upright => { - outer_corner_rect = Rect::new(p0, size_1_3); - outer_side_rect_1 = Rect::new(p1, size_width_2_3_height_1_3); - inner_corner_rect = Rect::new(p8, size_1_3); - outer_side_rect_0 = Rect::new(p3, size_width_1_3_height_2_3) - } - BasicRotationAngle::Clockwise90 => { - outer_corner_rect = Rect::new(p2, size_1_3); - outer_side_rect_1 = Rect::new(p5, size_width_1_3_height_2_3); - inner_corner_rect = Rect::new(p6, size_1_3); - outer_side_rect_0 = Rect::new(p0, size_width_2_3_height_1_3) - } - BasicRotationAngle::Clockwise180 => { - outer_corner_rect = Rect::new(p8, size_1_3); - outer_side_rect_1 = Rect::new(p6, size_width_2_3_height_1_3); - inner_corner_rect = Rect::new(p0, size_1_3); - outer_side_rect_0 = Rect::new(p2, size_width_1_3_height_2_3) - } - BasicRotationAngle::Clockwise270 => { - outer_corner_rect = Rect::new(p6, size_1_3); - outer_side_rect_1 = Rect::new(p0, size_width_1_3_height_2_3); - inner_corner_rect = Rect::new(p2, size_1_3); - outer_side_rect_0 = Rect::new(p7, size_width_2_3_height_1_3) - } - } - - self.add_solid_border_corner(&outer_corner_rect, - radius_extent, - color0, - color1, - outer_radius, - &Size2D::new(0.0, 0.0), - resource_cache, - frame_id, - can_tessellate, - rotation_angle, - device_pixel_ratio); - - self.add_color_rectangle(&outer_side_rect_1, &color0, resource_cache, frame_id); - - self.add_solid_border_corner(&inner_corner_rect, - radius_extent, - color0, - color1, - &Size2D::new(0.0, 0.0), - inner_radius, - resource_cache, - frame_id, - can_tessellate, - rotation_angle, - device_pixel_ratio); - - self.add_color_rectangle(&outer_side_rect_0, &color1, resource_cache, frame_id); - } - _ => { - self.add_solid_border_corner(corner_bounds, - radius_extent, - color0, - color1, - outer_radius, - inner_radius, - resource_cache, - frame_id, - can_tessellate, - rotation_angle, - device_pixel_ratio) - } - } - } - - fn add_solid_border_corner(&mut self, - corner_bounds: &Rect, - radius_extent: &Point2D, - color0: &ColorF, - color1: &ColorF, - outer_radius: &Size2D, - inner_radius: &Size2D, - resource_cache: &ResourceCache, - frame_id: FrameId, - can_tessellate: bool, - rotation_angle: BasicRotationAngle, - _device_pixel_ratio: f32) { - // TODO: Check for zero width/height borders! - // FIXME(pcwalton): It's kind of messy to be matching on the rotation angle here to pick - // the right rect to draw the rounded corner in. Is there a more elegant way to do this? - let (outer_corner_rect, inner_corner_rect, color0_rect, color1_rect) = - subdivide_border_corner(corner_bounds, radius_extent, rotation_angle); - - let dummy_mask_image = resource_cache.get_dummy_mask_image(); - - // Draw the rounded part of the corner. - let quad_count = if can_tessellate { - tessellator::quad_count_for_border_corner(outer_radius, self.device_pixel_ratio) - } else { - 1 - }; - for rect_index in 0..quad_count { - let tessellated_rect; - let index; - if can_tessellate { - tessellated_rect = - outer_corner_rect.tessellate_border_corner(outer_radius, - inner_radius, - self.device_pixel_ratio, - rotation_angle, - rect_index); - index = Some(rect_index) - } else { - tessellated_rect = outer_corner_rect; - index = None - }; - - let outer_radius_x = DevicePixel::new(outer_radius.width, self.device_pixel_ratio); - let outer_radius_y = DevicePixel::new(outer_radius.height, self.device_pixel_ratio); - let inner_radius_x = DevicePixel::new(inner_radius.width, self.device_pixel_ratio); - let inner_radius_y = DevicePixel::new(inner_radius.height, self.device_pixel_ratio); - - let mask_image = match BorderRadiusRasterOp::create(outer_radius_x, - outer_radius_y, - inner_radius_x, - inner_radius_y, - false, - index, - ImageFormat::A8) { - Some(raster_item) => { - resource_cache.get_raster(&RasterItem::BorderRadius(raster_item), frame_id) - } - None => dummy_mask_image, - }; - - // FIXME(pcwalton): Either use RGBA8 textures instead of alpha masks here, or implement - // a mask combiner. - let mask_uv = RectUv::from_uv_rect_rotation_angle(&mask_image.pixel_rect, - rotation_angle, - true); - - let tessellated_rect = RectPolygon { - pos: tessellated_rect, - varyings: mask_uv, - }; - - self.add_border_corner_piece(tessellated_rect, - mask_image, - color0, - color1, - resource_cache, - rotation_angle); - } - - // Draw the inner rect. - self.add_border_corner_piece(RectPolygon { - pos: inner_corner_rect, - varyings: RectUv::zero(), - }, - dummy_mask_image, - color0, - color1, - resource_cache, - rotation_angle); - - // Draw the two solid rects. - if util::rect_is_well_formed_and_nonempty(&color0_rect) { - self.add_color_rectangle(&color0_rect, color0, resource_cache, frame_id) - } - if util::rect_is_well_formed_and_nonempty(&color1_rect) { - self.add_color_rectangle(&color1_rect, color1, resource_cache, frame_id) - } - } - - /// Draws one rectangle making up a border corner. - fn add_border_corner_piece(&mut self, - rect_pos_uv: RectPolygon>, - mask_image: &TextureCacheItem, - color0: &ColorF, - color1: &ColorF, - resource_cache: &ResourceCache, - rotation_angle: BasicRotationAngle) { - if !rect_pos_uv.is_well_formed_and_nonempty() { - return - } - - let white_image = resource_cache.get_dummy_color_image(); - - let v0; - let v1; - let muv; - match rotation_angle { - BasicRotationAngle::Upright => { - v0 = rect_pos_uv.pos.origin; - v1 = rect_pos_uv.pos.bottom_right(); - muv = RectUv { - top_left: rect_pos_uv.varyings.top_left, - top_right: rect_pos_uv.varyings.top_right, - bottom_right: rect_pos_uv.varyings.bottom_right, - bottom_left: rect_pos_uv.varyings.bottom_left, - } - } - BasicRotationAngle::Clockwise90 => { - v0 = rect_pos_uv.pos.top_right(); - v1 = rect_pos_uv.pos.bottom_left(); - muv = RectUv { - top_left: rect_pos_uv.varyings.top_right, - top_right: rect_pos_uv.varyings.top_left, - bottom_right: rect_pos_uv.varyings.bottom_left, - bottom_left: rect_pos_uv.varyings.bottom_right, - } - } - BasicRotationAngle::Clockwise180 => { - v0 = rect_pos_uv.pos.bottom_right(); - v1 = rect_pos_uv.pos.origin; - muv = RectUv { - top_left: rect_pos_uv.varyings.bottom_right, - top_right: rect_pos_uv.varyings.bottom_left, - bottom_right: rect_pos_uv.varyings.top_left, - bottom_left: rect_pos_uv.varyings.top_right, - } - } - BasicRotationAngle::Clockwise270 => { - v0 = rect_pos_uv.pos.bottom_left(); - v1 = rect_pos_uv.pos.top_right(); - muv = RectUv { - top_left: rect_pos_uv.varyings.bottom_left, - top_right: rect_pos_uv.varyings.bottom_right, - bottom_right: rect_pos_uv.varyings.top_right, - bottom_left: rect_pos_uv.varyings.top_left, - } - } - } - - self.add_rectangle(white_image.texture_id, - mask_image.texture_id, - &Rect::new(v0, Size2D::new(v1.x - v0.x, v1.y - v0.y)), - &RectUv::zero(), - &muv, - &[*color1, *color1, *color0, *color0], - PackedVertexColorMode::BorderCorner, - None) - } - - fn add_color_image_rectangle(&mut self, - v0: &Point2D, - v1: &Point2D, - color0: &ColorF, - color1: &ColorF, - color_image: &TextureCacheItem, - resource_cache: &ResourceCache, - rotation_angle: BasicRotationAngle, - flip: bool) { - if color0.a <= 0.0 || color1.a <= 0.0 { - return - } - - let vertices_rect = Rect::new(*v0, Size2D::new(v1.x - v0.x, v1.y - v0.y)); - - let color_uv = RectUv::from_uv_rect_rotation_angle(&color_image.uv_rect(), - rotation_angle, - flip); - - let dummy_mask_image = resource_cache.get_dummy_mask_image(); - - self.add_simple_rectangle(color_image.texture_id, - &vertices_rect, - &color_uv, - dummy_mask_image.texture_id, - &dummy_mask_image.pixel_rect, - &[*color0, *color0, *color1, *color1], - None); - } - - pub fn add_border(&mut self, - rect: &Rect, - info: &BorderDisplayItem, - resource_cache: &ResourceCache, - frame_id: FrameId, - device_pixel_ratio: f32) { - // TODO: If any border segment is alpha, place all in alpha pass. - // Is it ever worth batching at a per-segment level? - let radius = &info.radius; - let left = &info.left; - let right = &info.right; - let top = &info.top; - let bottom = &info.bottom; - - let tl_outer = Point2D::new(rect.origin.x, rect.origin.y); - let tl_inner = tl_outer + Point2D::new(radius.top_left.width.max(left.width), - radius.top_left.height.max(top.width)); - - let tr_outer = Point2D::new(rect.origin.x + rect.size.width, rect.origin.y); - let tr_inner = tr_outer + Point2D::new(-radius.top_right.width.max(right.width), - radius.top_right.height.max(top.width)); - - let bl_outer = Point2D::new(rect.origin.x, rect.origin.y + rect.size.height); - let bl_inner = bl_outer + Point2D::new(radius.bottom_left.width.max(left.width), - -radius.bottom_left.height.max(bottom.width)); - - let br_outer = Point2D::new(rect.origin.x + rect.size.width, - rect.origin.y + rect.size.height); - let br_inner = br_outer - Point2D::new(radius.bottom_right.width.max(right.width), - radius.bottom_right.height.max(bottom.width)); - - let left_color = left.border_color(1.0, 2.0/3.0, 0.3, 0.7); - let top_color = top.border_color(1.0, 2.0/3.0, 0.3, 0.7); - let right_color = right.border_color(2.0/3.0, 1.0, 0.7, 0.3); - let bottom_color = bottom.border_color(2.0/3.0, 1.0, 0.7, 0.3); - - let can_tessellate = tessellator::can_tessellate_border(info); - - // Edges - self.add_border_edge(&Rect::new(Point2D::new(tl_outer.x, tl_inner.y), - Size2D::new(left.width, bl_inner.y - tl_inner.y)), - RectSide::Left, - &left_color, - info.left.style, - resource_cache, - frame_id); - - self.add_border_edge(&Rect::new(Point2D::new(tl_inner.x, tl_outer.y), - Size2D::new(tr_inner.x - tl_inner.x, - tr_outer.y + top.width - tl_outer.y)), - RectSide::Top, - &top_color, - info.top.style, - resource_cache, - frame_id); - - self.add_border_edge(&Rect::new(Point2D::new(br_outer.x - right.width, tr_inner.y), - Size2D::new(right.width, br_inner.y - tr_inner.y)), - RectSide::Right, - &right_color, - info.right.style, - resource_cache, - frame_id); - - self.add_border_edge(&Rect::new(Point2D::new(bl_inner.x, bl_outer.y - bottom.width), - Size2D::new(br_inner.x - bl_inner.x, - br_outer.y - bl_outer.y + bottom.width)), - RectSide::Bottom, - &bottom_color, - info.bottom.style, - resource_cache, - frame_id); - - // Corners - self.add_border_corner(info.left.style, - &Rect::new(tl_outer, - Size2D::new(tl_inner.x - tl_outer.x, - tl_inner.y - tl_outer.y)), - &Point2D::new(tl_outer.x + radius.top_left.width, - tl_outer.y + radius.top_left.height), - &left_color, - &top_color, - &radius.top_left, - &info.top_left_inner_radius(), - resource_cache, - frame_id, - can_tessellate, - BasicRotationAngle::Upright, - device_pixel_ratio); - - self.add_border_corner(info.top.style, - &Rect::new(Point2D::new(tr_inner.x, tr_outer.y), - Size2D::new(tr_outer.x - tr_inner.x, - tr_inner.y - tr_outer.y)), - &Point2D::new(tr_outer.x - radius.top_right.width, - tl_outer.y + radius.top_right.height), - &right_color, - &top_color, - &radius.top_right, - &info.top_right_inner_radius(), - resource_cache, - frame_id, - can_tessellate, - BasicRotationAngle::Clockwise90, - device_pixel_ratio); - - self.add_border_corner(info.right.style, - &Rect::new(br_inner, - Size2D::new(br_outer.x - br_inner.x, - br_outer.y - br_inner.y)), - &Point2D::new(br_outer.x - radius.bottom_right.width, - br_outer.y - radius.bottom_right.height), - &right_color, - &bottom_color, - &radius.bottom_right, - &info.bottom_right_inner_radius(), - resource_cache, - frame_id, - can_tessellate, - BasicRotationAngle::Clockwise180, - device_pixel_ratio); - - self.add_border_corner(info.bottom.style, - &Rect::new(Point2D::new(bl_outer.x, bl_inner.y), - Size2D::new(bl_inner.x - bl_outer.x, - bl_outer.y - bl_inner.y)), - &Point2D::new(bl_outer.x + radius.bottom_left.width, - bl_outer.y - radius.bottom_left.height), - &left_color, - &bottom_color, - &radius.bottom_left, - &info.bottom_left_inner_radius(), - resource_cache, - frame_id, - can_tessellate, - BasicRotationAngle::Clockwise270, - device_pixel_ratio); - } - - // FIXME(pcwalton): Assumes rectangles are well-formed with origin in TL - fn add_box_shadow_corner(&mut self, - top_left: &Point2D, - bottom_right: &Point2D, - corner_area_top_left: &Point2D, - corner_area_bottom_right: &Point2D, - box_rect: &Rect, - color: &ColorF, - blur_radius: f32, - border_radius: f32, - clip_mode: BoxShadowClipMode, - resource_cache: &ResourceCache, - frame_id: FrameId, - rotation_angle: BasicRotationAngle) { - let corner_area_rect = - Rect::new(*corner_area_top_left, - Size2D::new(corner_area_bottom_right.x - corner_area_top_left.x, - corner_area_bottom_right.y - corner_area_top_left.y)); - - self.push_clip_in_rect(&corner_area_rect); - - let inverted = match clip_mode { - BoxShadowClipMode::Outset | BoxShadowClipMode::None => false, - BoxShadowClipMode::Inset => true, - }; - - let color_image = match BoxShadowRasterOp::create_corner(blur_radius, - border_radius, - box_rect, - inverted, - self.device_pixel_ratio) { - Some(raster_item) => { - let raster_item = RasterItem::BoxShadow(raster_item); - resource_cache.get_raster(&raster_item, frame_id) - } - None => resource_cache.get_dummy_color_image(), - }; - - self.add_color_image_rectangle(top_left, - bottom_right, - color, - color, - &color_image, - resource_cache, - rotation_angle, - true); - - self.pop_clip_in_rect(); - } - - fn add_box_shadow_edge(&mut self, - top_left: &Point2D, - bottom_right: &Point2D, - edge_area_top_left: &Point2D, - edge_area_bottom_right: &Point2D, - box_rect: &Rect, - color: &ColorF, - blur_radius: f32, - border_radius: f32, - clip_mode: BoxShadowClipMode, - resource_cache: &ResourceCache, - frame_id: FrameId, - rotation_angle: BasicRotationAngle) { - - if top_left.x >= bottom_right.x || top_left.y >= bottom_right.y { - return - } - - let edge_area_rect = - Rect::new(*edge_area_top_left, - Size2D::new(edge_area_bottom_right.x - edge_area_top_left.x, - edge_area_bottom_right.y - edge_area_top_left.y)); - - self.push_clip_in_rect(&edge_area_rect); - let inverted = match clip_mode { - BoxShadowClipMode::Outset | BoxShadowClipMode::None => false, - BoxShadowClipMode::Inset => true, - }; - - let color_image = match BoxShadowRasterOp::create_edge(blur_radius, - border_radius, - box_rect, - inverted, - self.device_pixel_ratio) { - Some(raster_item) => { - let raster_item = RasterItem::BoxShadow(raster_item); - resource_cache.get_raster(&raster_item, frame_id) - } - None => resource_cache.get_dummy_color_image(), - }; - - self.add_color_image_rectangle(top_left, - bottom_right, - color, - color, - &color_image, - resource_cache, - rotation_angle, - false); - self.pop_clip_in_rect(); - } -} - -trait BorderSideHelpers { +pub trait BorderSideHelpers { fn border_color(&self, scale_factor_0: f32, scale_factor_1: f32, @@ -1858,14 +28,14 @@ impl BorderSideHelpers for BorderSide { if self.color.r != 0.0 || self.color.g != 0.0 || self.color.b != 0.0 { self.color.scale_rgb(scale_factor_1) } else { - ColorF::new(black_color_1, black_color_1, black_color_1, self.color.a) + ColorF::new(black_color_0, black_color_0, black_color_0, self.color.a) } } BorderStyle::Outset => { if self.color.r != 0.0 || self.color.g != 0.0 || self.color.b != 0.0 { self.color.scale_rgb(scale_factor_0) } else { - ColorF::new(black_color_0, black_color_0, black_color_0, self.color.a) + ColorF::new(black_color_1, black_color_1, black_color_1, self.color.a) } } _ => self.color, @@ -1874,21 +44,21 @@ impl BorderSideHelpers for BorderSide { } #[derive(Debug)] -struct BoxShadowMetrics { - edge_size: f32, - tl_outer: Point2D, - tl_inner: Point2D, - tr_outer: Point2D, - tr_inner: Point2D, - bl_outer: Point2D, - bl_inner: Point2D, - br_outer: Point2D, - br_inner: Point2D, +pub struct BoxShadowMetrics { + pub edge_size: f32, + pub tl_outer: Point2D, + pub tl_inner: Point2D, + pub tr_outer: Point2D, + pub tr_inner: Point2D, + pub bl_outer: Point2D, + pub bl_inner: Point2D, + pub br_outer: Point2D, + pub br_inner: Point2D, } impl BoxShadowMetrics { - fn new(box_bounds: &Rect, border_radius: f32, blur_radius: f32) -> BoxShadowMetrics { - let outside_edge_size = 3.0 * blur_radius; + pub fn new(box_bounds: &Rect, border_radius: f32, blur_radius: f32) -> BoxShadowMetrics { + let outside_edge_size = 2.0 * blur_radius; let inside_edge_size = outside_edge_size.max(border_radius); let edge_size = outside_edge_size + inside_edge_size; let inner_rect = box_bounds.inflate(-inside_edge_size, -inside_edge_size); @@ -1907,62 +77,3 @@ impl BoxShadowMetrics { } } } - -pub fn compute_box_shadow_rect(box_bounds: &Rect, - box_offset: &Point2D, - mut spread_radius: f32, - clip_mode: BoxShadowClipMode) - -> Rect { - let mut rect = (*box_bounds).clone(); - rect.origin.x += box_offset.x; - rect.origin.y += box_offset.y; - - if clip_mode == BoxShadowClipMode::Inset { - spread_radius = -spread_radius; - }; - - rect.inflate(spread_radius, spread_radius) -} - -/// Returns the top/left and bottom/right colors respectively. -fn groove_ridge_border_colors(color: &ColorF, border_style: BorderStyle) -> (ColorF, ColorF) { - match (color, border_style) { - (&ColorF { - r: 0.0, - g: 0.0, - b: 0.0, - a: _ - }, BorderStyle::Groove) => { - // Handle black specially (matching the new browser consensus here). - (ColorF::new(0.3, 0.3, 0.3, color.a), ColorF::new(0.7, 0.7, 0.7, color.a)) - } - (&ColorF { - r: 0.0, - g: 0.0, - b: 0.0, - a: _ - }, BorderStyle::Ridge) => { - // As above. - (ColorF::new(0.7, 0.7, 0.7, color.a), ColorF::new(0.3, 0.3, 0.3, color.a)) - } - (_, BorderStyle::Groove) => (util::scale_color(color, 1.0 / 3.0), *color), - (_, _) => (*color, util::scale_color(color, 2.0 / 3.0)), - } -} - -/// Subdivides the border corner into four quadrants and returns them in the order of outer corner, -/// inner corner, color 0 and color 1, respectively. See the diagram in the documentation for -/// `add_border_corner` for more information on what these values represent. -fn subdivide_border_corner(corner_bounds: &Rect, - point: &Point2D, - rotation_angle: BasicRotationAngle) - -> (Rect, Rect, Rect, Rect) { - let (tl, tr, br, bl) = util::subdivide_rect_into_quadrants(corner_bounds, point); - match rotation_angle { - BasicRotationAngle::Upright => (tl, br, bl, tr), - BasicRotationAngle::Clockwise90 => (tr, bl, tl, br), - BasicRotationAngle::Clockwise180 => (br, tl, tr, bl), - BasicRotationAngle::Clockwise270 => (bl, tr, br, tl), - } -} - diff --git a/src/debug_render.rs b/src/debug_render.rs index 50cf05f404..c3c3bce3c4 100644 --- a/src/debug_render.rs +++ b/src/debug_render.rs @@ -7,7 +7,7 @@ use device::{Device, ProgramId, VAOId, TextureId, VertexFormat}; use device::{TextureFilter, VertexUsageHint}; use euclid::{Matrix4D, Point2D, Size2D, Rect}; use gleam::gl; -use internal_types::{ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE}; +use internal_types::{ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE, DevicePixel}; use internal_types::{DebugFontVertex, DebugColorVertex, RenderTargetMode, PackedColor}; use std::f32; use webrender_traits::{ColorF, ImageFormat}; @@ -29,8 +29,8 @@ pub struct DebugRenderer { impl DebugRenderer { pub fn new(device: &mut Device) -> DebugRenderer { - let font_program_id = device.create_program("debug_font"); - let color_program_id = device.create_program("debug_color"); + let font_program_id = device.create_program("debug_font", "shared_other"); + let color_program_id = device.create_program("debug_color", "shared_other"); let font_vao = device.create_vao(VertexFormat::DebugFont, None); let line_vao = device.create_vao(VertexFormat::DebugColor, None); @@ -147,16 +147,16 @@ impl DebugRenderer { #[allow(dead_code)] pub fn add_line(&mut self, - x0: f32, - y0: f32, + x0: DevicePixel, + y0: DevicePixel, color0: &ColorF, - x1: f32, - y1: f32, + x1: DevicePixel, + y1: DevicePixel, color1: &ColorF) { let color0 = PackedColor::from_color(color0); let color1 = PackedColor::from_color(color1); - self.line_vertices.push(DebugColorVertex::new(x0, y0, color0)); - self.line_vertices.push(DebugColorVertex::new(x1, y1, color1)); + self.line_vertices.push(DebugColorVertex::new(x0.0 as f32, y0.0 as f32, color0)); + self.line_vertices.push(DebugColorVertex::new(x1.0 as f32, y1.0 as f32, color1)); } pub fn render(&mut self, diff --git a/src/device.rs b/src/device.rs index 570be098af..eb93dd3d94 100644 --- a/src/device.rs +++ b/src/device.rs @@ -5,7 +5,7 @@ use euclid::Matrix4D; use fnv::FnvHasher; use gleam::gl; -use internal_types::{PackedColor, PackedVertex, PackedVertexForQuad}; +use internal_types::{PackedVertex, PackedVertexForQuad}; use internal_types::{PackedVertexForTextureCacheUpdate, RenderTargetMode, TextureSampler}; use internal_types::{VertexAttribute, DebugFontVertex, DebugColorVertex}; //use notify::{self, Watcher}; @@ -32,7 +32,7 @@ const GL_FORMAT_BGRA: gl::GLuint = gl::BGRA; const GL_FORMAT_BGRA: gl::GLuint = gl::BGRA_EXT; #[cfg(target_os = "linux")] -const SHADER_VERSION: &'static str = "#version 130\n"; +const SHADER_VERSION: &'static str = "#version 150\n"; #[cfg(target_os = "macos")] const SHADER_VERSION: &'static str = "#version 150\n"; @@ -43,9 +43,9 @@ const SHADER_VERSION: &'static str = "#version 150\n"; #[cfg(target_os = "android")] const SHADER_VERSION: &'static str = "#version 300 es\n"; -static FRAGMENT_SHADER_PREAMBLE: &'static str = "gl3_common.fs.glsl"; -static VERTEX_SHADER_PREAMBLE: &'static str = "gl3_common.vs.glsl"; +static SHADER_PREAMBLE: &'static str = "shared.glsl"; +/* static QUAD_VERTICES: [PackedVertex; 6] = [ PackedVertex { x: 0.0, y: 0.0, @@ -108,6 +108,7 @@ static QUAD_VERTICES: [PackedVertex; 6] = [ tile_params_index: 0, }, ]; +*/ lazy_static! { pub static ref MAX_TEXTURE_SIZE: gl::GLint = { @@ -284,11 +285,6 @@ impl VertexFormat { } VertexFormat::Triangles => { gl::enable_vertex_attrib_array(VertexAttribute::Position as gl::GLuint); - gl::enable_vertex_attrib_array(VertexAttribute::ColorRectTL as gl::GLuint); - gl::enable_vertex_attrib_array(VertexAttribute::ColorTexCoordRectTop as - gl::GLuint); - gl::enable_vertex_attrib_array(VertexAttribute::MaskTexCoordRectTop as gl::GLuint); - gl::enable_vertex_attrib_array(VertexAttribute::Misc as gl::GLuint); self.set_divisors(0); @@ -300,30 +296,6 @@ impl VertexFormat { false, vertex_stride as gl::GLint, 0 + vertex_stride * offset); - gl::vertex_attrib_pointer(VertexAttribute::ColorRectTL as gl::GLuint, - 4, - gl::UNSIGNED_BYTE, - false, - vertex_stride as gl::GLint, - 8 + vertex_stride * offset); - gl::vertex_attrib_pointer(VertexAttribute::ColorTexCoordRectTop as gl::GLuint, - 2, - gl::FLOAT, - false, - vertex_stride as gl::GLint, - 12 + vertex_stride * offset); - gl::vertex_attrib_pointer(VertexAttribute::MaskTexCoordRectTop as gl::GLuint, - 2, - gl::UNSIGNED_SHORT, - false, - vertex_stride as gl::GLint, - 20 + vertex_stride * offset); - gl::vertex_attrib_pointer(VertexAttribute::Misc as gl::GLuint, - 4, - gl::UNSIGNED_BYTE, - false, - vertex_stride as gl::GLint, - 24 + vertex_stride * offset); } VertexFormat::RasterOp => { gl::enable_vertex_attrib_array(VertexAttribute::Position as gl::GLuint); @@ -415,7 +387,7 @@ impl VertexFormat { } impl TextureId { - fn bind(&self) { + pub fn bind(&self) { let TextureId(id) = *self; gl::bind_texture(gl::TEXTURE_2D, id); } @@ -478,6 +450,7 @@ struct Program { u_transform: gl::GLint, vs_path: PathBuf, fs_path: PathBuf, + prefix: Option, vs_id: Option, fs_id: Option, } @@ -545,6 +518,7 @@ impl Program { } false } else { + //println!("{}", gl::get_program_info_log(self.id)); true } } @@ -590,7 +564,7 @@ impl Drop for VAO { pub struct TextureId(pub gl::GLuint); // TODO: HACK: Should not be public! #[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)] -pub struct ProgramId(gl::GLuint); +pub struct ProgramId(pub gl::GLuint); #[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)] pub struct VAOId(gl::GLuint); @@ -606,23 +580,28 @@ struct IBOId(gl::GLuint); #[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))] pub struct GpuProfile { - active_query: usize, - gl_queries: Vec, - first_frame: bool, + next_query: usize, + qids: Vec, } #[cfg(target_os = "android")] pub struct GpuProfile; +const QUERY_COUNT: i32 = 4; + impl GpuProfile { #[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))] pub fn new() -> GpuProfile { - let queries = gl::gen_queries(4); + let queries = gl::gen_queries(QUERY_COUNT); + + for q in &queries { + gl::begin_query(gl::TIME_ELAPSED, *q); + gl::end_query(gl::TIME_ELAPSED); + } GpuProfile { - active_query: 0, - gl_queries: queries, - first_frame: true, + qids: queries, + next_query: 0, } } @@ -631,41 +610,29 @@ impl GpuProfile { GpuProfile } + #[cfg(any(target_os = "android", target_os = "gonk"))] + pub fn get(&mut self) -> u64 { + 0 + } + + #[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))] + pub fn get(&mut self) -> u64 { + let qi = self.next_query; + gl::get_query_object_ui64v(self.qids[qi], gl::QUERY_RESULT) + } + #[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))] pub fn begin(&mut self) { - let qid = self.gl_queries[self.active_query]; - gl::begin_query(gl::TIME_ELAPSED, qid); + gl::begin_query(gl::TIME_ELAPSED, self.qids[self.next_query]); } #[cfg(target_os = "android")] pub fn begin(&mut self) {} #[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))] - pub fn end(&mut self) -> u64 { + pub fn end(&mut self) { gl::end_query(gl::TIME_ELAPSED); - - // Wait until the previous results are available - let last_qid = if self.active_query == 0 { - (self.gl_queries.len() - 1) as u32 - } else { - (self.active_query - 1) as u32 - }; - - self.active_query += 1; - if self.active_query == self.gl_queries.len() { - self.active_query = 0 - } - - if self.first_frame { - self.first_frame = false; - return 0 - } - - if gl::get_query_object_iv(last_qid, gl::QUERY_RESULT_AVAILABLE) == 1 { - gl::get_query_object_ui64v(last_qid, gl::QUERY_RESULT) - } else { - 0 - } + self.next_query = (self.next_query + 1) % QUERY_COUNT as usize; } #[cfg(target_os = "android")] @@ -675,7 +642,7 @@ impl GpuProfile { #[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))] impl Drop for GpuProfile { fn drop(&mut self) { - gl::delete_queries(&self.gl_queries); + gl::delete_queries(&self.qids); } } @@ -776,6 +743,8 @@ pub struct Device { // device state bound_color_texture: TextureId, bound_mask_texture: TextureId, + bound_layer_textures: [TextureId; 8], + bound_cache_texture: TextureId, bound_program: ProgramId, bound_vao: VAOId, bound_fbo: FBOId, @@ -793,8 +762,7 @@ pub struct Device { vaos: HashMap>, // misc. - vertex_shader_preamble: String, - fragment_shader_preamble: String, + shader_preamble: String, //file_watcher: FileWatcherThread, // Used on android only @@ -809,17 +777,10 @@ impl Device { //let file_watcher = FileWatcherThread::new(file_changed_handler); let mut path = resource_path.clone(); - path.push(VERTEX_SHADER_PREAMBLE); + path.push(SHADER_PREAMBLE); let mut f = File::open(&path).unwrap(); - let mut vertex_shader_preamble = String::new(); - f.read_to_string(&mut vertex_shader_preamble).unwrap(); - //file_watcher.add_watch(path); - - let mut path = resource_path.clone(); - path.push(FRAGMENT_SHADER_PREAMBLE); - let mut f = File::open(&path).unwrap(); - let mut fragment_shader_preamble = String::new(); - f.read_to_string(&mut fragment_shader_preamble).unwrap(); + let mut shader_preamble = String::new(); + f.read_to_string(&mut shader_preamble).unwrap(); //file_watcher.add_watch(path); Device { @@ -829,6 +790,16 @@ impl Device { bound_color_texture: TextureId(0), bound_mask_texture: TextureId(0), + bound_cache_texture: TextureId(0), + bound_layer_textures: [ TextureId(0), + TextureId(0), + TextureId(0), + TextureId(0), + TextureId(0), + TextureId(0), + TextureId(0), + TextureId(0), + ], bound_program: ProgramId(0), bound_vao: VAOId(0), bound_fbo: FBOId(0), @@ -839,8 +810,7 @@ impl Device { programs: HashMap::with_hasher(Default::default()), vaos: HashMap::with_hasher(Default::default()), - vertex_shader_preamble: vertex_shader_preamble, - fragment_shader_preamble: fragment_shader_preamble, + shader_preamble: shader_preamble, next_vao_id: 1, //file_watcher: file_watcher, @@ -849,7 +819,7 @@ impl Device { pub fn compile_shader(path: &PathBuf, shader_type: gl::GLenum, - shader_preamble: &str, + shader_preamble: &[String], panic_on_fail: bool) -> Option { debug!("compile {:?}", path); @@ -857,7 +827,9 @@ impl Device { let mut f = File::open(&path).unwrap(); let mut s = String::new(); s.push_str(SHADER_VERSION); - s.push_str(shader_preamble); + for prefix in shader_preamble { + s.push_str(&prefix); + } f.read_to_string(&mut s).unwrap(); let id = gl::create_shader(shader_type); @@ -873,6 +845,7 @@ impl Device { None } else { + //println!("{}", gl::get_shader_info_log(id)); Some(id) } } @@ -894,6 +867,16 @@ impl Device { gl::active_texture(gl::TEXTURE1); gl::bind_texture(gl::TEXTURE_2D, 0); + self.bound_cache_texture = TextureId(0); + gl::active_texture(gl::TEXTURE2); + gl::bind_texture(gl::TEXTURE_2D, 0); + + for i in 0..self.bound_layer_textures.len() { + self.bound_layer_textures[i] = TextureId(0); + gl::active_texture(gl::TEXTURE3 + i as u32); + gl::bind_texture(gl::TEXTURE_2D, 0); + } + // Shader state self.bound_program = ProgramId(0); gl::use_program(0); @@ -932,6 +915,28 @@ impl Device { } } + pub fn bind_cache_texture(&mut self, texture_id: TextureId) { + debug_assert!(self.inside_frame); + + if self.bound_cache_texture != texture_id { + self.bound_cache_texture = texture_id; + gl::active_texture(gl::TEXTURE2); + texture_id.bind(); + gl::active_texture(gl::TEXTURE0); + } + } + + pub fn bind_layer_texture(&mut self, layer: usize, texture_id: TextureId) { + debug_assert!(self.inside_frame); + + if self.bound_layer_textures[layer] != texture_id { + self.bound_layer_textures[layer] = texture_id; + gl::active_texture(gl::TEXTURE3 + layer as u32); + texture_id.bind(); + gl::active_texture(gl::TEXTURE0); + } + } + pub fn bind_render_target(&mut self, texture_id: Option) { debug_assert!(self.inside_frame); @@ -1028,7 +1033,7 @@ impl Device { gl::tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as gl::GLint); } - fn upload_2d_texture_image(&mut self, + fn upload_texture_image(&mut self, width: u32, height: u32, internal_format: u32, @@ -1044,15 +1049,6 @@ impl Device { pixels); } - fn upload_texture_image(&mut self, - width: u32, - height: u32, - internal_format: u32, - format: u32, - pixels: Option<&[u8]>) { - self.upload_2d_texture_image(width, height, internal_format, format, pixels) - } - fn deinit_texture_image(&mut self) { gl::tex_image_2d(gl::TEXTURE_2D, 0, @@ -1107,7 +1103,7 @@ impl Device { RenderTargetMode::RenderTarget => { self.bind_color_texture(texture_id); self.set_texture_parameters(filter); - self.upload_2d_texture_image(width, height, internal_format, gl_format, None); + self.upload_texture_image(width, height, internal_format, gl_format, None); self.create_fbo_for_texture_if_necessary(texture_id); } RenderTargetMode::None => { @@ -1208,28 +1204,16 @@ impl Device { texture.fbo_ids.clear(); } - pub fn init_texture_if_necessary(&mut self, - texture_id: TextureId, - width: u32, - height: u32, - format: ImageFormat, - filter: TextureFilter, - mode: RenderTargetMode) { - debug_assert!(self.inside_frame); - { - let texture = self.textures.get_mut(&texture_id).expect("Didn't find texture!"); - if texture.format == format && - texture.width == width && - texture.height == height && - texture.filter == filter && - texture.mode == mode { - return - } - } - self.init_texture(texture_id, width, height, format, filter, mode, None) + pub fn create_program(&mut self, + base_filename: &str, + include_filename: &str) -> ProgramId { + self.create_program_with_prefix(base_filename, include_filename, None) } - pub fn create_program(&mut self, base_filename: &str) -> ProgramId { + pub fn create_program_with_prefix(&mut self, + base_filename: &str, + include_filename: &str, + prefix: Option) -> ProgramId { debug_assert!(self.inside_frame); let pid = gl::create_program(); @@ -1242,11 +1226,26 @@ impl Device { fs_path.push(&format!("{}.fs.glsl", base_filename)); //self.file_watcher.add_watch(fs_path.clone()); + let mut include_path = self.resource_path.clone(); + include_path.push(&format!("{}.glsl", include_filename)); + let mut f = File::open(&include_path).unwrap(); + let mut include = String::new(); + f.read_to_string(&mut include).unwrap(); + + let mut shared_path = self.resource_path.clone(); + shared_path.push(&format!("{}.glsl", base_filename)); + if let Ok(mut f) = File::open(&shared_path) { + let mut shared_code = String::new(); + f.read_to_string(&mut shared_code).unwrap(); + include.push_str(&shared_code); + } + let program = Program { id: pid, u_transform: -1, vs_path: vs_path, fs_path: fs_path, + prefix: prefix, vs_id: None, fs_id: None, }; @@ -1256,26 +1255,44 @@ impl Device { debug_assert!(self.programs.contains_key(&program_id) == false); self.programs.insert(program_id, program); - self.load_program(program_id, true); + self.load_program(program_id, include, true); program_id } fn load_program(&mut self, program_id: ProgramId, + include: String, panic_on_fail: bool) { debug_assert!(self.inside_frame); let program = self.programs.get_mut(&program_id).unwrap(); + let mut vs_preamble = Vec::new(); + let mut fs_preamble = Vec::new(); + + vs_preamble.push("#define WR_VERTEX_SHADER\n".to_owned()); + fs_preamble.push("#define WR_FRAGMENT_SHADER\n".to_owned()); + + if let Some(ref prefix) = program.prefix { + vs_preamble.push(prefix.clone()); + fs_preamble.push(prefix.clone()); + } + + vs_preamble.push(self.shader_preamble.to_owned()); + fs_preamble.push(self.shader_preamble.to_owned()); + + vs_preamble.push(include.clone()); + fs_preamble.push(include); + // todo(gw): store shader ids so they can be freed! let vs_id = Device::compile_shader(&program.vs_path, gl::VERTEX_SHADER, - &*self.vertex_shader_preamble, + &vs_preamble, panic_on_fail); let fs_id = Device::compile_shader(&program.fs_path, gl::FRAGMENT_SHADER, - &*self.fragment_shader_preamble, + &fs_preamble, panic_on_fail); match (vs_id, fs_id) { @@ -1339,10 +1356,48 @@ impl Device { if u_device_pixel_ratio != -1 { gl::uniform_1f(u_device_pixel_ratio, self.device_pixel_ratio); } + + let u_layer0 = gl::get_uniform_location(program.id, "sLayer0"); + if u_layer0 != -1 { + gl::uniform_1i(u_layer0, TextureSampler::CompositeLayer0 as i32); + } + let u_layer1 = gl::get_uniform_location(program.id, "sLayer1"); + if u_layer1 != -1 { + gl::uniform_1i(u_layer1, TextureSampler::CompositeLayer1 as i32); + } + let u_layer2 = gl::get_uniform_location(program.id, "sLayer2"); + if u_layer2 != -1 { + gl::uniform_1i(u_layer2, TextureSampler::CompositeLayer2 as i32); + } + let u_layer3 = gl::get_uniform_location(program.id, "sLayer3"); + if u_layer3 != -1 { + gl::uniform_1i(u_layer3, TextureSampler::CompositeLayer3 as i32); + } + let u_layer4 = gl::get_uniform_location(program.id, "sLayer4"); + if u_layer4 != -1 { + gl::uniform_1i(u_layer4, TextureSampler::CompositeLayer4 as i32); + } + let u_layer5 = gl::get_uniform_location(program.id, "sLayer5"); + if u_layer5 != -1 { + gl::uniform_1i(u_layer5, TextureSampler::CompositeLayer5 as i32); + } + let u_layer6 = gl::get_uniform_location(program.id, "sLayer6"); + if u_layer6 != -1 { + gl::uniform_1i(u_layer6, TextureSampler::CompositeLayer6 as i32); + } + let u_layer7 = gl::get_uniform_location(program.id, "sLayer7"); + if u_layer7 != -1 { + gl::uniform_1i(u_layer7, TextureSampler::CompositeLayer7 as i32); + } + let u_cache = gl::get_uniform_location(program.id, "sCache"); + if u_cache != -1 { + gl::uniform_1i(u_cache, TextureSampler::Cache as i32); + } } } } +/* pub fn refresh_shader(&mut self, path: PathBuf) { let mut vs_preamble_path = self.resource_path.clone(); vs_preamble_path.push(VERTEX_SHADER_PREAMBLE); @@ -1377,67 +1432,34 @@ impl Device { for program_id in programs_to_update { self.load_program(program_id, false); } - } + }*/ pub fn get_uniform_location(&self, program_id: ProgramId, name: &str) -> UniformLocation { let ProgramId(program_id) = program_id; UniformLocation(gl::get_uniform_location(program_id, name)) } - pub fn set_uniform_2f(&self, uniform: UniformLocation, x: f32, y: f32) { +/* + pub fn set_uniform_1i(&self, uniform: UniformLocation, x: i32) { debug_assert!(self.inside_frame); let UniformLocation(location) = uniform; - gl::uniform_2f(location, x, y); - } - - pub fn set_uniform_4f(&self, - uniform: UniformLocation, - x: f32, - y: f32, - z: f32, - w: f32) { - debug_assert!(self.inside_frame); - let UniformLocation(location) = uniform; - gl::uniform_4f(location, x, y, z, w); + gl::uniform_1i(location, x); } +*/ - pub fn set_uniform_vec4_array(&self, - uniform: UniformLocation, - vectors: &[f32]) { + pub fn set_uniform_2f(&self, uniform: UniformLocation, x: f32, y: f32) { debug_assert!(self.inside_frame); let UniformLocation(location) = uniform; - gl::uniform_4fv(location, vectors); + gl::uniform_2f(location, x, y); } - pub fn set_uniform_mat4_array(&self, - uniform: UniformLocation, - matrices: &[Matrix4D]) { +/* + pub fn set_uniform_4f(&self, uniform: UniformLocation, x: f32, y: f32, z: f32, w: f32) { debug_assert!(self.inside_frame); let UniformLocation(location) = uniform; - - // TODO(gw): Avoid alloc here by storing as 3x3 matrices at a higher level... - let mut floats = Vec::new(); - for matrix in matrices { - floats.push(matrix.m11); - floats.push(matrix.m12); - floats.push(matrix.m13); - floats.push(matrix.m14); - floats.push(matrix.m21); - floats.push(matrix.m22); - floats.push(matrix.m23); - floats.push(matrix.m24); - floats.push(matrix.m31); - floats.push(matrix.m32); - floats.push(matrix.m33); - floats.push(matrix.m34); - floats.push(matrix.m41); - floats.push(matrix.m42); - floats.push(matrix.m43); - floats.push(matrix.m44); - } - - gl::uniform_matrix_4fv(location, false, &floats); + gl::uniform_4f(location, x, y, z, w); } +*/ fn set_uniforms(&self, program: &Program, transform: &Matrix4D) { debug_assert!(self.inside_frame); @@ -1589,28 +1611,6 @@ impl Device { self.create_vao_with_vbos(format, main_vbo_id, aux_vbo_id, ibo_id, 0, true) } - #[inline(never)] - pub fn create_similar_vao(&mut self, - format: VertexFormat, - source_vao_id: VAOId, - offset: gl::GLuint) - -> VAOId { - let &VAO { - main_vbo_id, - aux_vbo_id, - ibo_id, - .. - } = self.vaos.get(&source_vao_id).expect("Bad VAO ID in `create_similar_vao()`!"); - self.create_vao_with_vbos(format, main_vbo_id, aux_vbo_id, ibo_id, offset, false) - } - - pub fn create_quad_vertex_buffer(&mut self) -> VBOId { - let buffer_id = VBOId(gl::gen_buffers(1)[0]); - buffer_id.bind(); - gl::buffer_data(gl::ARRAY_BUFFER, &QUAD_VERTICES, gl::STATIC_DRAW); - buffer_id - } - pub fn update_vao_main_vertices(&mut self, vao_id: VAOId, vertices: &[V], @@ -1624,19 +1624,6 @@ impl Device { gl::buffer_data(gl::ARRAY_BUFFER, &vertices, usage_hint.to_gl()); } - pub fn update_vao_aux_vertices(&mut self, - vao_id: VAOId, - vertices: &[V], - usage_hint: VertexUsageHint) { - debug_assert!(self.inside_frame); - - let vao = self.vaos.get(&vao_id).unwrap(); - debug_assert!(self.bound_vao == vao_id); - - vao.aux_vbo_id.as_ref().unwrap().bind(); - gl::buffer_data(gl::ARRAY_BUFFER, &vertices, usage_hint.to_gl()); - } - pub fn update_vao_indices(&mut self, vao_id: VAOId, indices: &[I], @@ -1673,20 +1660,21 @@ impl Device { vertex_count); } - pub fn draw_triangles_instanced_u16(&mut self, - first_vertex: i32, - index_count: i32, - instance_count: i32) { + pub fn draw_indexed_triangles_instanced_u16(&mut self, + index_count: i32, + instance_count: i32) { debug_assert!(self.inside_frame); - gl::draw_arrays_instanced(gl::TRIANGLES, first_vertex as i32, index_count, instance_count); + gl::draw_elements_instanced(gl::TRIANGLES, index_count, gl::UNSIGNED_SHORT, 0, instance_count); } +/* pub fn delete_vao(&mut self, vao_id: VAOId) { self.vaos.remove(&vao_id).expect(&format!("unable to remove vao {:?}", vao_id)); if self.bound_vao == vao_id { self.bound_vao = VAOId(0); } } +*/ pub fn end_frame(&mut self) { self.bind_render_target(None); diff --git a/src/frame.rs b/src/frame.rs index 23bd6607c7..bd7f119e85 100644 --- a/src/frame.rs +++ b/src/frame.rs @@ -3,34 +3,24 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use app_units::Au; -use batch::{MAX_MATRICES_PER_BATCH, OffsetParams}; -use device::{TextureId}; use euclid::{Matrix4D, Point2D, Point3D, Point4D, Rect, Size2D}; use fnv::FnvHasher; use geometry::ray_intersects_rect; -use internal_types::{ANGLE_FLOAT_TO_FIXED, AxisDirection, BatchInfo, BatchUpdate}; -use internal_types::{BatchUpdateList, BatchUpdateOp, ChildLayerIndex, ClearInfo}; -use internal_types::{CompositeBatchInfo, CompositeBatchJob, CompositionOp}; -use internal_types::{DrawCall, DrawCommand, DrawCompositeBatchInfo}; -use internal_types::{DrawCompositeBatchJob, DrawLayer, DrawListGroupId}; -use internal_types::{DrawListId, DrawListItemIndex, LowLevelFilterOp, MAX_RECT}; -use internal_types::{MaskRegion, RenderTargetId, RendererFrame}; -use internal_types::{StackingContextInfo, StackingContextIndex}; +use internal_types::{ANGLE_FLOAT_TO_FIXED, AxisDirection}; +use internal_types::{CompositionOp}; +use internal_types::{LowLevelFilterOp, MAX_RECT}; +use internal_types::{RendererFrame}; use layer::{Layer, ScrollingState}; -use node_compiler::NodeCompiler; -use renderer::CompositionOpHelpers; use resource_cache::ResourceCache; -use resource_list::BuildRequiredResources; use scene::{SceneStackingContext, ScenePipeline, Scene, SceneItem, SpecificSceneItem}; use scoped_threadpool; use std::collections::{HashMap, HashSet}; use std::hash::BuildHasherDefault; -use std::mem; -use texture_cache::TexturePage; -use util::{self, MatrixHelpers}; +use tiling::{Clip, FrameBuilder, FrameBuilderConfig}; +use util::{MatrixHelpers}; use webrender_traits::{AuxiliaryLists, PipelineId, Epoch, ScrollPolicy, ScrollLayerId}; -use webrender_traits::{StackingContext, FilterOp, ImageFormat, MixBlendMode}; -use webrender_traits::{ScrollEventPhase, ScrollLayerInfo, ScrollLayerState}; +use webrender_traits::{StackingContext, FilterOp, MixBlendMode}; +use webrender_traits::{ScrollEventPhase, ScrollLayerInfo, SpecificDisplayItem, ScrollLayerState}; #[cfg(target_os = "macos")] const CAN_OVERSCROLL: bool = true; @@ -41,54 +31,11 @@ const CAN_OVERSCROLL: bool = false; #[derive(Copy, Clone, PartialEq, PartialOrd, Debug)] pub struct FrameId(pub u32); -pub struct DrawListGroup { - pub id: DrawListGroupId, - - // Together, these define the granularity that batches - // can be created at. When compiling nodes, if either - // the scroll layer or render target are different from - // the current batch, it must be broken and a new batch started. - // This automatically handles the case of CompositeBatch, because - // for a composite batch to be present, the next draw list must be - // in a different render target! - pub scroll_layer_id: ScrollLayerId, - pub render_target_id: RenderTargetId, - - pub draw_list_ids: Vec, -} - -impl DrawListGroup { - fn new(id: DrawListGroupId, - scroll_layer_id: ScrollLayerId, - render_target_id: RenderTargetId) -> DrawListGroup { - DrawListGroup { - id: id, - scroll_layer_id: scroll_layer_id, - render_target_id: render_target_id, - draw_list_ids: Vec::new(), - } - } - - fn can_add(&self, - scroll_layer_id: ScrollLayerId, - render_target_id: RenderTargetId) -> bool { - let scroll_ok = scroll_layer_id == self.scroll_layer_id; - let target_ok = render_target_id == self.render_target_id; - let size_ok = self.draw_list_ids.len() < MAX_MATRICES_PER_BATCH; - scroll_ok && target_ok && size_ok - } - - fn push(&mut self, draw_list_id: DrawListId) { - self.draw_list_ids.push(draw_list_id); - } -} - struct FlattenContext<'a> { resource_cache: &'a mut ResourceCache, scene: &'a Scene, pipeline_sizes: &'a mut HashMap>, - current_draw_list_group: Option, - device_pixel_ratio: f32, + builder: &'a mut FrameBuilder, } #[derive(Debug)] @@ -120,340 +67,6 @@ struct FlattenInfo { pipeline_id: PipelineId, } -#[derive(Debug)] -pub enum FrameRenderItem { - /// The extra boolean indicates whether a Z-buffer clear is needed. - CompositeBatch(CompositeBatchInfo, bool), - DrawListBatch(DrawListGroupId), -} - -pub struct RenderTarget { - id: RenderTargetId, - size: Size2D, - /// The origin in render target space. - origin: Point2D, - items: Vec, - texture_id: Option, - children: Vec, - - page_allocator: Option, - texture_id_list: Vec, -} - -impl RenderTarget { - fn new(id: RenderTargetId, - origin: Point2D, - size: Size2D, - texture_id: Option) -> RenderTarget { - RenderTarget { - id: id, - size: size, - origin: origin, - items: Vec::new(), - texture_id: texture_id, - children: Vec::new(), - texture_id_list: Vec::new(), - page_allocator: None, - } - } - - fn allocate_target_rect(&mut self, - width: f32, - height: f32, - device_pixel_ratio: f32, - resource_cache: &mut ResourceCache, - frame_id: FrameId) -> (Point2D, TextureId) { - // If the target is more than 512x512 (an arbitrary choice), assign it - // to an exact sized render target - assuming that there probably aren't - // many of them. This minimises GPU memory wastage if there are just a small - // number of large targets. Otherwise, attempt to allocate inside a shared render - // target texture - this allows composite batching to take place when - // there are a lot of small targets (which is more efficient). - if width < 512.0 && height < 512.0 { - if self.page_allocator.is_none() { - let texture_size = 2048; - let device_pixel_size = texture_size * device_pixel_ratio as u32; - - let texture_id = resource_cache.allocate_render_target(device_pixel_size, - device_pixel_size, - ImageFormat::RGBA8, - frame_id); - self.texture_id_list.push(texture_id); - self.page_allocator = Some(TexturePage::new(texture_id, texture_size)); - } - - // TODO(gw): This has accuracy issues if the size of a rendertarget is - // not scene pixel aligned! - let size = Size2D::new(width as u32, height as u32); - let allocated_origin = self.page_allocator - .as_mut() - .unwrap() - .allocate(&size); - if let Some(allocated_origin) = allocated_origin { - let origin = Point2D::new(allocated_origin.x as f32, - allocated_origin.y as f32); - return (origin, self.page_allocator.as_ref().unwrap().texture_id()) - } - } - - let device_pixel_width = width as u32 * device_pixel_ratio as u32; - let device_pixel_height = height as u32 * device_pixel_ratio as u32; - - let texture_id = resource_cache.allocate_render_target(device_pixel_width, - device_pixel_height, - ImageFormat::RGBA8, - frame_id); - self.texture_id_list.push(texture_id); - - (Point2D::zero(), texture_id) - } - - fn collect_and_sort_visible_batches(&mut self, - resource_cache: &mut ResourceCache, - draw_list_groups: &HashMap>, - layers: &HashMap>, - stacking_context_info: &[StackingContextInfo], - device_pixel_ratio: f32) -> DrawLayer { - let mut commands = vec![]; - for item in &self.items { - match item { - &FrameRenderItem::CompositeBatch(ref info, z_clear_needed) => { - let layer = &layers[&info.scroll_layer_id]; - let world_transform = layer.world_transform - .as_ref() - .expect("No world transform set!"); - - if z_clear_needed && !commands.is_empty() { - commands.push(DrawCommand::Clear(ClearInfo { - clear_color: false, - clear_z: true, - clear_stencil: true, - })) - } - - let mut draw_jobs = vec![]; - for job in &info.jobs { - draw_jobs.push(DrawCompositeBatchJob { - rect: job.rect, - local_transform: job.transform, - world_transform: job.transform.mul(world_transform), - child_layer_index: job.child_layer_index, - }) - } - - commands.push(DrawCommand::CompositeBatch(DrawCompositeBatchInfo { - operation: info.operation, - texture_id: info.texture_id, - scroll_layer_id: info.scroll_layer_id, - jobs: draw_jobs, - })) - } - &FrameRenderItem::DrawListBatch(draw_list_group_id) => { - let draw_list_group = &draw_list_groups[&draw_list_group_id]; - debug_assert!(draw_list_group.draw_list_ids.len() <= MAX_MATRICES_PER_BATCH); - - let layer = &layers[&draw_list_group.scroll_layer_id]; - let mut matrix_palette = - vec![Matrix4D::identity(); draw_list_group.draw_list_ids.len()]; - let mut offset_palette = - vec![OffsetParams::identity(); draw_list_group.draw_list_ids.len()]; - - // Update batch matrices - let mut z_clear_needed = false; - for (index, draw_list_id) in draw_list_group.draw_list_ids.iter().enumerate() { - let draw_list = resource_cache.get_draw_list(*draw_list_id); - - match draw_list.stacking_context_index { - Some(StackingContextIndex(stacking_context_id)) => { - let context = &stacking_context_info[stacking_context_id]; - if context.z_clear_needed { - z_clear_needed = true - } - - let mut world_transform_in_render_target_space = - layer.world_transform.expect("No world transform set!"); - if self.texture_id.is_some() { - // If we're rendering to a temporary target, the X/Y - // translation part of the transform will be applied by whoever - // composites us. - world_transform_in_render_target_space.m41 = 0.0; - world_transform_in_render_target_space.m42 = 0.0; - }; - let transform = - world_transform_in_render_target_space.mul(&context.transform); - matrix_palette[index] = transform; - - offset_palette[index].stacking_context_x0 = - context.offset_from_layer.x; - offset_palette[index].stacking_context_y0 = - context.offset_from_layer.y; - } - None => { - // This can happen if the root pipeline was set before any stacking - // context was set for it (during navigation, usually). In that - // case we just render nothing. - continue - } - } - } - - let mut batch_info = BatchInfo::new(matrix_palette, offset_palette); - - // Collect relevant draws from each node in the tree. - let mut any_were_visible = false; - for node in &layer.aabb_tree.nodes { - if node.is_visible { - any_were_visible = true; - - debug_assert!(node.compiled_node.is_some()); - let compiled_node = node.compiled_node.as_ref().unwrap(); - - let batch_list = compiled_node.batch_list.iter().find(|batch_list| { - batch_list.draw_list_group_id == draw_list_group_id - }); - - if let Some(batch_list) = batch_list { - let mut region = MaskRegion::new(); - - let vertex_buffer_id = compiled_node.vertex_buffer_id.unwrap(); - - // TODO(gw): Support mask regions for nested render targets - // with transforms. - if self.texture_id.is_none() { - // Mask out anything outside this AABB tree node. - // This is a requirement to ensure paint order is correctly - // maintained since the batches are built in parallel. - region.add_mask(node.split_rect, - layer.world_transform - .expect("No world transform set!")); - - // Mask out anything outside this viewport. This is used - // for things like clipping content that is outside a - // transformed iframe. - let mask_rect = layer.viewport_rect; - region.add_mask(mask_rect, layer.viewport_transform); - } - - for batch in &batch_list.batches { - region.draw_calls.push(DrawCall { - tile_params: batch.tile_params.clone(), // TODO(gw): Move this instead? - clip_rects: batch.clip_rects.clone(), - vertex_buffer_id: vertex_buffer_id, - color_texture_id: batch.color_texture_id, - mask_texture_id: batch.mask_texture_id, - first_instance: batch.first_instance, - instance_count: batch.instance_count, - }); - } - - batch_info.regions.push(region); - } - } - } - - if any_were_visible { - // Add a clear command if necessary. - if z_clear_needed && !commands.is_empty() { - commands.push(DrawCommand::Clear(ClearInfo { - clear_color: false, - clear_z: true, - clear_stencil: true, - })) - } - - // Finally, add the batch + draw calls - commands.push(DrawCommand::Batch(batch_info)); - } - } - } - } - - let mut child_layers = Vec::new(); - - for child in &mut self.children { - let child_layer = child.collect_and_sort_visible_batches(resource_cache, - draw_list_groups, - layers, - stacking_context_info, - device_pixel_ratio); - - child_layers.push(child_layer); - } - - DrawLayer::new(self.id, - self.origin, - self.size, - self.texture_id, - commands, - child_layers) - } - - fn reset(&mut self, - pending_updates: &mut BatchUpdateList, - resource_cache: &mut ResourceCache) { - self.texture_id_list.clear(); - resource_cache.free_old_render_targets(); - - for mut child in &mut self.children.drain(..) { - child.reset(pending_updates, - resource_cache); - } - - self.items.clear(); - self.page_allocator = None; - } - - fn push_composite(&mut self, - op: CompositionOp, - texture_id: TextureId, - target: Rect, - transform: &Matrix4D, - child_layer_index: ChildLayerIndex, - scroll_layer_id: ScrollLayerId, - z_clear_needed: bool) { - // TODO(gw): Relax the restriction on batch breaks for FB reads - // once the proper render target allocation code is done! - let need_new_batch = op.needs_framebuffer() || match self.items.last() { - Some(&FrameRenderItem::CompositeBatch(ref info, _)) => { - info.operation != op || info.texture_id != texture_id - } - Some(&FrameRenderItem::DrawListBatch(..)) | None => true, - }; - - if need_new_batch { - self.items.push(FrameRenderItem::CompositeBatch(CompositeBatchInfo { - operation: op, - texture_id: texture_id, - jobs: Vec::new(), - scroll_layer_id: scroll_layer_id, - }, z_clear_needed)); - } - - // TODO(gw): This seems a little messy - restructure how current batch works! - match self.items.last_mut().unwrap() { - &mut FrameRenderItem::CompositeBatch(ref mut batch, ref mut old_z_clear_needed) => { - let job = CompositeBatchJob { - rect: target, - transform: *transform, - child_layer_index: child_layer_index, - }; - batch.jobs.push(job); - - if !*old_z_clear_needed && z_clear_needed { - *old_z_clear_needed = true - } - } - _ => { - unreachable!(); - } - } - } - - fn push_draw_list_group(&mut self, draw_list_group_id: DrawListGroupId) { - self.items.push(FrameRenderItem::DrawListBatch(draw_list_group_id)); - } -} - pub type LayerMap = HashMap>; pub struct Frame { @@ -462,14 +75,11 @@ pub struct Frame { pub pipeline_auxiliary_lists: HashMap>, - pub pending_updates: BatchUpdateList, - pub root: Option, - pub stacking_context_info: Vec, - next_render_target_id: RenderTargetId, - next_draw_list_group_id: DrawListGroupId, - draw_list_groups: HashMap>, pub root_scroll_layer_id: Option, id: FrameId, + debug: bool, + frame_builder_config: FrameBuilderConfig, + frame_builder: Option, } enum SceneItemKind<'a> { @@ -597,37 +207,27 @@ impl StackingContextHelpers for StackingContext { } impl Frame { - pub fn new() -> Frame { + pub fn new(debug: bool, config: FrameBuilderConfig) -> Frame { Frame { pipeline_epoch_map: HashMap::with_hasher(Default::default()), - pending_updates: BatchUpdateList::new(), pipeline_auxiliary_lists: HashMap::with_hasher(Default::default()), - root: None, layers: HashMap::with_hasher(Default::default()), - stacking_context_info: Vec::new(), - next_render_target_id: RenderTargetId(0), - next_draw_list_group_id: DrawListGroupId(0), - draw_list_groups: HashMap::with_hasher(Default::default()), root_scroll_layer_id: None, id: FrameId(0), + debug: debug, + frame_builder: None, + frame_builder_config: config, } } - pub fn reset(&mut self, resource_cache: &mut ResourceCache) + pub fn reset(&mut self) -> HashMap> { - self.draw_list_groups.clear(); self.pipeline_epoch_map.clear(); - self.stacking_context_info.clear(); - - if let Some(mut root) = self.root.take() { - root.reset(&mut self.pending_updates, resource_cache); - } // Free any render targets from last frame. // TODO: This should really re-use existing targets here... let mut old_layer_scrolling_states = HashMap::with_hasher(Default::default()); - for (layer_id, mut old_layer) in &mut self.layers.drain() { - old_layer.reset(&mut self.pending_updates); + for (layer_id, old_layer) in &mut self.layers.drain() { old_layer_scrolling_states.insert(layer_id, old_layer.scrolling); } @@ -637,22 +237,6 @@ impl Frame { old_layer_scrolling_states } - fn next_render_target_id(&mut self) -> RenderTargetId { - let RenderTargetId(render_target_id) = self.next_render_target_id; - self.next_render_target_id = RenderTargetId(render_target_id + 1); - RenderTargetId(render_target_id) - } - - fn next_draw_list_group_id(&mut self) -> DrawListGroupId { - let DrawListGroupId(draw_list_group_id) = self.next_draw_list_group_id; - self.next_draw_list_group_id = DrawListGroupId(draw_list_group_id + 1); - DrawListGroupId(draw_list_group_id) - } - - pub fn pending_updates(&mut self) -> BatchUpdateList { - mem::replace(&mut self.pending_updates, BatchUpdateList::new()) - } - pub fn get_scroll_layer(&self, cursor: &Point2D, scroll_layer_id: ScrollLayerId) -> Option { self.layers.get(&scroll_layer_id).and_then(|layer| { @@ -804,7 +388,7 @@ impl Frame { device_pixel_ratio: f32) { if let Some(root_pipeline_id) = scene.root_pipeline_id { if let Some(root_pipeline) = scene.pipeline_map.get(&root_pipeline_id) { - let old_layer_scrolling_states = self.reset(resource_cache); + let old_layer_scrolling_states = self.reset(); self.pipeline_auxiliary_lists = scene.pipeline_auxiliary_lists.clone(); @@ -817,13 +401,6 @@ impl Frame { .expect("root layer must be a scroll layer!"); self.root_scroll_layer_id = Some(root_scroll_layer_id); - let root_target_id = self.next_render_target_id(); - - let mut root_target = RenderTarget::new(root_target_id, - Point2D::zero(), - root_pipeline.viewport_size, - None); - // Insert global position: fixed elements layer debug_assert!(self.layers.is_empty()); let root_fixed_layer_id = ScrollLayerId::create_fixed(root_pipeline_id); @@ -840,41 +417,41 @@ impl Frame { // Work around borrow check on resource cache { - let mut context = FlattenContext { - resource_cache: resource_cache, - scene: scene, - pipeline_sizes: pipeline_sizes, - current_draw_list_group: None, - device_pixel_ratio: device_pixel_ratio, - }; + let mut frame_builder = FrameBuilder::new(root_pipeline.viewport_size, + device_pixel_ratio, + self.debug, + self.frame_builder_config); + + { + let mut context = FlattenContext { + resource_cache: resource_cache, + scene: scene, + pipeline_sizes: pipeline_sizes, + builder: &mut frame_builder, + }; - let parent_info = FlattenInfo { - viewport_rect: Rect::new(Point2D::zero(), root_pipeline.viewport_size), - viewport_transform: Matrix4D::identity(), - offset_from_origin: Point2D::zero(), - offset_from_current_layer: Point2D::zero(), - default_scroll_layer_id: root_scroll_layer_id, - actual_scroll_layer_id: root_scroll_layer_id, - fixed_scroll_layer_id: root_fixed_layer_id, - current_clip_rect: MAX_RECT, - local_transform: Matrix4D::identity(), - local_perspective: Matrix4D::identity(), - world_transform: Matrix4D::identity(), - pipeline_id: root_pipeline_id, - }; + let parent_info = FlattenInfo { + viewport_rect: Rect::new(Point2D::zero(), root_pipeline.viewport_size), + viewport_transform: Matrix4D::identity(), + offset_from_origin: Point2D::zero(), + offset_from_current_layer: Point2D::zero(), + default_scroll_layer_id: root_scroll_layer_id, + actual_scroll_layer_id: root_scroll_layer_id, + fixed_scroll_layer_id: root_fixed_layer_id, + current_clip_rect: MAX_RECT, + local_transform: Matrix4D::identity(), + local_perspective: Matrix4D::identity(), + world_transform: Matrix4D::identity(), + pipeline_id: root_pipeline_id, + }; - let root_pipeline = SceneItemKind::Pipeline(root_pipeline); - self.flatten(root_pipeline, - &parent_info, - &mut context, - &mut root_target, - 0); - self.root = Some(root_target); - - if let Some(last_draw_list_group) = context.current_draw_list_group.take() { - self.draw_list_groups.insert(last_draw_list_group.id, - last_draw_list_group); + let root_pipeline = SceneItemKind::Pipeline(root_pipeline); + self.flatten(root_pipeline, + &parent_info, + &mut context); } + + self.frame_builder = Some(frame_builder); } // TODO(gw): These are all independent - can be run through thread pool if it shows up in the profile! @@ -893,65 +470,94 @@ impl Frame { fn add_items_to_target(&mut self, scene_items: &[SceneItem], info: &FlattenInfo, - target: &mut RenderTarget, context: &mut FlattenContext, - _level: i32, - z_clear_needed: bool) { - let stacking_context_index = StackingContextIndex(self.stacking_context_info.len()); - self.stacking_context_info.push(StackingContextInfo { - offset_from_layer: info.offset_from_current_layer, - local_clip_rect: info.current_clip_rect, - transform: info.world_transform, - z_clear_needed: z_clear_needed, - }); + sc_rect: Rect, + composition_ops: Vec) { + context.builder.push_layer(sc_rect, + info.current_clip_rect, + info.world_transform, + info.pipeline_id, + info.actual_scroll_layer_id, + info.offset_from_current_layer, + composition_ops); for item in scene_items { match item.specific { SpecificSceneItem::DrawList(draw_list_id) => { - let draw_list = context.resource_cache.get_draw_list_mut(draw_list_id); + let draw_list = context.resource_cache.get_draw_list(draw_list_id); + let builder = &mut context.builder; - // Store draw context - draw_list.stacking_context_index = Some(stacking_context_index); - - let needs_new_draw_group = match context.current_draw_list_group { - Some(ref draw_list_group) => { - !draw_list_group.can_add(info.actual_scroll_layer_id, - target.id) - } - None => { - true - } + let auxiliary_lists = { + self.pipeline_auxiliary_lists + .get(&info.pipeline_id) + .expect("No auxiliary lists?!") }; - if needs_new_draw_group { - if let Some(draw_list_group) = context.current_draw_list_group.take() { - self.draw_list_groups.insert(draw_list_group.id, - draw_list_group); - } - - let draw_list_group_id = self.next_draw_list_group_id(); - - let new_draw_list_group = DrawListGroup::new(draw_list_group_id, - info.actual_scroll_layer_id, - target.id); - - target.push_draw_list_group(draw_list_group_id); - - context.current_draw_list_group = Some(new_draw_list_group); - } + for item in &draw_list.items { + let clips = auxiliary_lists.complex_clip_regions(&item.clip.complex); + let clip = if clips.is_empty() { + None + } else { + Some(Box::new(Clip::from_clip_region(&clips[0]))) + }; - context.current_draw_list_group.as_mut().unwrap().push(draw_list_id); - - let draw_list_group_id = context.current_draw_list_group.as_ref().unwrap().id; - let layer = self.layers.get_mut(&info.actual_scroll_layer_id).unwrap(); - for (item_index, item) in draw_list.items.iter().enumerate() { - let item_index = DrawListItemIndex(item_index as u32); - let rect = item.rect - .translate(&info.offset_from_current_layer); - layer.insert(rect, - draw_list_group_id, - draw_list_id, - item_index); + match item.item { + SpecificDisplayItem::WebGL(ref info) => { + builder.add_webgl_rectangle(item.rect, + &item.clip.main, + clip, + info.context_id); + } + SpecificDisplayItem::Image(ref info) => { + builder.add_image(item.rect, + &item.clip.main, + clip, + &info.stretch_size, + info.image_key, + info.image_rendering); + } + SpecificDisplayItem::Text(ref text_info) => { + builder.add_text(item.rect, + &item.clip.main, + clip, + text_info.font_key, + text_info.size, + text_info.blur_radius, + &text_info.color, + text_info.glyphs); + } + SpecificDisplayItem::Rectangle(ref info) => { + builder.add_solid_rectangle(&item.rect, + &item.clip.main, + clip, + &info.color); + } + SpecificDisplayItem::Gradient(ref info) => { + builder.add_gradient(item.rect, + &item.clip.main, + clip, + info.start_point, + info.end_point, + info.stops); + } + SpecificDisplayItem::BoxShadow(ref box_shadow_info) => { + builder.add_box_shadow(&box_shadow_info.box_bounds, + &item.clip.main, + clip, + &box_shadow_info.offset, + &box_shadow_info.color, + box_shadow_info.blur_radius, + box_shadow_info.spread_radius, + box_shadow_info.border_radius, + box_shadow_info.clip_mode); + } + SpecificDisplayItem::Border(ref info) => { + builder.add_border(item.rect, + &item.clip.main, + clip, + info); + } + } } } SpecificSceneItem::StackingContext(id, pipeline_id) => { @@ -963,9 +569,7 @@ impl Frame { let child = SceneItemKind::StackingContext(stacking_context, pipeline_id); self.flatten(child, info, - context, - target, - _level+1); + context); } SpecificSceneItem::Iframe(ref iframe_info) => { let pipeline = context.scene @@ -1032,22 +636,20 @@ impl Frame { self.flatten(iframe, &iframe_info, - context, - target, - _level+1); + context); } } } } + + context.builder.pop_layer(); } fn flatten(&mut self, scene_item: SceneItemKind, parent_info: &FlattenInfo, - context: &mut FlattenContext, - target: &mut RenderTarget, - level: i32) { - let _pf = util::ProfileScope::new(" flatten"); + context: &mut FlattenContext) { + //let _pf = util::ProfileScope::new(" flatten"); let (stacking_context, pipeline_id) = match scene_item { SceneItemKind::StackingContext(stacking_context, pipeline_id) => { @@ -1065,6 +667,12 @@ impl Frame { } }; + // TODO(gw): This is broken for 3d transforms ref test. + // Removing it fixes that but it needs a proper solution before + // merging which will coming when the clipping branch merges! + + let local_clip_rect = Some(stacking_context.overflow); + /* // FIXME(pcwalton): This is a not-great partial solution to servo/servo#10164. A better // solution would be to do this only if the transform consists of a translation+scale only // and fall back to stenciling if the object has a more complex transform. @@ -1085,7 +693,7 @@ impl Frame { .translate(&-stacking_context.bounds.origin) .intersection(&stacking_context.overflow) } - }; + };*/ if let Some(local_clip_rect) = local_clip_rect { let scene_items = scene_item.collect_scene_items(&context.scene); @@ -1107,12 +715,16 @@ impl Frame { // Build local and world space transform let origin = parent_info.offset_from_current_layer + stacking_context.bounds.origin; - let mut local_transform = Matrix4D::identity(); - if composition_operations.is_empty() { - local_transform = local_transform.translate(origin.x, origin.y, 0.0) - .mul(&stacking_context.transform) - .translate(-origin.x, -origin.y, 0.0) - } + //let local_transform = if composition_operations.is_empty() { + // Matrix4D::identity().translate(origin.x, origin.y, 0.0) + // .mul(&stacking_context.transform) + // .translate(-origin.x, -origin.y, 0.0) + //} else { + // Matrix4D::identity() + //}; + let local_transform = Matrix4D::identity().translate(origin.x, origin.y, 0.0) + .mul(&stacking_context.transform) + .translate(-origin.x, -origin.y, 0.0); // Build local space perspective transform let local_perspective = Matrix4D::identity().translate(origin.x, origin.y, 0.0) @@ -1123,11 +735,14 @@ impl Frame { let world_transform = parent_info.world_transform.mul(&local_perspective) .mul(&local_transform); + /* let viewport_rect = if composition_operations.is_empty() { parent_info.viewport_rect } else { Rect::new(Point2D::zero(), parent_info.viewport_rect.size) }; + */ + let viewport_rect = parent_info.viewport_rect; let mut info = FlattenInfo { viewport_rect: viewport_rect, @@ -1197,119 +812,24 @@ impl Frame { } } - // When establishing a new 3D context, clear Z. This is only needed if there - // are child stacking contexts, otherwise it is a redundant clear. - let z_clear_needed = stacking_context.establishes_3d_context && - stacking_context.has_stacking_contexts; - // TODO: Account for scroll offset with transforms! - if composition_operations.is_empty() { - self.add_items_to_target(&scene_items, - &info, - target, - context, - level, - z_clear_needed); - } else { - // TODO(gw): This makes the reftests work (mix_blend_mode) and - // inline_stacking_context, but it seems wrong. - // Need to investigate those and see what the root - // issue is... - let empty_stacking_context = stacking_context.bounds.size.width == 0.0 || - stacking_context.bounds.size.height == 0.0; - let target_size = if empty_stacking_context { - stacking_context.overflow.size - } else { - stacking_context.bounds.size - }; - let target_origin = Point2D::new(info.offset_from_origin.x, - info.offset_from_origin.y); - let unfiltered_target_rect = Rect::new(target_origin, target_size); - let mut target_rect = unfiltered_target_rect; - for composition_operation in &composition_operations { - target_rect = composition_operation.target_rect(&target_rect); - } - - let child_layer_index = ChildLayerIndex(target.children.len() as u32); - - let render_target_size = Size2D::new(target_rect.size.width, - target_rect.size.height); - let render_target_id = self.next_render_target_id(); - - let (origin, texture_id) = - target.allocate_target_rect(target_rect.size.width, - target_rect.size.height, - context.device_pixel_ratio, - context.resource_cache, - self.id); - - let mut new_target = RenderTarget::new(render_target_id, - origin, - render_target_size, - Some(texture_id)); - - let local_transform = - Matrix4D::identity().translate(origin.x, origin.y, 0.0) - .mul(&stacking_context.transform) - .translate(-origin.x, -origin.y, 0.0); - for composition_operation in composition_operations { - target.push_composite(composition_operation, - texture_id, - target_rect, - &local_transform, - child_layer_index, - info.actual_scroll_layer_id, - z_clear_needed); - } - - info.offset_from_current_layer = Point2D::zero(); - - self.add_items_to_target(&scene_items, - &info, - &mut new_target, - context, - level, - z_clear_needed); - - target.children.push(new_target); - } + self.add_items_to_target(&scene_items, + &info, + context, + stacking_context.overflow, + composition_operations); } } } pub fn build(&mut self, resource_cache: &mut ResourceCache, - thread_pool: &mut scoped_threadpool::Pool, - device_pixel_ratio: f32) + _thread_pool: &mut scoped_threadpool::Pool, + _device_pixel_ratio: f32) -> RendererFrame { - // Traverse layer trees to calculate visible nodes - for (_, layer) in &mut self.layers { - layer.cull(); - } - - // Build resource list for newly visible nodes - self.update_resource_lists(resource_cache, thread_pool); - - // Update texture cache and build list of raster jobs. - self.update_texture_cache_and_build_raster_jobs(resource_cache); - - // Rasterize needed glyphs on worker threads - self.raster_glyphs(thread_pool, resource_cache); - - // Compile nodes that have become visible - self.compile_visible_nodes(thread_pool, resource_cache, device_pixel_ratio); - - // Update the batch cache from newly compiled nodes - self.update_batch_cache(); - - // Update the layer transform matrices self.update_layer_transforms(); - - // Collect the visible batches into a frame - let frame = self.collect_and_sort_visible_batches(resource_cache, device_pixel_ratio); - + let frame = self.build_frame(resource_cache); resource_cache.expire_old_resources(self.id); - frame } @@ -1363,127 +883,20 @@ impl Frame { } } - pub fn update_resource_lists(&mut self, - resource_cache: &ResourceCache, - thread_pool: &mut scoped_threadpool::Pool) { - let _pf = util::ProfileScope::new(" update_resource_lists"); - - for (_, layer) in &mut self.layers { - let nodes = &mut layer.aabb_tree.nodes; - let pipeline_auxiliary_lists = &self.pipeline_auxiliary_lists; - - thread_pool.scoped(|scope| { - for node in nodes { - if node.is_visible && node.compiled_node.is_none() { - scope.execute(move || { - node.build_resource_list(resource_cache, pipeline_auxiliary_lists); - }); - } - } - }); - } - } - - pub fn update_texture_cache_and_build_raster_jobs(&mut self, - resource_cache: &mut ResourceCache) { - let _pf = util::ProfileScope::new(" update_texture_cache_and_build_raster_jobs"); - - let frame_id = self.id; - for (_, layer) in &self.layers { - for node in &layer.aabb_tree.nodes { - if node.is_visible { - let resource_list = node.resource_list.as_ref().unwrap(); - resource_cache.add_resource_list(resource_list, frame_id); - } - } - } - } - - pub fn raster_glyphs(&mut self, - thread_pool: &mut scoped_threadpool::Pool, - resource_cache: &mut ResourceCache) { - let _pf = util::ProfileScope::new(" raster_glyphs"); - resource_cache.raster_pending_glyphs(thread_pool, self.id); - } - - pub fn compile_visible_nodes(&mut self, - thread_pool: &mut scoped_threadpool::Pool, - resource_cache: &ResourceCache, - device_pixel_ratio: f32) { - let _pf = util::ProfileScope::new(" compile_visible_nodes"); - - let layers = &mut self.layers; - let stacking_context_info = &self.stacking_context_info; - let draw_list_groups = &self.draw_list_groups; - let frame_id = self.id; - let pipeline_auxiliary_lists = &self.pipeline_auxiliary_lists; - - thread_pool.scoped(|scope| { - for (_, layer) in layers { - let nodes = &mut layer.aabb_tree.nodes; - for node in nodes { - if node.is_visible && node.compiled_node.is_none() { - scope.execute(move || { - node.compile(resource_cache, - frame_id, - device_pixel_ratio, - stacking_context_info, - draw_list_groups, - pipeline_auxiliary_lists); - }); - } - } - } + fn build_frame(&mut self, + resource_cache: &mut ResourceCache) -> RendererFrame { + let frame_builder = self.frame_builder.take(); + let frame = frame_builder.map(|mut builder| { + builder.build(resource_cache, + self.id, + &self.pipeline_auxiliary_lists, + &self.layers) }); - } - - pub fn update_batch_cache(&mut self) { - let _pf = util::ProfileScope::new(" update_batch_cache"); - - // Allocate and update VAOs - for (_, layer) in &mut self.layers { - for node in &mut layer.aabb_tree.nodes { - if node.is_visible { - let compiled_node = node.compiled_node.as_mut().unwrap(); - if let Some(vertex_buffer) = compiled_node.vertex_buffer.take() { - debug_assert!(compiled_node.vertex_buffer_id.is_none()); - - self.pending_updates.push(BatchUpdate { - id: vertex_buffer.id, - op: BatchUpdateOp::Create(vertex_buffer.vertices), - }); - - compiled_node.vertex_buffer_id = Some(vertex_buffer.id); - } - } - } - } - } - - pub fn collect_and_sort_visible_batches(&mut self, - resource_cache: &mut ResourceCache, - device_pixel_ratio: f32) - -> RendererFrame { - let root_layer = match self.root { - Some(ref mut root) => { - root.collect_and_sort_visible_batches(resource_cache, - &self.draw_list_groups, - &self.layers, - &self.stacking_context_info, - device_pixel_ratio) - } - None => { - DrawLayer::new(RenderTargetId(0), - Point2D::zero(), - Size2D::zero(), - None, - Vec::new(), - Vec::new()) - } - }; let layers_bouncing_back = self.collect_layers_bouncing_back(); - RendererFrame::new(self.pipeline_epoch_map.clone(), layers_bouncing_back, root_layer) + RendererFrame::new(self.pipeline_epoch_map.clone(), + layers_bouncing_back, + frame) } fn collect_layers_bouncing_back(&self) diff --git a/src/geometry.rs b/src/geometry.rs index 7e5abaab96..2afa0d5c3e 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -83,3 +83,39 @@ pub fn ray_intersects_rect(ray_origin: Point3D, (tmin < t1) && (tmax > t0) */ } + +/* +pub fn circle_contains_rect(circle_center: &Point2D, + radius: f32, + rect: &Rect) -> bool { + let dx = (circle_center.x - rect.origin.x).max(rect.origin.x + rect.size.width - circle_center.x); + let dy = (circle_center.y - rect.origin.y).max(rect.origin.y + rect.size.height - circle_center.y); + radius * radius >= dx * dx + dy * dy +} + +pub fn rect_intersects_circle(circle_center: &Point2D, + radius: f32, + rect: &Rect) -> bool { + let circle_distance_x = (circle_center.x - (rect.origin.x + rect.size.width * 0.5)).abs(); + let circle_distance_y = (circle_center.y - (rect.origin.y + rect.size.height * 0.5)).abs(); + + if circle_distance_x > rect.size.width * 0.5 + radius { + return false + } + if circle_distance_y > rect.size.height * 0.5 + radius { + return false + } + + if circle_distance_x <= rect.size.width * 0.5 { + return true; + } + if circle_distance_y <= rect.size.height * 0.5 { + return true; + } + + let corner_distance_sq = (circle_distance_x - rect.size.width * 0.5) * (circle_distance_x - rect.size.width * 0.5) + + (circle_distance_y - rect.size.height * 0.5) * (circle_distance_y - rect.size.height * 0.5); + + corner_distance_sq <= radius * radius +} +*/ diff --git a/src/internal_types.rs b/src/internal_types.rs index 3ead001def..fe628b21a7 100644 --- a/src/internal_types.rs +++ b/src/internal_types.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use app_units::Au; -use batch::{VertexBuffer, Batch, VertexBufferId, OffsetParams, TileParams}; +use batch::{VertexBufferId, TileParams}; use device::{TextureId, TextureFilter}; use euclid::{Matrix4D, Point2D, Rect, Size2D}; use fnv::FnvHasher; @@ -17,31 +17,26 @@ use std::ops::{Add, Sub}; use std::path::PathBuf; use std::sync::Arc; use texture_cache::BorderType; -use util; +use tiling; use webrender_traits::{FontKey, Epoch, ColorF, PipelineId}; use webrender_traits::{ImageFormat, MixBlendMode, NativeFontHandle, DisplayItem, ScrollLayerId}; #[derive(Hash, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] -pub struct DevicePixel(i32); +pub struct DevicePixel(pub i32); impl DevicePixel { - pub fn from_u32(value: u32) -> DevicePixel { - DevicePixel(value as i32) + pub fn new(value: f32, device_pixel_ratio: f32) -> DevicePixel { + DevicePixel((value * device_pixel_ratio).round() as i32) } - pub fn from_f32(value: f32) -> DevicePixel { - debug_assert!(value.fract() == 0.0); + pub fn from_u32(value: u32) -> DevicePixel { DevicePixel(value as i32) } - pub fn new(value: f32, device_pixel_ratio: f32) -> DevicePixel { - DevicePixel((value * device_pixel_ratio).round() as i32) - } - // TODO(gw): Remove eventually... - pub fn as_u16(&self) -> u16 { + pub fn as_f32(&self) -> f32 { let DevicePixel(value) = *self; - value as u16 + value as f32 } // TODO(gw): Remove eventually... @@ -49,12 +44,6 @@ impl DevicePixel { let DevicePixel(value) = *self; value as u32 } - - // TODO(gw): Remove eventually... - pub fn as_f32(&self) -> f32 { - let DevicePixel(value) = *self; - value as f32 - } } impl Add for DevicePixel { @@ -114,6 +103,17 @@ pub type DrawListId = FreeListItemId; pub enum TextureSampler { Color, Mask, + + Cache, + + CompositeLayer0, + CompositeLayer1, + CompositeLayer2, + CompositeLayer3, + CompositeLayer4, + CompositeLayer5, + CompositeLayer6, + CompositeLayer7, } pub enum VertexAttribute { @@ -167,12 +167,6 @@ pub struct WorkVertex { pub v: f32, } -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum PackedVertexColorMode { - Gradient, - BorderCorner, -} - #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct PackedVertexForQuad { @@ -206,65 +200,22 @@ pub struct PackedVertexForQuad { pub tile_params_index: u8, } -impl PackedVertexForQuad { - pub fn new(position: &Rect, - colors: &[ColorF; 4], - uv: &RectUv, - muv: &RectUv, - color_mode: PackedVertexColorMode) - -> PackedVertexForQuad { - PackedVertexForQuad { - x: position.origin.x, - y: position.origin.y, - width: position.size.width, - height: position.size.height, - color_tl: PackedColor::from_color(&colors[0]), - color_tr: PackedColor::from_color(&colors[1]), - color_br: PackedColor::from_color(&colors[2]), - color_bl: PackedColor::from_color(&colors[3]), - u_tl: uv.top_left.x, - v_tl: uv.top_left.y, - u_tr: uv.top_right.x, - v_tr: uv.top_right.y, - u_bl: uv.bottom_left.x, - v_bl: uv.bottom_left.y, - u_br: uv.bottom_right.x, - v_br: uv.bottom_right.y, - mu_tl: muv.top_left.x.as_u16(), - mv_tl: muv.top_left.y.as_u16(), - mu_tr: muv.top_right.x.as_u16(), - mv_tr: muv.top_right.y.as_u16(), - mu_bl: muv.bottom_left.x.as_u16(), - mv_bl: muv.bottom_left.y.as_u16(), - mu_br: muv.bottom_right.x.as_u16(), - mv_br: muv.bottom_right.y.as_u16(), - matrix_index: 0, - clip_in_rect_index: 0, - clip_out_rect_index: 0, - tile_params_index: match color_mode { - PackedVertexColorMode::Gradient => 0x00, - PackedVertexColorMode::BorderCorner => 0x80, - }, - } - } +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct PackedVertex { + pub pos: [f32; 2], } #[derive(Debug, Clone, Copy)] #[repr(C)] -pub struct PackedVertex { +pub struct FontVertex { pub x: f32, pub y: f32, - pub color: PackedColor, - pub u: f32, - pub v: f32, - pub mu: u16, - pub mv: u16, - pub matrix_index: u8, - pub clip_in_rect_index: u8, - pub clip_out_rect_index: u8, - pub tile_params_index: u8, + pub s: f32, + pub t: f32, } +/* impl PackedVertex { pub fn from_components(x: f32, y: f32, @@ -311,7 +262,7 @@ impl PackedVertex { tile_params_index: 0, } } -} +}*/ #[derive(Debug)] pub struct DebugFontVertex { @@ -361,8 +312,6 @@ pub enum TextureUpdateDetails { Raw, Blit(Vec), Blur(Vec, Size2D, Au, TextureImage, TextureImage, BorderType), - /// All four corners, the tessellation index, and whether inverted, respectively. - BorderRadius(DevicePixel, DevicePixel, DevicePixel, DevicePixel, Option, bool, BorderType), /// Blur radius, border radius, box size, raster origin, and whether inverted, respectively. BoxShadow(DevicePixel, DevicePixel, Size2D, Point2D, bool, BorderType), } @@ -402,33 +351,6 @@ impl TextureUpdateList { } } -pub enum BatchUpdateOp { - Create(Vec), - Destroy, -} - -pub struct BatchUpdate { - pub id: VertexBufferId, - pub op: BatchUpdateOp, -} - -pub struct BatchUpdateList { - pub updates: Vec, -} - -impl BatchUpdateList { - pub fn new() -> BatchUpdateList { - BatchUpdateList { - updates: Vec::new(), - } - } - - #[inline] - pub fn push(&mut self, update: BatchUpdate) { - self.updates.push(update); - } -} - // TODO(gw): Use bitflags crate for ClearInfo... // TODO(gw): Expand clear info to handle color, depth etc as needed. @@ -450,61 +372,6 @@ pub struct DrawCall { pub instance_count: u32, } -#[derive(Clone, Debug)] -pub struct OutputMask { - pub rect: Rect, - pub transform: Matrix4D, -} - -impl OutputMask { - pub fn new(rect: Rect, - transform: Matrix4D) -> OutputMask { - OutputMask { - rect: rect, - transform: transform, - } - } -} - -#[derive(Clone, Debug)] -pub struct MaskRegion { - pub masks: Vec, - pub draw_calls: Vec, -} - -impl MaskRegion { - pub fn new() -> MaskRegion { - MaskRegion { - masks: Vec::new(), - draw_calls: Vec::new(), - } - } - - pub fn add_mask(&mut self, - rect: Rect, - transform: Matrix4D) { - self.masks.push(OutputMask::new(rect, transform)); - } -} - -#[derive(Clone, Debug)] -pub struct BatchInfo { - pub matrix_palette: Vec>, - pub offset_palette: Vec, - pub regions: Vec, -} - -impl BatchInfo { - pub fn new(matrix_palette: Vec>, - offset_palette: Vec) -> BatchInfo { - BatchInfo { - matrix_palette: matrix_palette, - offset_palette: offset_palette, - regions: Vec::new(), - } - } -} - #[derive(Debug, Clone)] pub struct CompositeBatchJob { pub rect: Rect, @@ -528,71 +395,24 @@ pub struct CompositeBatchInfo { pub jobs: Vec, } -#[derive(Debug, Clone)] -pub struct DrawCompositeBatchInfo { - pub operation: CompositionOp, - pub texture_id: TextureId, - pub scroll_layer_id: ScrollLayerId, - pub jobs: Vec, -} - -#[derive(Clone, Debug)] -pub enum DrawCommand { - Batch(BatchInfo), - CompositeBatch(DrawCompositeBatchInfo), - Clear(ClearInfo), -} - #[derive(Clone, Copy, Debug, Ord, PartialOrd, PartialEq, Eq, Hash)] pub struct ChildLayerIndex(pub u32); -#[derive(Debug)] -pub struct DrawLayer { - // This layer - pub id: RenderTargetId, - pub commands: Vec, - pub texture_id: Option, - pub origin: Point2D, - pub size: Size2D, - - // Children - pub child_layers: Vec, -} - -impl DrawLayer { - pub fn new(id: RenderTargetId, - origin: Point2D, - size: Size2D, - texture_id: Option, - commands: Vec, - child_layers: Vec) - -> DrawLayer { - DrawLayer { - id: id, - origin: origin, - size: size, - texture_id: texture_id, - commands: commands, - child_layers: child_layers, - } - } -} - pub struct RendererFrame { pub pipeline_epoch_map: HashMap>, pub layers_bouncing_back: HashSet>, - pub root_layer: DrawLayer, + pub frame: Option, } impl RendererFrame { pub fn new(pipeline_epoch_map: HashMap>, layers_bouncing_back: HashSet>, - root_layer: DrawLayer) + frame: Option) -> RendererFrame { RendererFrame { pipeline_epoch_map: pipeline_epoch_map, layers_bouncing_back: layers_bouncing_back, - root_layer: root_layer, + frame: frame, } } } @@ -600,9 +420,10 @@ impl RendererFrame { pub enum ResultMsg { UpdateTextureCache(TextureUpdateList), RefreshShader(PathBuf), - NewFrame(RendererFrame, BatchUpdateList, BackendProfileCounters), + NewFrame(RendererFrame, BackendProfileCounters), } +#[repr(u32)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum AxisDirection { Horizontal, @@ -659,42 +480,12 @@ impl FreeListItem for DrawList { #[derive(Clone, Copy, Debug, Ord, PartialOrd, PartialEq, Eq)] pub struct DrawListItemIndex(pub u32); -#[derive(Debug)] -pub struct BatchList { - pub batches: Vec, - pub draw_list_group_id: DrawListGroupId, -} - -pub struct CompiledNode { - // TODO(gw): These are mutually exclusive - unify into an enum? - pub vertex_buffer: Option, - pub vertex_buffer_id: Option, - - pub batch_list: Vec, -} - -impl CompiledNode { - pub fn new() -> CompiledNode { - CompiledNode { - batch_list: Vec::new(), - vertex_buffer: None, - vertex_buffer_id: None, - } - } -} - #[derive(Clone, Copy, Debug)] pub struct RectPolygon { pub pos: Rect, pub varyings: Varyings, } -impl RectPolygon { - pub fn is_well_formed_and_nonempty(&self) -> bool { - util::rect_is_well_formed_and_nonempty(&self.pos) - } -} - #[derive(Clone, Copy, Debug)] pub struct RectColors { pub top_left: ColorF, @@ -711,72 +502,6 @@ pub struct RectUv { pub bottom_right: Point2D, } -impl RectUv { - pub fn zero() -> RectUv { - RectUv { - top_left: Point2D::zero(), - top_right: Point2D::zero(), - bottom_left: Point2D::zero(), - bottom_right: Point2D::zero(), - } - } - - pub fn from_uv_rect_rotation_angle(uv_rect: &RectUv, - rotation_angle: BasicRotationAngle, - flip_90_degree_rotations: bool) -> RectUv { - match (rotation_angle, flip_90_degree_rotations) { - (BasicRotationAngle::Upright, _) => { - RectUv { - top_left: uv_rect.top_left, - top_right: uv_rect.top_right, - bottom_right: uv_rect.bottom_right, - bottom_left: uv_rect.bottom_left, - } - } - (BasicRotationAngle::Clockwise90, true) => { - RectUv { - top_right: uv_rect.top_left, - top_left: uv_rect.top_right, - bottom_left: uv_rect.bottom_right, - bottom_right: uv_rect.bottom_left, - } - } - (BasicRotationAngle::Clockwise90, false) => { - RectUv { - top_right: uv_rect.top_left, - bottom_right: uv_rect.top_right, - bottom_left: uv_rect.bottom_right, - top_left: uv_rect.bottom_left, - } - } - (BasicRotationAngle::Clockwise180, _) => { - RectUv { - bottom_right: uv_rect.top_left, - bottom_left: uv_rect.top_right, - top_left: uv_rect.bottom_right, - top_right: uv_rect.bottom_left, - } - } - (BasicRotationAngle::Clockwise270, true) => { - RectUv { - bottom_left: uv_rect.top_left, - bottom_right: uv_rect.top_right, - top_right: uv_rect.bottom_right, - top_left: uv_rect.bottom_left, - } - } - (BasicRotationAngle::Clockwise270, false) => { - RectUv { - bottom_left: uv_rect.top_left, - top_left: uv_rect.top_right, - top_right: uv_rect.bottom_right, - bottom_right: uv_rect.bottom_left, - } - } - } - } -} - #[derive(Clone, Debug)] pub struct PolygonPosColorUv { pub vertices: Vec, @@ -866,43 +591,6 @@ impl PackedVertexForTextureCacheUpdate { } } -#[derive(Clone, Debug, Hash, Eq, PartialEq)] -pub struct BorderRadiusRasterOp { - pub outer_radius_x: DevicePixel, - pub outer_radius_y: DevicePixel, - pub inner_radius_x: DevicePixel, - pub inner_radius_y: DevicePixel, - pub index: Option, - pub image_format: ImageFormat, - pub inverted: bool, -} - -impl BorderRadiusRasterOp { - pub fn create(outer_radius_x: DevicePixel, - outer_radius_y: DevicePixel, - inner_radius_x: DevicePixel, - inner_radius_y: DevicePixel, - inverted: bool, - index: Option, - image_format: ImageFormat) - -> Option { - if outer_radius_x > DevicePixel::zero() || outer_radius_y > DevicePixel::zero() || - inner_radius_x > DevicePixel::zero() || inner_radius_y > DevicePixel::zero() { - Some(BorderRadiusRasterOp { - outer_radius_x: outer_radius_x, - outer_radius_y: outer_radius_y, - inner_radius_x: inner_radius_x, - inner_radius_y: inner_radius_y, - index: index, - inverted: inverted, - image_format: image_format, - }) - } else { - None - } - } -} - #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct BoxShadowRasterOp { pub blur_radius: DevicePixel, @@ -915,13 +603,14 @@ pub struct BoxShadowRasterOp { pub inverted: bool, } +/* impl BoxShadowRasterOp { pub fn raster_rect(blur_radius: f32, border_radius: f32, part: BoxShadowPart, box_rect: &Rect) -> Rect { - let outer_extent = 3.0 * blur_radius; + let outer_extent = blur_radius; let inner_extent = outer_extent.max(border_radius); let extent = outer_extent + inner_extent; match part { @@ -1001,12 +690,12 @@ impl BoxShadowRasterOp { None } } -} +}*/ #[derive(Clone, Hash, PartialEq, Eq, Debug)] pub enum BoxShadowPart { - Corner, - Edge, + _Corner, + _Edge, } #[derive(Clone, Hash, PartialEq, Eq, Debug)] @@ -1030,16 +719,7 @@ impl GlyphKey { #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub enum RasterItem { - BorderRadius(BorderRadiusRasterOp), - BoxShadow(BoxShadowRasterOp), -} - -#[derive(Clone, Copy, Debug)] -pub enum BasicRotationAngle { - Upright, - Clockwise90, - Clockwise180, - Clockwise270, + _BoxShadow(BoxShadowRasterOp), } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] @@ -1061,26 +741,3 @@ pub enum CompositionOp { MixBlend(MixBlendMode), Filter(LowLevelFilterOp), } - -impl CompositionOp { - pub fn target_rect(&self, unfiltered_target_rect: &Rect) -> Rect { - match *self { - CompositionOp::Filter(LowLevelFilterOp::Blur(amount, AxisDirection::Horizontal)) => { - unfiltered_target_rect.inflate(amount.to_f32_px(), 0.0) - } - CompositionOp::Filter(LowLevelFilterOp::Blur(amount, AxisDirection::Vertical)) => { - unfiltered_target_rect.inflate(0.0, amount.to_f32_px()) - } - _ => *unfiltered_target_rect, - } - } -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum RectSide { - Top, - Right, - Bottom, - Left, -} - diff --git a/src/layer.rs b/src/layer.rs index bd49d7f3b8..e749d06045 100644 --- a/src/layer.rs +++ b/src/layer.rs @@ -2,16 +2,11 @@ * 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 aabbtree::{AABBTree, NodeIndex}; use euclid::{Matrix4D, Point2D, Rect, Size2D}; -use internal_types::{BatchUpdate, BatchUpdateList, BatchUpdateOp}; -use internal_types::{DrawListItemIndex, DrawListId, DrawListGroupId}; use spring::{DAMPING, STIFFNESS, Spring}; use webrender_traits::{PipelineId, ScrollLayerId, ServoStackingContextId}; pub struct Layer { - // TODO: Remove pub from here if possible in the future - pub aabb_tree: AABBTree, pub scrolling: ScrollingState, /// The viewable region, in world coordinates. @@ -42,11 +37,7 @@ impl Layer { pipeline_id: PipelineId, stacking_context_id: ServoStackingContextId) -> Layer { - let rect = Rect::new(Point2D::zero(), layer_size); - let aabb_tree = AABBTree::new(8192.0, &rect); - Layer { - aabb_tree: aabb_tree, scrolling: ScrollingState::new(), viewport_rect: *viewport_rect, viewport_transform: *viewport_transform, @@ -65,45 +56,8 @@ impl Layer { self.children.push(child); } - pub fn reset(&mut self, pending_updates: &mut BatchUpdateList) { - for node in &mut self.aabb_tree.nodes { - if let Some(ref mut compiled_node) = node.compiled_node { - let vertex_buffer_id = compiled_node.vertex_buffer_id.take().unwrap(); - pending_updates.push(BatchUpdate { - id: vertex_buffer_id, - op: BatchUpdateOp::Destroy, - }); - } - } - } - - #[inline] - pub fn insert(&mut self, - rect: Rect, - draw_list_group_id: DrawListGroupId, - draw_list_id: DrawListId, - item_index: DrawListItemIndex) { - self.aabb_tree.insert(rect, - draw_list_group_id, - draw_list_id, - item_index); - } - pub fn finalize(&mut self, scrolling: &ScrollingState) { self.scrolling = *scrolling; - self.aabb_tree.finalize(); - } - - pub fn cull(&mut self) { - let viewport_rect = self.viewport_rect; - let adjusted_viewport = viewport_rect.translate(&-self.world_origin) - .translate(&-self.scrolling.offset); - self.aabb_tree.cull(&adjusted_viewport); - } - - #[allow(dead_code)] - pub fn print(&self) { - self.aabb_tree.print(NodeIndex(0), 0); } pub fn overscroll_amount(&self) -> Size2D { diff --git a/src/lib.rs b/src/lib.rs index 53eed4865c..b15715ae53 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +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/. */ +#![feature(step_by)] //#![feature(mpsc_select)] //! A GPU based Webrender. @@ -47,7 +48,6 @@ extern crate lazy_static; #[macro_use] extern crate log; -mod aabbtree; mod batch; mod batch_builder; mod debug_font_data; @@ -58,15 +58,14 @@ mod freelist; mod geometry; mod internal_types; mod layer; -mod node_compiler; mod profiler; mod render_backend; mod resource_cache; mod resource_list; mod scene; mod spring; -mod tessellator; mod texture_cache; +mod tiling; mod util; mod platform { @@ -107,6 +106,5 @@ extern crate time; extern crate webrender_traits; extern crate offscreen_gl_context; extern crate byteorder; -extern crate bit_set; pub use renderer::{Renderer, RendererOptions}; diff --git a/src/node_compiler.rs b/src/node_compiler.rs deleted file mode 100644 index e882e6ef0c..0000000000 --- a/src/node_compiler.rs +++ /dev/null @@ -1,165 +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/. */ - -use aabbtree::AABBTreeNode; -use batch::{BatchBuilder, VertexBuffer}; -use fnv::FnvHasher; -use frame::{DrawListGroup, FrameId}; -use internal_types::{DrawListItemIndex, CompiledNode, StackingContextInfo}; -use internal_types::{BatchList, StackingContextIndex}; -use internal_types::{DrawListGroupId}; -use std::hash::BuildHasherDefault; -use resource_cache::ResourceCache; -use std::collections::HashMap; -use webrender_traits::{AuxiliaryLists, PipelineId, SpecificDisplayItem}; - -pub trait NodeCompiler { - fn compile(&mut self, - resource_cache: &ResourceCache, - frame_id: FrameId, - device_pixel_ratio: f32, - stacking_context_info: &[StackingContextInfo], - draw_list_groups: &HashMap>, - pipeline_auxiliary_lists: &HashMap>); -} - -impl NodeCompiler for AABBTreeNode { - fn compile(&mut self, - resource_cache: &ResourceCache, - frame_id: FrameId, - device_pixel_ratio: f32, - stacking_context_info: &[StackingContextInfo], - draw_list_groups: &HashMap>, - pipeline_auxiliary_lists: &HashMap>) { - let mut compiled_node = CompiledNode::new(); - let mut vertex_buffer = VertexBuffer::new(); - - for draw_list_group_segment in &self.draw_list_group_segments { - let mut builder = BatchBuilder::new(&mut vertex_buffer, device_pixel_ratio); - - // TODO(gw): This is a HACK to fix matrix palette index offsets - there needs to - // be no holes in this array to match the draw group matrix palette. It's - // noticeable on wikipedia. Find a better solution to this!!! - let draw_list_group = &draw_list_groups[&draw_list_group_segment.draw_list_group_id]; - - for draw_list_id in &draw_list_group.draw_list_ids { - let draw_list_index_buffer = draw_list_group_segment.index_buffers.iter().find(|ib| { - ib.draw_list_id == *draw_list_id - }); - - if let Some(draw_list_index_buffer) = draw_list_index_buffer { - let draw_list = resource_cache.get_draw_list(draw_list_index_buffer.draw_list_id); - let auxiliary_lists = - pipeline_auxiliary_lists.get(&draw_list.pipeline_id) - .expect("No auxiliary lists for pipeline?!"); - - let StackingContextIndex(stacking_context_id) = draw_list.stacking_context_index.unwrap(); - let context = &stacking_context_info[stacking_context_id]; - - let offset_from_layer = context.offset_from_layer; - builder.set_current_clip_rect_offset(offset_from_layer); - - for index in &draw_list_index_buffer.indices { - let DrawListItemIndex(index) = *index; - let display_item = &draw_list.items[index as usize]; - - let clip_rect = display_item.clip.main.intersection(&context.local_clip_rect); - - if let Some(ref clip_rect) = clip_rect { - builder.push_clip_in_rect(clip_rect); - builder.push_complex_clip( - auxiliary_lists.complex_clip_regions(&display_item.clip.complex)); - - match display_item.item { - SpecificDisplayItem::WebGL(ref info) => { - builder.add_webgl_rectangle(&display_item.rect, - resource_cache, - &info.context_id, - frame_id); - } - SpecificDisplayItem::Image(ref info) => { - builder.add_image(&display_item.rect, - &info.stretch_size, - info.image_key, - info.image_rendering, - resource_cache, - frame_id); - } - SpecificDisplayItem::Text(ref info) => { - let glyphs = auxiliary_lists.glyph_instances(&info.glyphs); - builder.add_text(&display_item.rect, - info.font_key, - info.size, - info.blur_radius, - &info.color, - &glyphs, - resource_cache, - frame_id, - device_pixel_ratio); - } - SpecificDisplayItem::Rectangle(ref info) => { - builder.add_color_rectangle(&display_item.rect, - &info.color, - resource_cache, - frame_id); - } - SpecificDisplayItem::Gradient(ref info) => { - builder.add_gradient(&display_item.rect, - &info.start_point, - &info.end_point, - &info.stops, - auxiliary_lists, - resource_cache, - frame_id); - } - SpecificDisplayItem::BoxShadow(ref info) => { - builder.add_box_shadow(&info.box_bounds, - &info.offset, - &info.color, - info.blur_radius, - info.spread_radius, - info.border_radius, - info.clip_mode, - resource_cache, - frame_id); - } - SpecificDisplayItem::Border(ref info) => { - builder.add_border(&display_item.rect, - info, - resource_cache, - frame_id, - device_pixel_ratio); - } - } - - builder.pop_complex_clip(); - builder.pop_clip_in_rect(); - } - } - } - - builder.next_draw_list(); - } - - let batches = builder.finalize(); - if !batches.is_empty() { - compiled_node.batch_list.push(BatchList { - batches: batches, - draw_list_group_id: draw_list_group_segment.draw_list_group_id, - }); - } - } - - compiled_node.vertex_buffer = Some(vertex_buffer); - self.compiled_node = Some(compiled_node); - } -} diff --git a/src/profiler.rs b/src/profiler.rs index cee9b3ad28..83610667dd 100644 --- a/src/profiler.rs +++ b/src/profiler.rs @@ -172,7 +172,9 @@ pub struct RendererProfileCounters { pub struct RendererProfileTimers { pub cpu_time: TimeProfileCounter, - pub gpu_time: TimeProfileCounter, + pub gpu_time_paint: TimeProfileCounter, + pub gpu_time_composite: TimeProfileCounter, + pub gpu_time_total: TimeProfileCounter, } impl RendererProfileCounters { @@ -196,7 +198,9 @@ impl RendererProfileTimers { pub fn new() -> RendererProfileTimers { RendererProfileTimers { cpu_time: TimeProfileCounter::new("Compositor CPU Time", false), - gpu_time: TimeProfileCounter::new("GPU Time", false), + gpu_time_paint: TimeProfileCounter::new("GPU Time (paint)", false), + gpu_time_composite: TimeProfileCounter::new("GPU Time (composite)", false), + gpu_time_total: TimeProfileCounter::new("GPU Time (total)", false), } } } @@ -432,20 +436,22 @@ impl Profiler { &backend_profile.image_templates, ], debug_renderer, true); - self.draw_counters(&[ - &backend_profile.total_time, - &renderer_timers.cpu_time, - &renderer_timers.gpu_time, - ], debug_renderer, false); - self.draw_counters(&[ &renderer_profile.draw_calls, &renderer_profile.vertices, + ], debug_renderer, true); + + self.draw_counters(&[ + &backend_profile.total_time, + &renderer_timers.cpu_time, + &renderer_timers.gpu_time_paint, + &renderer_timers.gpu_time_composite, + &renderer_timers.gpu_time_total, ], debug_renderer, false); self.backend_time.push(backend_profile.total_time.nanoseconds); self.compositor_time.push(renderer_timers.cpu_time.nanoseconds); - self.gpu_time.push(renderer_timers.gpu_time.nanoseconds); + self.gpu_time.push(renderer_timers.gpu_time_total.nanoseconds); let rect = self.backend_time.draw_graph(self.x_left, self.y_left, "CPU (backend)", debug_renderer); self.y_left += rect.size.height + 10.0; diff --git a/src/render_backend.rs b/src/render_backend.rs index 6798324561..69220b16c3 100644 --- a/src/render_backend.rs +++ b/src/render_backend.rs @@ -14,11 +14,12 @@ use std::collections::HashMap; use std::io::{Cursor, Read}; use std::sync::{Arc, Mutex}; use std::sync::mpsc::Sender; -use texture_cache::{TextureCache, TextureCacheItemId}; +use texture_cache::TextureCache; use webrender_traits::{ApiMsg, AuxiliaryLists, BuiltDisplayList, IdNamespace}; use webrender_traits::{PipelineId, RenderNotifier, WebGLContextId}; use batch::new_id; use device::TextureId; +use tiling::FrameBuilderConfig; use offscreen_gl_context::{ColorAttachmentType, GLContext}; use offscreen_gl_context::{NativeGLContext, NativeGLContextHandle}; @@ -49,18 +50,16 @@ impl RenderBackend { payload_tx: IpcBytesSender, result_tx: Sender, device_pixel_ratio: f32, - white_image_id: TextureCacheItemId, - dummy_mask_image_id: TextureCacheItemId, texture_cache: TextureCache, enable_aa: bool, notifier: Arc>>>, - webrender_context_handle: Option) -> RenderBackend { + webrender_context_handle: Option, + config: FrameBuilderConfig, + debug: bool) -> RenderBackend { let mut thread_pool = scoped_threadpool::Pool::new(8); let resource_cache = ResourceCache::new(&mut thread_pool, texture_cache, - white_image_id, - dummy_mask_image_id, device_pixel_ratio, enable_aa); @@ -73,7 +72,7 @@ impl RenderBackend { device_pixel_ratio: device_pixel_ratio, resource_cache: resource_cache, scene: Scene::new(), - frame: Frame::new(), + frame: Frame::new(debug, config), next_namespace_id: IdNamespace(1), notifier: notifier, webrender_context_handle: webrender_context_handle, @@ -212,6 +211,7 @@ impl RenderBackend { ApiMsg::Scroll(delta, cursor, move_phase) => { let frame = profile_counters.total_time.profile(|| { if self.frame.scroll(delta, cursor, move_phase) { + self.build_scene(); Some(self.render()) } else { None @@ -229,6 +229,7 @@ impl RenderBackend { ApiMsg::TickScrollingBounce => { let frame = profile_counters.total_time.profile(|| { self.frame.tick_scrolling_bounce_animations(); + self.build_scene(); self.render() }); @@ -237,7 +238,7 @@ impl RenderBackend { ApiMsg::TranslatePointToLayerSpace(point, tx) => { // First, find the specific layer that contains the point. let point = point / self.device_pixel_ratio; - if let (Some(root_pipeline_id), Some(root_scroll_layer_id)) = + if let (Some(..), Some(root_scroll_layer_id)) = (self.scene.root_pipeline_id, self.frame.root_scroll_layer_id) { if let Some(scroll_layer_id) = @@ -388,8 +389,7 @@ impl RenderBackend { fn publish_frame(&mut self, frame: RendererFrame, profile_counters: &mut BackendProfileCounters) { - let pending_updates = self.frame.pending_updates(); - let msg = ResultMsg::NewFrame(frame, pending_updates, profile_counters.clone()); + let msg = ResultMsg::NewFrame(frame, profile_counters.clone()); self.result_tx.send(msg).unwrap(); profile_counters.reset(); } diff --git a/src/renderer.rs b/src/renderer.rs index 608f7d3652..fc26312d4d 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -9,46 +9,43 @@ //! //! [renderer]: struct.Renderer.html -use batch::{RasterBatch, VertexBufferId}; -use bit_set::BitSet; +use batch::RasterBatch; use debug_render::DebugRenderer; use device::{Device, ProgramId, TextureId, UniformLocation, VertexFormat, GpuProfile}; -use device::{TextureFilter, VAOId, VBOId, VertexUsageHint, FileWatcherHandler}; -use euclid::{Matrix4D, Point2D, Point4D, Rect, Size2D}; -use fnv::FnvHasher; +use device::{TextureFilter, VAOId, VertexUsageHint, FileWatcherHandler}; +use euclid::{Matrix4D, Point2D, Rect, Size2D}; use gleam::gl; -use internal_types::{RendererFrame, ResultMsg, TextureUpdateOp, BatchUpdateOp, BatchUpdateList}; +use internal_types::{RendererFrame, ResultMsg, TextureUpdateOp}; use internal_types::{TextureUpdateDetails, TextureUpdateList, PackedVertex, RenderTargetMode}; use internal_types::{ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE, DevicePixel}; -use internal_types::{PackedVertexForTextureCacheUpdate, CompositionOp, ChildLayerIndex}; -use internal_types::{AxisDirection, LowLevelFilterOp, DrawCommand, DrawLayer, ANGLE_FLOAT_TO_FIXED}; -use internal_types::{BasicRotationAngle, RenderTargetId}; +use internal_types::{PackedVertexForTextureCacheUpdate, CompositionOp}; +use internal_types::{AxisDirection}; use ipc_channel::ipc; use profiler::{Profiler, BackendProfileCounters}; use profiler::{RendererProfileTimers, RendererProfileCounters}; use render_backend::RenderBackend; -use std::collections::HashMap; +use std::cmp; use std::f32; -use std::hash::BuildHasherDefault; use std::mem; use std::path::PathBuf; use std::sync::{Arc, Mutex}; use std::sync::mpsc::{channel, Receiver, Sender}; use std::thread; -use tessellator::BorderCornerTessellation; use texture_cache::{BorderType, TextureCache, TextureInsertOp}; +use tiling::{self, Frame, FrameBuilderConfig, PrimitiveBatchData, PackedTile}; +use tiling::{TransformedRectKind, RenderTarget, ClearTile, PackedLayer}; use time::precise_time_ns; use webrender_traits::{ColorF, Epoch, PipelineId, RenderNotifier}; use webrender_traits::{ImageFormat, MixBlendMode, RenderApiSender}; use offscreen_gl_context::{NativeGLContext, NativeGLContextMethods}; -use util::{MatrixHelpers, RectHelpers}; pub const BLUR_INFLATION_FACTOR: u32 = 3; pub const MAX_RASTER_OP_SIZE: u32 = 2048; -const MAX_CACHED_QUAD_VAOS: usize = 8; - -// TODO(gw): HACK! Need to support lighten/darken mix-blend-mode properly on android... +const UBO_BIND_LAYERS: u32 = 1; +const UBO_BIND_CLEAR_TILES: u32 = 2; +const UBO_BIND_PRIM_TILES: u32 = 3; +const UBO_BIND_CACHE_ITEMS: u32 = 4; #[derive(Clone, Copy)] struct VertexBuffer { @@ -84,13 +81,6 @@ impl CompositionOpHelpers for CompositionOp { } } -struct RenderContext { - blend_program_id: ProgramId, - filter_program_id: ProgramId, - device_pixel_ratio: f32, - framebuffer_size: Size2D, -} - struct FileWatcher { notifier: Arc>>>, result_tx: Sender, @@ -104,44 +94,112 @@ impl FileWatcherHandler for FileWatcher { } } +fn get_ubo_max_len(max_ubo_size: usize) -> usize { + let item_size = mem::size_of::(); + let max_items = max_ubo_size / item_size; + + // TODO(gw): Clamping to 1024 since some shader compilers + // seem to go very slow when you have high + // constants for array lengths. Investigate + // whether this clamping actually hurts performance! + cmp::min(max_items, 1024) +} + +fn create_prim_shader(name: &'static str, + device: &mut Device, + max_prim_tiles: usize, + max_prim_layers: usize, + max_prim_items: usize) -> ProgramId { + let prefix = format!("#define WR_MAX_PRIM_TILES {}\n#define WR_MAX_PRIM_LAYERS {}\n#define WR_MAX_PRIM_ITEMS {}\n", max_prim_tiles, + max_prim_layers, + max_prim_items); + + let program_id = device.create_program_with_prefix(name, + "prim_shared", + Some(prefix)); + + let tiles_index = gl::get_uniform_block_index(program_id.0, "Tiles"); + gl::uniform_block_binding(program_id.0, tiles_index, UBO_BIND_PRIM_TILES); + + let layer_index = gl::get_uniform_block_index(program_id.0, "Layers"); + gl::uniform_block_binding(program_id.0, layer_index, UBO_BIND_LAYERS); + + let item_index = gl::get_uniform_block_index(program_id.0, "Items"); + gl::uniform_block_binding(program_id.0, item_index, UBO_BIND_CACHE_ITEMS); + + println!("PrimShader {}: items={}/{} tiles={}/{} layers={}/{}", name, + item_index, + max_prim_items, + tiles_index, + max_prim_tiles, + layer_index, + max_prim_layers); + + program_id +} + +fn create_clear_shader(name: &'static str, + device: &mut Device, + max_clear_tiles: usize) -> ProgramId { + let prefix = format!("#define WR_MAX_CLEAR_TILES {}", max_clear_tiles); + + let program_id = device.create_program_with_prefix(name, + "shared_other", + Some(prefix)); + + let tile_index = gl::get_uniform_block_index(program_id.0, "Tiles"); + gl::uniform_block_binding(program_id.0, tile_index, UBO_BIND_CLEAR_TILES); + + println!("ClearShader {}: tiles={}/{}", name, tile_index, max_clear_tiles); + + program_id +} + pub struct Renderer { result_rx: Receiver, device: Device, pending_texture_updates: Vec, - pending_batch_updates: Vec, pending_shader_updates: Vec, current_frame: Option, device_pixel_ratio: f32, - vertex_buffers: HashMap, BuildHasherDefault>, raster_batches: Vec, - quad_vertex_buffer: Option, - cached_quad_vaos: Vec, - simple_triangles_vao: Option, raster_op_vao: Option, - quad_program_id: ProgramId, - u_quad_transform_array: UniformLocation, - u_quad_offset_array: UniformLocation, - u_tile_params: UniformLocation, - u_clip_rects: UniformLocation, - u_atlas_params: UniformLocation, - - blit_program_id: ProgramId, - - border_program_id: ProgramId, - - blend_program_id: ProgramId, - u_blend_params: UniformLocation, - - filter_program_id: ProgramId, - u_filter_params: UniformLocation, - box_shadow_program_id: ProgramId, blur_program_id: ProgramId, u_direction: UniformLocation, - mask_program_id: ProgramId, + ps_rectangle: ProgramId, + ps_text: ProgramId, + ps_image: ProgramId, + ps_border: ProgramId, + ps_box_shadow: ProgramId, + ps_blend: ProgramId, + ps_composite: ProgramId, + ps_aligned_gradient: ProgramId, + ps_angle_gradient: ProgramId, + + ps_rectangle_clip: ProgramId, + ps_image_clip: ProgramId, + + ps_rectangle_transform: ProgramId, + ps_image_transform: ProgramId, + + tile_clear_shader: ProgramId, + + max_clear_tiles: usize, + max_prim_rectangles: usize, + max_prim_rectangles_clip: usize, + max_prim_texts: usize, + max_prim_images: usize, + max_prim_images_clip: usize, + max_prim_borders: usize, + max_prim_box_shadows: usize, + max_prim_blends: usize, + max_prim_composites: usize, + max_prim_aligned_gradients: usize, + max_prim_angle_gradients: usize, notifier: Arc>>>, @@ -156,9 +214,11 @@ pub struct Renderer { max_raster_op_size: u32, raster_op_target_a8: TextureId, raster_op_target_rgba8: TextureId, - temporary_fb_texture: TextureId, + render_targets: [TextureId; 2], - gpu_profile: GpuProfile, + gpu_profile_paint: GpuProfile, + gpu_profile_composite: GpuProfile, + quad_vao_id: VAOId, } impl Renderer { @@ -198,16 +258,97 @@ impl Renderer { Box::new(file_watch_handler)); device.begin_frame(); - let quad_program_id = device.create_program("quad"); - let blit_program_id = device.create_program("blit"); - let border_program_id = device.create_program("border"); - let blend_program_id = device.create_program("blend"); - let filter_program_id = device.create_program("filter"); - let box_shadow_program_id = device.create_program("box_shadow"); - let blur_program_id = device.create_program("blur"); - let mask_program_id = device.create_program("mask"); + let box_shadow_program_id = device.create_program("box_shadow", "shared_other"); + let blur_program_id = device.create_program("blur", "shared_other"); let max_raster_op_size = MAX_RASTER_OP_SIZE * options.device_pixel_ratio as u32; + let max_ubo_size = gl::get_integer_v(gl::MAX_UNIFORM_BLOCK_SIZE) as usize; + let max_prim_tiles = get_ubo_max_len::(max_ubo_size); + let max_prim_layers = get_ubo_max_len::(max_ubo_size); + + let max_prim_rectangles = get_ubo_max_len::(max_ubo_size); + let max_prim_rectangles_clip = get_ubo_max_len::(max_ubo_size); + let max_prim_texts = get_ubo_max_len::(max_ubo_size); + let max_prim_images = get_ubo_max_len::(max_ubo_size); + let max_prim_images_clip = get_ubo_max_len::(max_ubo_size); + let max_prim_borders = get_ubo_max_len::(max_ubo_size); + let max_prim_box_shadows = get_ubo_max_len::(max_ubo_size); + let max_prim_blends = get_ubo_max_len::(max_ubo_size); + let max_prim_composites = get_ubo_max_len::(max_ubo_size); + let max_prim_aligned_gradients = get_ubo_max_len::(max_ubo_size); + let max_prim_angle_gradients = get_ubo_max_len::(max_ubo_size); + + let ps_rectangle = create_prim_shader("ps_rectangle", + &mut device, + max_prim_tiles, + max_prim_layers, + max_prim_rectangles); + let ps_rectangle_clip = create_prim_shader("ps_rectangle_clip", + &mut device, + max_prim_tiles, + max_prim_layers, + max_prim_rectangles_clip); + let ps_text = create_prim_shader("ps_text", + &mut device, + max_prim_tiles, + max_prim_layers, + max_prim_texts); + let ps_image = create_prim_shader("ps_image", + &mut device, + max_prim_tiles, + max_prim_layers, + max_prim_images); + let ps_image_clip = create_prim_shader("ps_image_clip", + &mut device, + max_prim_tiles, + max_prim_layers, + max_prim_images_clip); + + let ps_border = create_prim_shader("ps_border", + &mut device, + max_prim_tiles, + max_prim_layers, + max_prim_borders); + let ps_box_shadow = create_prim_shader("ps_box_shadow", + &mut device, + max_prim_tiles, + max_prim_layers, + max_prim_box_shadows); + let ps_aligned_gradient = create_prim_shader("ps_gradient", + &mut device, + max_prim_tiles, + max_prim_layers, + max_prim_aligned_gradients); + let ps_angle_gradient = create_prim_shader("ps_angle_gradient", + &mut device, + max_prim_tiles, + max_prim_layers, + max_prim_angle_gradients); + + let ps_rectangle_transform = create_prim_shader("ps_rectangle_transform", + &mut device, + max_prim_tiles, + max_prim_layers, + max_prim_rectangles); + let ps_image_transform = create_prim_shader("ps_image_transform", + &mut device, + max_prim_tiles, + max_prim_layers, + max_prim_images); + let ps_blend = create_prim_shader("ps_blend", + &mut device, + max_prim_tiles, + max_prim_layers, + max_prim_blends); + let ps_composite = create_prim_shader("ps_composite", + &mut device, + max_prim_tiles, + max_prim_layers, + max_prim_composites); + + let max_clear_tiles = get_ubo_max_len::(max_ubo_size); + let tile_clear_shader = create_clear_shader("ps_clear", &mut device, max_clear_tiles); + let texture_ids = device.create_texture_ids(1024); let mut texture_cache = TextureCache::new(texture_ids); let white_pixels: Vec = vec![ @@ -263,7 +404,32 @@ impl Renderer { RenderTargetMode::RenderTarget, None); - let temporary_fb_texture = device.create_texture_ids(1)[0]; + let x0 = 0.0; + let y0 = 0.0; + let x1 = 1.0; + let y1 = 1.0; + + // TODO(gw): Consider separate VBO for quads vs border corners if VS ever shows up in profile! + let quad_indices: [u16; 6] = [ 0, 1, 2, 2, 1, 3 ]; + let quad_vertices = [ + PackedVertex { + pos: [x0, y0], + }, + PackedVertex { + pos: [x1, y0], + }, + PackedVertex { + pos: [x0, y1], + }, + PackedVertex { + pos: [x1, y1], + }, + ]; + + let quad_vao_id = device.create_vao(VertexFormat::Triangles, None); + device.bind_vao(quad_vao_id); + device.update_vao_indices(quad_vao_id, &quad_indices, VertexUsageHint::Static); + device.update_vao_main_vertices(quad_vao_id, &quad_vertices, VertexUsageHint::Static); device.end_frame(); @@ -273,6 +439,10 @@ impl Renderer { // texture ids let context_handle = NativeGLContext::current_handle(); + let config = FrameBuilderConfig::new(max_prim_layers, + max_prim_tiles); + + let debug = options.debug; let (device_pixel_ratio, enable_aa) = (options.device_pixel_ratio, options.enable_aa); let payload_tx_for_backend = payload_tx.clone(); thread::spawn(move || { @@ -281,12 +451,12 @@ impl Renderer { payload_tx_for_backend, result_tx, device_pixel_ratio, - white_image_id, - dummy_mask_image_id, texture_cache, enable_aa, backend_notifier, - context_handle); + context_handle, + config, + debug); backend.run(); }); @@ -294,32 +464,40 @@ impl Renderer { result_rx: result_rx, device: device, current_frame: None, - vertex_buffers: HashMap::with_hasher(Default::default()), raster_batches: Vec::new(), - quad_vertex_buffer: None, - simple_triangles_vao: None, raster_op_vao: None, - cached_quad_vaos: Vec::new(), pending_texture_updates: Vec::new(), - pending_batch_updates: Vec::new(), pending_shader_updates: Vec::new(), - border_program_id: border_program_id, device_pixel_ratio: options.device_pixel_ratio, - blend_program_id: blend_program_id, - filter_program_id: filter_program_id, - quad_program_id: quad_program_id, - blit_program_id: blit_program_id, box_shadow_program_id: box_shadow_program_id, blur_program_id: blur_program_id, - mask_program_id: mask_program_id, - u_blend_params: UniformLocation::invalid(), - u_filter_params: UniformLocation::invalid(), + tile_clear_shader: tile_clear_shader, + ps_rectangle: ps_rectangle, + ps_rectangle_clip: ps_rectangle_clip, + ps_image_clip: ps_image_clip, + ps_text: ps_text, + ps_image: ps_image, + ps_border: ps_border, + ps_box_shadow: ps_box_shadow, + ps_blend: ps_blend, + ps_composite: ps_composite, + ps_aligned_gradient: ps_aligned_gradient, + ps_angle_gradient: ps_angle_gradient, + ps_rectangle_transform: ps_rectangle_transform, + ps_image_transform: ps_image_transform, + max_clear_tiles: max_clear_tiles, + max_prim_rectangles: max_prim_rectangles, + max_prim_rectangles_clip: max_prim_rectangles_clip, + max_prim_texts: max_prim_texts, + max_prim_images: max_prim_images, + max_prim_images_clip: max_prim_images_clip, + max_prim_borders: max_prim_borders, + max_prim_box_shadows: max_prim_box_shadows, + max_prim_blends: max_prim_blends, + max_prim_composites: max_prim_composites, + max_prim_aligned_gradients: max_prim_aligned_gradients, + max_prim_angle_gradients: max_prim_angle_gradients, u_direction: UniformLocation::invalid(), - u_quad_offset_array: UniformLocation::invalid(), - u_quad_transform_array: UniformLocation::invalid(), - u_atlas_params: UniformLocation::invalid(), - u_tile_params: UniformLocation::invalid(), - u_clip_rects: UniformLocation::invalid(), notifier: notifier, debug: debug_renderer, backend_profile_counters: BackendProfileCounters::new(), @@ -330,9 +508,11 @@ impl Renderer { last_time: 0, raster_op_target_a8: raster_op_target_a8, raster_op_target_rgba8: raster_op_target_rgba8, - temporary_fb_texture: temporary_fb_texture, + render_targets: [TextureId(0), TextureId(0)], max_raster_op_size: max_raster_op_size, - gpu_profile: GpuProfile::new(), + gpu_profile_paint: GpuProfile::new(), + gpu_profile_composite: GpuProfile::new(), + quad_vao_id: quad_vao_id, }; renderer.update_uniform_locations(); @@ -357,13 +537,6 @@ impl Renderer { } fn update_uniform_locations(&mut self) { - self.u_quad_transform_array = self.device.get_uniform_location(self.quad_program_id, "uMatrixPalette"); - self.u_quad_offset_array = self.device.get_uniform_location(self.quad_program_id, "uOffsets"); - self.u_tile_params = self.device.get_uniform_location(self.quad_program_id, "uTileParams"); - self.u_clip_rects = self.device.get_uniform_location(self.quad_program_id, "uClipRects"); - self.u_atlas_params = self.device.get_uniform_location(self.quad_program_id, "uAtlasParams"); - self.u_blend_params = self.device.get_uniform_location(self.blend_program_id, "uBlendParams"); - self.u_filter_params = self.device.get_uniform_location(self.filter_program_id, "uFilterParams"); self.u_direction = self.device.get_uniform_location(self.blur_program_id, "uDirection"); } @@ -393,9 +566,8 @@ impl Renderer { ResultMsg::UpdateTextureCache(update_list) => { self.pending_texture_updates.push(update_list); } - ResultMsg::NewFrame(frame, update_list, profile_counters) => { + ResultMsg::NewFrame(frame, profile_counters) => { self.backend_profile_counters = profile_counters; - self.pending_batch_updates.push(update_list); self.current_frame = Some(frame); } ResultMsg::RefreshShader(path) => { @@ -412,17 +584,20 @@ impl Renderer { pub fn render(&mut self, framebuffer_size: Size2D) { let mut profile_timers = RendererProfileTimers::new(); - self.gpu_profile.begin(); + // Block CPU waiting for last frame's GPU profiles to arrive. + // In general this shouldn't block unless heavily GPU limited. + let paint_ns = self.gpu_profile_paint.get(); + let composite_ns = self.gpu_profile_composite.get(); profile_timers.cpu_time.profile(|| { self.device.begin_frame(); gl::disable(gl::SCISSOR_TEST); - gl::clear_color(1.0, 1.0, 1.0, 0.0); + //gl::clear_color(1.0, 1.0, 1.0, 0.0); + //gl::clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT); - self.update_shaders(); + //self.update_shaders(); self.update_texture_cache(); - self.update_batches(); self.draw_frame(framebuffer_size); }); @@ -430,8 +605,11 @@ impl Renderer { let ns = current_time - self.last_time; self.profile_counters.frame_time.set(ns); - let gpu_ns = self.gpu_profile.end(); - profile_timers.gpu_time.set(gpu_ns); + profile_timers.gpu_time_paint.set(paint_ns); + profile_timers.gpu_time_composite.set(composite_ns); + + let gpu_ns = paint_ns + composite_ns; + profile_timers.gpu_time_total.set(gpu_ns); if self.enable_profiler { self.profiler.draw_profile(&self.backend_profile_counters, @@ -443,8 +621,8 @@ impl Renderer { self.profile_counters.reset(); self.profile_counters.frame_counter.inc(); - let debug_size = Size2D::new((framebuffer_size.width as f32 / self.device_pixel_ratio) as u32, - (framebuffer_size.height as f32 / self.device_pixel_ratio) as u32); + let debug_size = Size2D::new(framebuffer_size.width as u32, + framebuffer_size.height as u32); self.debug.render(&mut self.device, &debug_size); self.device.end_frame(); self.last_time = current_time; @@ -457,67 +635,20 @@ impl Renderer { } } - fn update_batches(&mut self) { - let mut pending_batch_updates = mem::replace(&mut self.pending_batch_updates, vec![]); - for update_list in pending_batch_updates.drain(..) { - for update in update_list.updates { - match update.op { - BatchUpdateOp::Create(vertices) => { - if self.quad_vertex_buffer.is_none() { - self.quad_vertex_buffer = Some(self.device.create_quad_vertex_buffer()) - } - - let vao_id = match self.cached_quad_vaos.pop() { - Some(quad_vao_id) => quad_vao_id, - None => { - self.device.create_vao(VertexFormat::Rectangles, - Some(self.quad_vertex_buffer.unwrap())) - } - }; - - self.device.bind_vao(vao_id); - - self.device.update_vao_aux_vertices(vao_id, - &vertices, - VertexUsageHint::Static); - - self.vertex_buffers.insert(update.id, vec![ - VertexBufferAndOffset { - buffer: VertexBuffer { - vao_id: vao_id, - }, - offset: 0, - } - ]); - } - BatchUpdateOp::Destroy => { - let vertex_buffers_and_offsets = - self.vertex_buffers.remove(&update.id).unwrap(); - for vertex_buffer_and_offset in vertex_buffers_and_offsets.into_iter() { - if self.cached_quad_vaos.len() < MAX_CACHED_QUAD_VAOS && - vertex_buffer_and_offset.offset == 0 { - self.cached_quad_vaos.push(vertex_buffer_and_offset.buffer.vao_id); - } else { - self.device.delete_vao(vertex_buffer_and_offset.buffer.vao_id); - } - } - } - } - } - } - } - +/* fn update_shaders(&mut self) { let update_uniforms = !self.pending_shader_updates.is_empty(); for path in self.pending_shader_updates.drain(..) { - self.device.refresh_shader(path); + panic!("todo"); + //self.device.refresh_shader(path); } if update_uniforms { self.update_uniform_locations(); } } +*/ fn update_texture_cache(&mut self) { let mut pending_texture_updates = mem::replace(&mut self.pending_texture_updates, vec![]); @@ -713,108 +844,6 @@ impl Renderer { ] }); } - TextureUpdateDetails::BorderRadius(outer_rx, - outer_ry, - inner_rx, - inner_ry, - index, - inverted, - border_type) => { - // From here on out everything is in device coordinates. - let border_program_id = self.border_program_id; - let color = if inverted { - ColorF::new(0.0, 0.0, 0.0, 1.0) - } else { - ColorF::new(1.0, 1.0, 1.0, 1.0) - }; - - let border_radii_outer = Point2D::new(outer_rx.as_f32(), outer_ry.as_f32()); - let border_radii_inner = Point2D::new(inner_rx.as_f32(), inner_ry.as_f32()); - - let zero_point = Point2D::new(0.0, 0.0); - let zero_size = Size2D::new(0.0, 0.0); - - self.add_rect_to_raster_batch(update.id, - TextureId(0), - border_program_id, - None, - &Rect::new(Point2D::new(x as u32, y as u32), - Size2D::new(width as u32, height as u32)), - border_type, - |texture_rect| { - let border_radii_outer_size = - Size2D::new(border_radii_outer.x, - border_radii_outer.y); - let border_radii_inner_size = - Size2D::new(border_radii_inner.x, - border_radii_inner.y); - let untessellated_rect = - Rect::new(texture_rect.origin, border_radii_outer_size); - let tessellated_rect = - match index { - None => untessellated_rect, - Some(index) => { - untessellated_rect.tessellate_border_corner( - &border_radii_outer_size, - &border_radii_inner_size, - 1.0, - BasicRotationAngle::Upright, - index) - } - }; - - let border_position = - untessellated_rect.bottom_right() - - (tessellated_rect.origin - texture_rect.origin); - - [ - PackedVertexForTextureCacheUpdate::new( - &texture_rect.origin, - &color, - &zero_point, - &border_radii_outer, - &border_radii_inner, - &border_position, - &zero_point, - &zero_size, - &zero_size, - 0.0), - PackedVertexForTextureCacheUpdate::new( - &texture_rect.top_right(), - &color, - &zero_point, - &border_radii_outer, - &border_radii_inner, - &border_position, - &zero_point, - &zero_size, - &zero_size, - 0.0), - PackedVertexForTextureCacheUpdate::new( - &texture_rect.bottom_left(), - &color, - &zero_point, - &border_radii_outer, - &border_radii_inner, - &border_position, - &zero_point, - &zero_size, - &zero_size, - 0.0), - PackedVertexForTextureCacheUpdate::new( - &texture_rect.bottom_right(), - &color, - &zero_point, - &border_radii_outer, - &border_radii_inner, - &border_position, - &zero_point, - &zero_size, - &zero_size, - 0.0), - ] - }); - } TextureUpdateDetails::BoxShadow(blur_radius, border_radius, box_rect_size, @@ -1153,728 +1182,478 @@ impl Renderer { } } - fn compute_layer_viewport_in_render_target_space(&self, layer: &DrawLayer) -> Rect { - let layer_origin = Point2D::new(layer.origin.x * self.device_pixel_ratio, - layer.origin.y * self.device_pixel_ratio); - let layer_size = Size2D::new(layer.size.width * self.device_pixel_ratio, - layer.size.height * self.device_pixel_ratio); - Rect::new(layer_origin, layer_size) + fn add_debug_rect(&mut self, + p0: Point2D, + p1: Point2D, + label: &str, + c: &ColorF) { + let tile_x0 = p0.x; + let tile_y0 = p0.y; + let tile_x1 = p1.x; + let tile_y1 = p1.y; + + self.debug.add_line(tile_x0, + tile_y0, + c, + tile_x1, + tile_y0, + c); + self.debug.add_line(tile_x0, + tile_y1, + c, + tile_x1, + tile_y1, + c); + self.debug.add_line(tile_x0, + tile_y0, + c, + tile_x0, + tile_y1, + c); + self.debug.add_line(tile_x1, + tile_y0, + c, + tile_x1, + tile_y1, + c); + if label.len() > 0 { + self.debug.add_text((tile_x0.0 as f32 + tile_x1.0 as f32) * 0.5, + (tile_y0.0 as f32 + tile_y1.0 as f32) * 0.5, + label, + c); + } } - fn calculate_layer_viewports(&self, - layer: &DrawLayer, - render_context: &RenderContext, - viewport: &Rect, - layer_viewports: &mut HashMap>) { - layer_viewports.insert(layer.id, *viewport); - - let mut indices_of_composited_children = BitSet::new(); - for command in &layer.commands { - match *command { - DrawCommand::CompositeBatch(ref info) => { - for job in &info.jobs { - indices_of_composited_children.insert(job.child_layer_index.0 as usize); - let child_layer = &layer.child_layers[job.child_layer_index.0 as usize]; - if let Some(ref new_viewport) = job.world_transform - .transform_rect(&job.rect) - .intersection(viewport) { - self.calculate_layer_viewports(child_layer, - render_context, - new_viewport, - layer_viewports) - } - } - } - DrawCommand::Batch(_) | DrawCommand::Clear(_) => {} - } + fn draw_target(&mut self, + render_target: Option, + target: &RenderTarget, + target_size: &Size2D, + cache_texture: TextureId, + should_clear: bool) { + self.device.bind_render_target(render_target); + gl::viewport(0, + 0, + target_size.width as i32, + target_size.height as i32); + + gl::disable(gl::BLEND); + + // TODO(gw): oops! + self.device.bind_cache_texture(cache_texture); + for i in 0..8 { + self.device.bind_layer_texture(i, cache_texture); } - for (index, child_layer) in layer.child_layers.iter().enumerate() { - if !indices_of_composited_children.contains(index) { - // FIXME(pcwalton): This is probably wrong for nested composites. - let layer_viewport = - self.compute_layer_viewport_in_render_target_space(child_layer); - println!("calculate_layer_viewports: child layer_viewport={:?}", layer_viewport); - if let Some(ref new_viewport) = layer_viewport.intersection(viewport) { - self.calculate_layer_viewports(child_layer, - render_context, - new_viewport, - layer_viewports) - } + let projection = match render_target { + Some(..) => { + // todo(gw): remove me! + gl::clear_color(0.0, 0.0, 0.0, 0.0); + + Matrix4D::ortho(0.0, + target_size.width as f32, + 0.0, + target_size.height as f32, + ORTHO_NEAR_PLANE, + ORTHO_FAR_PLANE) } - } - } + None => { + // todo(gw): remove me! + gl::clear_color(1.0, 1.0, 1.0, 1.0); + + Matrix4D::ortho(0.0, + target_size.width as f32, + target_size.height as f32, + 0.0, + ORTHO_NEAR_PLANE, + ORTHO_FAR_PLANE) + } + }; - fn draw_layer(&mut self, - layer: &DrawLayer, - render_context: &RenderContext, - layer_viewports: &HashMap>) { - // Draw child layers first, to ensure that dependent render targets - // have been built before they are read as a texture. - for child in &layer.child_layers { - self.draw_layer(child, render_context, layer_viewports) + // todo(gw): remove me! + if should_clear { + gl::clear(gl::COLOR_BUFFER_BIT); } - if !layer_viewports.contains_key(&layer.id) { - // Off screen. - return - } + for batcher in &target.alpha_batchers { + let layer_ubos = gl::gen_buffers(batcher.layer_ubos.len() as i32); + let tile_ubos = gl::gen_buffers(batcher.tile_ubos.len() as i32); - self.device.bind_render_target(layer.texture_id); + for (ubo_data, ubo_id) in batcher.layer_ubos + .iter() + .zip(layer_ubos.iter()) { + gl::bind_buffer(gl::UNIFORM_BUFFER, *ubo_id); + gl::buffer_data(gl::UNIFORM_BUFFER, ubo_data, gl::STATIC_DRAW); + } - // TODO(gw): This may not be needed in all cases... - let layer_viewport = self.compute_layer_viewport_in_render_target_space(layer); - let layer_viewport = - Rect::new(Point2D::new(layer_viewport.origin.x.round() as gl::GLint, - layer_viewport.origin.y.round() as gl::GLint), - Size2D::new(layer_viewport.size.width.round() as gl::GLint, - layer_viewport.size.height.round() as gl::GLint)); + for (ubo_data, ubo_id) in batcher.tile_ubos + .iter() + .zip(tile_ubos.iter()) { + gl::bind_buffer(gl::UNIFORM_BUFFER, *ubo_id); + gl::buffer_data(gl::UNIFORM_BUFFER, ubo_data, gl::STATIC_DRAW); + } - set_scissor_or_viewport(&layer_viewport, render_context, layer.texture_id, gl::scissor); - set_scissor_or_viewport(&layer_viewport, render_context, layer.texture_id, gl::viewport); + gl::enable(gl::BLEND); + gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); + gl::blend_equation(gl::FUNC_ADD); + + for batch in &batcher.batches { + match &batch.data { + &PrimitiveBatchData::Blend(..) => {} + &PrimitiveBatchData::Composite(..) => {} + _ => { + gl::bind_buffer_base(gl::UNIFORM_BUFFER, UBO_BIND_LAYERS, layer_ubos[batch.layer_ubo_index]); + gl::bind_buffer_base(gl::UNIFORM_BUFFER, UBO_BIND_PRIM_TILES, tile_ubos[batch.tile_ubo_index]); + } + } - gl::clear_color(1.0, 1.0, 1.0, 0.0); - gl::clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT); + match &batch.data { + &PrimitiveBatchData::Blend(ref ubo_data) => { + self.device.bind_program(self.ps_blend, &projection); + self.device.bind_vao(self.quad_vao_id); - let projection = Matrix4D::ortho(0.0, - layer.size.width, - layer.size.height, - 0.0, - ORTHO_NEAR_PLANE, - ORTHO_FAR_PLANE); + for chunk in ubo_data.chunks(self.max_prim_blends) { + let ubos = gl::gen_buffers(1); + let ubo = ubos[0]; - for cmd in &layer.commands { - match cmd { - &DrawCommand::Clear(ref info) => { - let mut clear_bits = 0; - if info.clear_color { - clear_bits |= gl::COLOR_BUFFER_BIT; - } - if info.clear_z { - clear_bits |= gl::DEPTH_BUFFER_BIT; - } - if info.clear_stencil { - clear_bits |= gl::STENCIL_BUFFER_BIT; - } - gl::clear(clear_bits); - } - &DrawCommand::Batch(ref info) => { - // TODO: probably worth sorting front to back to minimize overdraw (if profiling shows fragment / rop bound) - - self.enable_msaa(true); - - gl::enable(gl::BLEND); - gl::blend_func_separate(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA, - gl::ONE, gl::ONE); - gl::blend_equation(gl::FUNC_ADD); - - self.device.bind_program(self.quad_program_id, &projection); - - if !info.offset_palette.is_empty() { - // TODO(gw): Avoid alloc here... - let mut floats = Vec::new(); - for vec in &info.offset_palette { - floats.push(vec.stacking_context_x0); - floats.push(vec.stacking_context_y0); - floats.push(vec.render_target_x0); - floats.push(vec.render_target_y0); - } + gl::bind_buffer(gl::UNIFORM_BUFFER, ubo); + gl::buffer_data(gl::UNIFORM_BUFFER, &chunk, gl::STATIC_DRAW); + gl::bind_buffer_base(gl::UNIFORM_BUFFER, UBO_BIND_CACHE_ITEMS, ubo); - self.device.set_uniform_vec4_array(self.u_quad_offset_array, - &floats); + self.device.draw_indexed_triangles_instanced_u16(6, chunk.len() as gl::GLint); + self.profile_counters.vertices.add(6 * chunk.len()); + self.profile_counters.draw_calls.inc(); + + gl::delete_buffers(&ubos); + } } + &PrimitiveBatchData::Composite(ref ubo_data) => { + self.device.bind_program(self.ps_composite, &projection); + self.device.bind_vao(self.quad_vao_id); - self.device.set_uniform_mat4_array(self.u_quad_transform_array, - &info.matrix_palette); - - // Render any masks to the stencil buffer. - for region in &info.regions { - let mut valid_mask_count = 0; - let mut scissor_rect = layer_viewport; - for mask in ®ion.masks { - - // If we can represent the mask by just adjusting the scissor rect, - // don't draw it. - if mask.transform - .can_losslessly_transform_and_perspective_project_a_2d_rect() { - // Convert the mask rect to 4D points. - let top_left = to_point4d(&mask.rect.origin); - let top_right = to_point4d(&mask.rect.top_right()); - let bottom_right = to_point4d(&mask.rect.bottom_right()); - let bottom_left = to_point4d(&mask.rect.bottom_left()); - - // Transform in normalized device coordinates. - let transform = projection.mul(&mask.transform); - let top_left = - transform.transform_point_and_perspective_project(&top_left); - let top_right = - transform.transform_point_and_perspective_project(&top_right); - let bottom_right = - transform.transform_point_and_perspective_project( - &bottom_right); - let bottom_left = - transform.transform_point_and_perspective_project( - &bottom_left); - let transformed_mask_rect = Rect::from_points(&top_left, - &top_right, - &bottom_right, - &bottom_left); - - // Convert to window coordinates. - let transformed_mask_origin = Point2D::new( - (transformed_mask_rect.origin.x + 1.0) / 2.0 * - layer_viewport.size.width as f32 + - layer_viewport.origin.x as f32, - (1.0 - transformed_mask_rect.size.height - - transformed_mask_rect.origin.y) / 2.0 * - layer_viewport.size.height as f32 + - layer_viewport.origin.y as f32); - let transformed_mask_size = Size2D::new( - transformed_mask_rect.size.width / 2.0 * - layer_viewport.size.width as f32, - transformed_mask_rect.size.height / 2.0 * - layer_viewport.size.height as f32); - let transformed_mask_rect = Rect::new(transformed_mask_origin, - transformed_mask_size); - - // Round and convert to integers. - let transformed_mask_origin = - Point2D::new(transformed_mask_rect.origin.x.round() as i32, - transformed_mask_rect.origin.y.round() as i32); - let transformed_mask_size = - Size2D::new(transformed_mask_rect.size.width.round() as i32, - transformed_mask_rect.size.height.round() as i32); - let transformed_mask_rect = Rect::new(transformed_mask_origin, - transformed_mask_size); - - scissor_rect = transformed_mask_rect.intersection(&scissor_rect) - .unwrap_or(Rect::zero()); - continue - } + for chunk in ubo_data.chunks(self.max_prim_composites) { + let ubos = gl::gen_buffers(1); + let ubo = ubos[0]; - // First time we find a valid mask, clear stencil and setup render states - if valid_mask_count == 0 { - // TODO(pcwalton): Don't clear stencil if already clear!!! - gl::clear(gl::STENCIL_BUFFER_BIT); - gl::enable(gl::STENCIL_TEST); - gl::color_mask(false, false, false, false); - gl::depth_mask(false); - gl::stencil_mask(0xff); - gl::stencil_func(gl::ALWAYS, 1, 0xff); - gl::stencil_op(gl::KEEP, gl::INCR, gl::INCR) - } + gl::bind_buffer(gl::UNIFORM_BUFFER, ubo); + gl::buffer_data(gl::UNIFORM_BUFFER, &chunk, gl::STATIC_DRAW); + gl::bind_buffer_base(gl::UNIFORM_BUFFER, UBO_BIND_CACHE_ITEMS, ubo); - // TODO(gw): The below is a copy pasta and can be trivially optimized. - let (mut indices, mut vertices) = (vec![], vec![]); - indices.push(0); - indices.push(1); - indices.push(2); - indices.push(2); - indices.push(3); - indices.push(1); - - let color = ColorF::new(0.0, 0.0, 0.0, 0.0); - - let x0 = mask.rect.origin.x; - let y0 = mask.rect.origin.y; - let x1 = x0 + mask.rect.size.width; - let y1 = y0 + mask.rect.size.height; - - vertices.extend_from_slice(&[ - PackedVertex::from_components( - x0, y0, - &color, - 0.0, 0.0, - 0.0, 0.0), - PackedVertex::from_components( - x1, y0, - &color, - 1.0, 0.0, - 1.0, 0.0), - PackedVertex::from_components( - x0, y1, - &color, - 0.0, 1.0, - 0.0, 1.0), - PackedVertex::from_components( - x1, y1, - &color, - 1.0, 1.0, - 1.0, 1.0), - ]); - - let wvp = projection.mul(&mask.transform); - self.device.bind_program(self.mask_program_id, &wvp); - - draw_simple_triangles(&mut self.simple_triangles_vao, - &mut self.device, - &mut self.profile_counters, - &indices[..], - &vertices[..], - TextureId(0)); - - valid_mask_count += 1; - } + self.device.draw_indexed_triangles_instanced_u16(6, chunk.len() as gl::GLint); + self.profile_counters.vertices.add(6 * chunk.len()); + self.profile_counters.draw_calls.inc(); - // If any masks were found, enable stencil test rejection. - // TODO(gw): This may be faster to switch the logic and - // rely on sfail! - if valid_mask_count > 0 { - gl::stencil_op(gl::KEEP, gl::KEEP, gl::KEEP); - gl::stencil_func(gl::EQUAL, valid_mask_count, 0xff); - gl::color_mask(true, true, true, true); - gl::depth_mask(true); - self.device.bind_program(self.quad_program_id, - &projection); + gl::delete_buffers(&ubos); } + } + &PrimitiveBatchData::Rectangles(ref ubo_data) => { + let shader = match batch.transform_kind { + TransformedRectKind::AxisAligned => self.ps_rectangle, + TransformedRectKind::Complex => self.ps_rectangle_transform, + }; + self.device.bind_program(shader, &projection); + self.device.bind_vao(self.quad_vao_id); - // If the scissor rect was updated, scissor. - if scissor_rect != layer_viewport { - set_scissor_or_viewport(&scissor_rect, - render_context, - layer.texture_id, - gl::scissor) - } + for chunk in ubo_data.chunks(self.max_prim_rectangles) { + let ubos = gl::gen_buffers(1); + let ubo = ubos[0]; - for draw_call in ®ion.draw_calls { - let vao_id = self.get_or_create_similar_vao_with_offset( - draw_call.vertex_buffer_id, - VertexFormat::Rectangles, - draw_call.first_instance); - self.device.bind_vao(vao_id); - - if !draw_call.tile_params.is_empty() { - // TODO(gw): Avoid alloc here... - let mut floats = Vec::new(); - for vec in &draw_call.tile_params { - floats.push(vec.u0); - floats.push(vec.v0); - floats.push(vec.u_size); - floats.push(vec.v_size); - } - - self.device.set_uniform_vec4_array(self.u_tile_params, - &floats); - } + gl::bind_buffer(gl::UNIFORM_BUFFER, ubo); + gl::buffer_data(gl::UNIFORM_BUFFER, &chunk, gl::STATIC_DRAW); + gl::bind_buffer_base(gl::UNIFORM_BUFFER, UBO_BIND_CACHE_ITEMS, ubo); - if !draw_call.clip_rects.is_empty() { - // TODO(gw): Avoid alloc here... - let mut floats = Vec::new(); - for rect in &draw_call.clip_rects { - floats.push(rect.origin.x); - floats.push(rect.origin.y); - floats.push(rect.origin.x + rect.size.width); - floats.push(rect.origin.y + rect.size.height); - } - - self.device.set_uniform_vec4_array(self.u_clip_rects, - &floats); - } + self.device.draw_indexed_triangles_instanced_u16(6, chunk.len() as gl::GLint); + self.profile_counters.vertices.add(6 * chunk.len()); + self.profile_counters.draw_calls.inc(); - self.device.bind_mask_texture(draw_call.mask_texture_id); - self.device.bind_color_texture(draw_call.color_texture_id); - - // TODO(gw): Although a minor cost, this is an extra hashtable lookup for every - // draw call, when the batch textures are (almost) always the same. - // This could probably be cached or provided elsewhere. - let color_size = self.device - .get_texture_dimensions(draw_call.color_texture_id); - let mask_size = self.device - .get_texture_dimensions(draw_call.mask_texture_id); - self.device.set_uniform_4f(self.u_atlas_params, - color_size.0 as f32, - color_size.1 as f32, - mask_size.0 as f32, - mask_size.1 as f32); + gl::delete_buffers(&ubos); + } + } + &PrimitiveBatchData::RectanglesClip(ref ubo_data) => { + self.device.bind_program(self.ps_rectangle_clip, &projection); + self.device.bind_vao(self.quad_vao_id); + + for chunk in ubo_data.chunks(self.max_prim_rectangles_clip) { + let ubos = gl::gen_buffers(1); + let ubo = ubos[0]; + gl::bind_buffer(gl::UNIFORM_BUFFER, ubo); + gl::buffer_data(gl::UNIFORM_BUFFER, &chunk, gl::STATIC_DRAW); + gl::bind_buffer_base(gl::UNIFORM_BUFFER, UBO_BIND_CACHE_ITEMS, ubo); + + self.device.draw_indexed_triangles_instanced_u16(6, chunk.len() as gl::GLint); + self.profile_counters.vertices.add(6 * chunk.len()); self.profile_counters.draw_calls.inc(); - self.device - .draw_triangles_instanced_u16(0, 6, draw_call.instance_count as i32); + gl::delete_buffers(&ubos); } + } + &PrimitiveBatchData::Image(ref ubo_data) => { + let shader = match batch.transform_kind { + TransformedRectKind::AxisAligned => self.ps_image, + TransformedRectKind::Complex => self.ps_image_transform, + }; + self.device.bind_program(shader, &projection); + self.device.bind_vao(self.quad_vao_id); + self.device.bind_color_texture(batch.color_texture_id); - // Disable stencil test if it was used - if valid_mask_count > 0 { - gl::disable(gl::STENCIL_TEST); - } + for chunk in ubo_data.chunks(self.max_prim_images) { + let ubos = gl::gen_buffers(1); + let ubo = ubos[0]; + + gl::bind_buffer(gl::UNIFORM_BUFFER, ubo); + gl::buffer_data(gl::UNIFORM_BUFFER, &chunk, gl::STATIC_DRAW); + gl::bind_buffer_base(gl::UNIFORM_BUFFER, UBO_BIND_CACHE_ITEMS, ubo); - // Reset scissor if it was used. - if scissor_rect != layer_viewport { - set_scissor_or_viewport(&layer_viewport, - render_context, - layer.texture_id, - gl::scissor); + self.device.draw_indexed_triangles_instanced_u16(6, chunk.len() as gl::GLint); + self.profile_counters.vertices.add(6 * chunk.len()); + self.profile_counters.draw_calls.inc(); + + gl::delete_buffers(&ubos); } } - } - &DrawCommand::CompositeBatch(ref info) => { - let mut any_jobs_were_visible = false; - for job in &info.jobs { - let ChildLayerIndex(child_layer_index) = job.child_layer_index; - let src_target = &layer.child_layers[child_layer_index as usize]; - if layer_viewports.contains_key(&src_target.id) { - any_jobs_were_visible = true; - break + &PrimitiveBatchData::ImageClip(ref ubo_data) => { + self.device.bind_program(self.ps_image_clip, &projection); + self.device.bind_vao(self.quad_vao_id); + self.device.bind_color_texture(batch.color_texture_id); + + for chunk in ubo_data.chunks(self.max_prim_images_clip) { + let ubos = gl::gen_buffers(1); + let ubo = ubos[0]; + + gl::bind_buffer(gl::UNIFORM_BUFFER, ubo); + gl::buffer_data(gl::UNIFORM_BUFFER, &chunk, gl::STATIC_DRAW); + gl::bind_buffer_base(gl::UNIFORM_BUFFER, UBO_BIND_CACHE_ITEMS, ubo); + + self.device.draw_indexed_triangles_instanced_u16(6, chunk.len() as gl::GLint); + self.profile_counters.vertices.add(6 * chunk.len()); + self.profile_counters.draw_calls.inc(); + + gl::delete_buffers(&ubos); } } - if !any_jobs_were_visible { - continue; - } + &PrimitiveBatchData::Borders(ref ubo_data) => { + self.device.bind_program(self.ps_border, &projection); + self.device.bind_vao(self.quad_vao_id); - let needs_fb = info.operation.needs_framebuffer(); - - let alpha; - if needs_fb { - match info.operation { - CompositionOp::MixBlend(blend_mode) => { - gl::disable(gl::BLEND); - self.device.bind_program(render_context.blend_program_id, - &projection); - self.device.set_uniform_4f(self.u_blend_params, - blend_mode as i32 as f32, - 0.0, - 0.0, - 0.0); - } - _ => unreachable!(), - } - self.device.bind_mask_texture(self.temporary_fb_texture); - alpha = 1.0; - } else { - gl::enable(gl::BLEND); - - let program; - let mut filter_params = None; - match info.operation { - CompositionOp::Filter(LowLevelFilterOp::Brightness( - amount)) => { - gl::blend_func(gl::CONSTANT_COLOR, gl::ZERO); - gl::blend_equation(gl::FUNC_ADD); - gl::blend_color(amount.to_f32_px(), - amount.to_f32_px(), - amount.to_f32_px(), - 1.0); - alpha = 1.0; - program = self.blit_program_id; - } - CompositionOp::Filter(LowLevelFilterOp::Opacity(amount)) => { - gl::blend_func_separate(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA, - gl::ONE, gl::ONE); - gl::blend_equation(gl::FUNC_ADD); - alpha = amount.to_f32_px(); - program = self.blit_program_id; - } - CompositionOp::Filter(filter_op) => { - alpha = 1.0; - program = render_context.filter_program_id; - - let (opcode, amount, param0, param1) = match filter_op { - LowLevelFilterOp::Blur(radius, - AxisDirection::Horizontal) => { - gl::blend_func_separate(gl::SRC_ALPHA, - gl::ONE_MINUS_SRC_ALPHA, - gl::ONE, - gl::ONE); - (0.0, - radius.to_f32_px() * self.device_pixel_ratio, - 1.0, - 0.0) - } - LowLevelFilterOp::Blur(radius, - AxisDirection::Vertical) => { - gl::blend_func_separate(gl::SRC_ALPHA, - gl::ONE_MINUS_SRC_ALPHA, - gl::ONE, - gl::ONE); - (0.0, - radius.to_f32_px() * self.device_pixel_ratio, - 0.0, - 1.0) - } - LowLevelFilterOp::Contrast(amount) => { - gl::disable(gl::BLEND); - (1.0, amount.to_f32_px(), 0.0, 0.0) - } - LowLevelFilterOp::Grayscale(amount) => { - gl::disable(gl::BLEND); - (2.0, amount.to_f32_px(), 0.0, 0.0) - } - LowLevelFilterOp::HueRotate(angle) => { - gl::disable(gl::BLEND); - (3.0, - (angle as f32) / ANGLE_FLOAT_TO_FIXED, - 0.0, - 0.0) - } - LowLevelFilterOp::Invert(amount) => { - gl::disable(gl::BLEND); - (4.0, amount.to_f32_px(), 0.0, 0.0) - } - LowLevelFilterOp::Saturate(amount) => { - gl::disable(gl::BLEND); - (5.0, amount.to_f32_px(), 0.0, 0.0) - } - LowLevelFilterOp::Sepia(amount) => { - gl::disable(gl::BLEND); - (6.0, amount.to_f32_px(), 0.0, 0.0) - } - LowLevelFilterOp::Brightness(_) | - LowLevelFilterOp::Opacity(_) => { - // Expressible using GL blend modes, so not handled - // here. - unreachable!() - } - }; - - filter_params = Some((opcode, amount, param0, param1)); - } - CompositionOp::MixBlend(MixBlendMode::Multiply) => { - gl::blend_func(gl::DST_COLOR, gl::ZERO); - gl::blend_equation(gl::FUNC_ADD); - program = self.blit_program_id; - alpha = 1.0; - } - CompositionOp::MixBlend(MixBlendMode::Darken) => { - gl::blend_func(gl::ONE, gl::ONE); - gl::blend_equation(gl::MIN); - program = self.blit_program_id; - alpha = 1.0; - } - CompositionOp::MixBlend(MixBlendMode::Lighten) => { - gl::blend_func(gl::ONE, gl::ONE); - gl::blend_equation(gl::MAX); - program = self.blit_program_id; - alpha = 1.0; - } - _ => unreachable!(), - } + for chunk in ubo_data.chunks(self.max_prim_borders) { + let ubos = gl::gen_buffers(1); + let ubo = ubos[0]; - self.device.bind_program(program, &projection); + gl::bind_buffer(gl::UNIFORM_BUFFER, ubo); + gl::buffer_data(gl::UNIFORM_BUFFER, &chunk, gl::STATIC_DRAW); + gl::bind_buffer_base(gl::UNIFORM_BUFFER, UBO_BIND_CACHE_ITEMS, ubo); + + self.device.draw_indexed_triangles_instanced_u16(6, chunk.len() as gl::GLint); + self.profile_counters.vertices.add(6 * chunk.len()); + self.profile_counters.draw_calls.inc(); - if let Some(ref filter_params) = filter_params { - self.device.set_uniform_4f(self.u_filter_params, - filter_params.0, - filter_params.1, - filter_params.2, - filter_params.3); + gl::delete_buffers(&ubos); } } + &PrimitiveBatchData::BoxShadows(ref ubo_data) => { + self.device.bind_program(self.ps_box_shadow, &projection); + self.device.bind_vao(self.quad_vao_id); - let (mut indices, mut vertices) = (vec![], vec![]); - for job in &info.jobs { - let ChildLayerIndex(child_layer_index) = job.child_layer_index; - let src_target = &layer.child_layers[child_layer_index as usize]; - if !layer_viewports.contains_key(&src_target.id) { - continue + for chunk in ubo_data.chunks(self.max_prim_box_shadows) { + let ubos = gl::gen_buffers(1); + let ubo = ubos[0]; + + gl::bind_buffer(gl::UNIFORM_BUFFER, ubo); + gl::buffer_data(gl::UNIFORM_BUFFER, &chunk, gl::STATIC_DRAW); + gl::bind_buffer_base(gl::UNIFORM_BUFFER, UBO_BIND_CACHE_ITEMS, ubo); + + self.device.draw_indexed_triangles_instanced_u16(6, chunk.len() as gl::GLint); + self.profile_counters.vertices.add(6 * chunk.len()); + self.profile_counters.draw_calls.inc(); + + gl::delete_buffers(&ubos); } + } + &PrimitiveBatchData::Text(ref ubo_data) => { + self.device.bind_program(self.ps_text, &projection); + self.device.bind_vao(self.quad_vao_id); + self.device.bind_color_texture(batch.color_texture_id); + + for chunk in ubo_data.chunks(self.max_prim_texts) { + let ubos = gl::gen_buffers(1); + let ubo = ubos[0]; + + gl::bind_buffer(gl::UNIFORM_BUFFER, ubo); + gl::buffer_data(gl::UNIFORM_BUFFER, &chunk, gl::STATIC_DRAW); + gl::bind_buffer_base(gl::UNIFORM_BUFFER, UBO_BIND_CACHE_ITEMS, ubo); + + self.device.draw_indexed_triangles_instanced_u16(6, chunk.len() as gl::GLint); + self.profile_counters.vertices.add(6 * chunk.len()); + self.profile_counters.draw_calls.inc(); - let p0 = Point2D::new(job.rect.origin.x as f32, job.rect.origin.y as f32); - let p1 = Point2D::new(job.rect.max_x() as f32, job.rect.max_y() as f32); - - // TODO(glennw): No need to re-init this FB working copy texture - // every time... - if needs_fb { - let fb_rect_size = Size2D::new(job.rect.size.width as f32 * render_context.device_pixel_ratio, - job.rect.size.height as f32 * render_context.device_pixel_ratio); - - let inverted_y0 = layer.size.height - - job.rect.size.height as f32 - - p0.y; - let fb_rect_origin = Point2D::new( - p0.x * render_context.device_pixel_ratio, - inverted_y0 * render_context.device_pixel_ratio); - - self.device.init_texture_if_necessary(self.temporary_fb_texture, - fb_rect_size.width as u32, - fb_rect_size.height as u32, - ImageFormat::RGBA8, - TextureFilter::Nearest, - RenderTargetMode::None); - self.device.read_framebuffer_rect( - self.temporary_fb_texture, - 0, - 0, - fb_rect_origin.x as i32, - fb_rect_origin.y as i32, - fb_rect_size.width as i32, - fb_rect_size.height as i32); + gl::delete_buffers(&ubos); } + } + &PrimitiveBatchData::AlignedGradient(ref ubo_data) => { + self.device.bind_program(self.ps_aligned_gradient, &projection); + self.device.bind_vao(self.quad_vao_id); + + for chunk in ubo_data.chunks(self.max_prim_aligned_gradients) { + let ubos = gl::gen_buffers(1); + let ubo = ubos[0]; - let vertex_count = vertices.len() as u16; - indices.push(vertex_count + 0); - indices.push(vertex_count + 1); - indices.push(vertex_count + 2); - indices.push(vertex_count + 2); - indices.push(vertex_count + 3); - indices.push(vertex_count + 1); - - let color = ColorF::new(1.0, 1.0, 1.0, alpha); - - debug_assert!(src_target.texture_id.unwrap() == info.texture_id); - - let pixel_uv = Rect::new( - Point2D::new(src_target.origin.x as u32, - src_target.origin.y as u32), - Size2D::new(src_target.size.width as u32, - src_target.size.height as u32)); - - let (texture_width, texture_height) = self.device.get_texture_dimensions(info.texture_id); - let texture_width = texture_width as f32 / self.device_pixel_ratio; - let texture_height = texture_height as f32 / self.device_pixel_ratio; - let texture_uv = Rect::new( - Point2D::new( - pixel_uv.origin.x as f32 / texture_width, - pixel_uv.origin.y as f32 / texture_height), - Size2D::new(pixel_uv.size.width as f32 / texture_width, - pixel_uv.size.height as f32 / texture_height)); - - let tl = job.world_transform.transform_point(&p0); - let tr = job.world_transform - .transform_point(&Point2D::new(p1.x, p0.y)); - let br = job.world_transform.transform_point(&p1); - let bl = job.world_transform - .transform_point(&Point2D::new(p0.x, p1.y)); - - if needs_fb { - vertices.extend_from_slice(&[ - PackedVertex::from_components( - tl.x, tl.y, - &color, - texture_uv.origin.x, texture_uv.max_y(), - 0.0, 1.0), - PackedVertex::from_components( - tr.x, tr.y, - &color, - texture_uv.max_x(), texture_uv.max_y(), - 1.0, 1.0), - PackedVertex::from_components( - bl.x, bl.y, - &color, - texture_uv.origin.x, texture_uv.origin.y, - 0.0, 0.0), - PackedVertex::from_components( - br.x, br.y, - &color, - texture_uv.max_x(), texture_uv.origin.y, - 1.0, 0.0), - ]); - } else { - vertices.extend_from_slice(&[ - PackedVertex::from_components_unscaled_muv( - tl.x, tl.y, - &color, - texture_uv.origin.x, texture_uv.max_y(), - texture_width as u16, texture_height as u16), - PackedVertex::from_components_unscaled_muv( - tr.x, tr.y, - &color, - texture_uv.max_x(), texture_uv.max_y(), - texture_width as u16, texture_height as u16), - PackedVertex::from_components_unscaled_muv( - bl.x, bl.y, - &color, - texture_uv.origin.x, texture_uv.origin.y, - texture_width as u16, texture_height as u16), - PackedVertex::from_components_unscaled_muv( - br.x, br.y, - &color, - texture_uv.max_x(), texture_uv.origin.y, - texture_width as u16, texture_height as u16), - ]); + gl::bind_buffer(gl::UNIFORM_BUFFER, ubo); + gl::buffer_data(gl::UNIFORM_BUFFER, &chunk, gl::STATIC_DRAW); + gl::bind_buffer_base(gl::UNIFORM_BUFFER, UBO_BIND_CACHE_ITEMS, ubo); + + self.device.draw_indexed_triangles_instanced_u16(6, chunk.len() as gl::GLint); + self.profile_counters.vertices.add(6 * chunk.len()); + self.profile_counters.draw_calls.inc(); + + gl::delete_buffers(&ubos); } } + &PrimitiveBatchData::AngleGradient(ref ubo_data) => { + self.device.bind_program(self.ps_angle_gradient, &projection); + self.device.bind_vao(self.quad_vao_id); + + for chunk in ubo_data.chunks(self.max_prim_angle_gradients) { + let ubos = gl::gen_buffers(1); + let ubo = ubos[0]; + + gl::bind_buffer(gl::UNIFORM_BUFFER, ubo); + gl::buffer_data(gl::UNIFORM_BUFFER, &chunk, gl::STATIC_DRAW); + gl::bind_buffer_base(gl::UNIFORM_BUFFER, UBO_BIND_CACHE_ITEMS, ubo); - draw_simple_triangles(&mut self.simple_triangles_vao, - &mut self.device, - &mut self.profile_counters, - &indices[..], - &vertices[..], - info.texture_id); + self.device.draw_indexed_triangles_instanced_u16(6, chunk.len() as gl::GLint); + self.profile_counters.vertices.add(6 * chunk.len()); + self.profile_counters.draw_calls.inc(); + + gl::delete_buffers(&ubos); + } + } } } + + gl::disable(gl::BLEND); + gl::delete_buffers(&tile_ubos); + gl::delete_buffers(&layer_ubos); } + } + + fn draw_tile_frame(&mut self, + frame: &Frame, + framebuffer_size: &Size2D) { + //println!("render {} debug rects", frame.debug_rects.len()); + self.gpu_profile_paint.begin(); + self.gpu_profile_paint.end(); + self.gpu_profile_composite.begin(); + + for debug_rect in frame.debug_rects.iter().rev() { + self.add_debug_rect(debug_rect.rect.origin, + debug_rect.rect.bottom_right(), + &debug_rect.label, + &debug_rect.color); + } + + gl::depth_mask(false); + gl::disable(gl::STENCIL_TEST); + gl::disable(gl::BLEND); - fn set_scissor_or_viewport(rect: &Rect, - render_context: &RenderContext, - layer_texture_id: Option, - function: fn(gl::GLint, gl::GLint, gl::GLint, gl::GLint)) { - let y = match layer_texture_id { - Some(_) => rect.origin.y, - None => { - render_context.framebuffer_size.height as gl::GLint - rect.size.height - - rect.origin.y + let projection = Matrix4D::ortho(0.0, + framebuffer_size.width as f32, + framebuffer_size.height as f32, + 0.0, + ORTHO_NEAR_PLANE, + ORTHO_FAR_PLANE); + + if frame.phases.is_empty() { + gl::clear_color(0.3, 0.3, 0.3, 1.0); + gl::clear(gl::COLOR_BUFFER_BIT); + } else { + if self.render_targets[0] == TextureId(0) { + self.render_targets[0] = self.device.create_texture_ids(1)[0]; + self.render_targets[1] = self.device.create_texture_ids(1)[0]; + + self.device.init_texture(self.render_targets[0], + frame.cache_size.width as u32, + frame.cache_size.height as u32, + ImageFormat::RGBA8, + TextureFilter::Linear, + RenderTargetMode::RenderTarget, + None); + + self.device.init_texture(self.render_targets[1], + frame.cache_size.width as u32, + frame.cache_size.height as u32, + ImageFormat::RGBA8, + TextureFilter::Linear, + RenderTargetMode::RenderTarget, + None); + } + + for (phase_index, phase) in frame.phases.iter().enumerate() { + let mut render_target_index = 0; + + for target in &phase.targets { + if target.is_framebuffer { + let ct_index = self.render_targets[1 - render_target_index]; + self.draw_target(None, + target, + &Size2D::new(framebuffer_size.width as f32, framebuffer_size.height as f32), + ct_index, + phase_index == 0); + } else { + let rt_index = self.render_targets[render_target_index]; + let ct_index = self.render_targets[1 - render_target_index]; + self.draw_target(Some(rt_index), + target, + &frame.cache_size, + ct_index, + true); + render_target_index = 1 - render_target_index; + } } - }; - function(rect.origin.x, y, rect.size.width, rect.size.height) + } } - fn to_point4d(point: &Point2D) -> Point4D { - Point4D::new(point.x, point.y, 0.0, 1.0) + // Clear tiles with no items + if !frame.clear_tiles.is_empty() { + self.device.bind_program(self.tile_clear_shader, &projection); + self.device.bind_vao(self.quad_vao_id); + + for chunk in frame.clear_tiles.chunks(self.max_clear_tiles) { + let ubos = gl::gen_buffers(1); + let ubo = ubos[0]; + + gl::bind_buffer(gl::UNIFORM_BUFFER, ubo); + gl::buffer_data(gl::UNIFORM_BUFFER, &chunk, gl::STATIC_DRAW); + gl::bind_buffer_base(gl::UNIFORM_BUFFER, UBO_BIND_CLEAR_TILES, ubo); + + self.device.draw_indexed_triangles_instanced_u16(6, chunk.len() as gl::GLint); + self.profile_counters.vertices.add(6 * chunk.len()); + self.profile_counters.draw_calls.inc(); + + gl::delete_buffers(&ubos); + } } + + self.gpu_profile_composite.end(); } fn draw_frame(&mut self, framebuffer_size: Size2D) { if let Some(frame) = self.current_frame.take() { // TODO: cache render targets! - let render_context = RenderContext { - blend_program_id: self.blend_program_id, - filter_program_id: self.filter_program_id, - device_pixel_ratio: self.device_pixel_ratio, - framebuffer_size: framebuffer_size, - }; - - let mut layer_viewports = HashMap::new(); - let framebuffer_size = &render_context.framebuffer_size; - let framebuffer_rect = Rect::new(Point2D::zero(), - Size2D::new(framebuffer_size.width as f32, - framebuffer_size.height as f32)); - self.calculate_layer_viewports(&frame.root_layer, - &render_context, - &framebuffer_rect, - &mut layer_viewports); - // TODO(gw): Doesn't work well with transforms. // Look into this... gl::disable(gl::DEPTH_TEST); - gl::depth_func(gl::LEQUAL); - gl::enable(gl::SCISSOR_TEST); + gl::disable(gl::SCISSOR_TEST); + gl::disable(gl::BLEND); - self.draw_layer(&frame.root_layer, &render_context, &layer_viewports); + if let Some(ref frame) = frame.frame { + self.draw_tile_frame(frame, &framebuffer_size); + } // Restore frame - avoid borrow checker! self.current_frame = Some(frame); } } - - fn get_or_create_similar_vao_with_offset(&mut self, - source_vertex_buffer_id: VertexBufferId, - format: VertexFormat, - offset: u32) - -> VAOId { - let source_vertex_buffers_and_offsets = - self.vertex_buffers.get_mut(&source_vertex_buffer_id) - .expect("Didn't find source vertex buffer ID in \ - `get_or_create_similar_vao_with_offset()`!"); - if let Some(vertex_buffer_and_offset) = - source_vertex_buffers_and_offsets.iter().find(|vertex_buffer| { - vertex_buffer.offset == offset - }) { - return vertex_buffer_and_offset.buffer.vao_id - } - - let vao = - self.device.create_similar_vao(format, - source_vertex_buffers_and_offsets[0].buffer.vao_id, - offset); - source_vertex_buffers_and_offsets.push(VertexBufferAndOffset { - buffer: VertexBuffer { - vao_id: vao, - }, - offset: offset, - }); - vao - } } #[derive(Clone, Debug)] @@ -1884,35 +1663,5 @@ pub struct RendererOptions { pub enable_aa: bool, pub enable_msaa: bool, pub enable_profiler: bool, + pub debug: bool, } - -fn draw_simple_triangles(simple_triangles_vao: &mut Option, - device: &mut Device, - profile_counters: &mut RendererProfileCounters, - indices: &[u16], - vertices: &[PackedVertex], - texture: TextureId) { - let vao_id = match *simple_triangles_vao { - Some(ref mut vao_id) => *vao_id, - None => { - let vao_id = device.create_vao(VertexFormat::Triangles, None); - *simple_triangles_vao = Some(vao_id); - vao_id - } - }; - device.bind_color_texture(texture); - device.bind_vao(vao_id); - device.update_vao_indices(vao_id, &indices[..], VertexUsageHint::Dynamic); - device.update_vao_main_vertices(vao_id, &vertices[..], VertexUsageHint::Dynamic); - - profile_counters.vertices.add(indices.len()); - profile_counters.draw_calls.inc(); - - device.draw_triangles_u16(0, indices.len() as gl::GLint); -} - -struct VertexBufferAndOffset { - buffer: VertexBuffer, - offset: u32, -} - diff --git a/src/resource_cache.rs b/src/resource_cache.rs index 4c5f16c618..72e86262f3 100644 --- a/src/resource_cache.rs +++ b/src/resource_cache.rs @@ -129,16 +129,11 @@ pub struct ResourceCache { texture_cache: TextureCache, pending_raster_jobs: Vec, - - white_image_id: TextureCacheItemId, - dummy_mask_image_id: TextureCacheItemId, } impl ResourceCache { pub fn new(thread_pool: &mut scoped_threadpool::Pool, texture_cache: TextureCache, - white_image_id: TextureCacheItemId, - dummy_mask_image_id: TextureCacheItemId, device_pixel_ratio: f32, enable_aa: bool) -> ResourceCache { @@ -167,8 +162,6 @@ impl ResourceCache { texture_cache: texture_cache, pending_raster_jobs: Vec::new(), device_pixel_ratio: device_pixel_ratio, - white_image_id: white_image_id, - dummy_mask_image_id: dummy_mask_image_id, enable_aa: enable_aa, } } @@ -307,11 +300,9 @@ impl ResourceCache { } pub fn raster_pending_glyphs(&mut self, - thread_pool: &mut scoped_threadpool::Pool, frame_id: FrameId) { // Run raster jobs in parallel - run_raster_jobs(thread_pool, - &mut self.pending_raster_jobs, + run_raster_jobs(&mut self.pending_raster_jobs, &self.font_templates, self.device_pixel_ratio, self.enable_aa); @@ -366,44 +357,14 @@ impl ResourceCache { self.draw_lists.get(draw_list_id) } - pub fn get_draw_list_mut(&mut self, draw_list_id: DrawListId) -> &mut DrawList { - self.draw_lists.get_mut(draw_list_id) - } - pub fn remove_draw_list(&mut self, draw_list_id: DrawListId) { self.draw_lists.free(draw_list_id); } - pub fn allocate_render_target(&mut self, - width: u32, - height: u32, - format: ImageFormat, - frame_id: FrameId) - -> TextureId { - self.texture_cache.allocate_render_target(width, - height, - format, - frame_id) - } - - pub fn free_old_render_targets(&mut self) { - self.texture_cache.free_old_render_targets() - } - pub fn pending_updates(&mut self) -> TextureUpdateList { self.texture_cache.pending_updates() } - #[inline] - pub fn get_dummy_mask_image(&self) -> &TextureCacheItem { - self.texture_cache.get(self.dummy_mask_image_id) - } - - #[inline] - pub fn get_dummy_color_image(&self) -> &TextureCacheItem { - self.texture_cache.get(self.white_image_id) - } - #[inline] pub fn get_glyph(&self, glyph_key: &GlyphKey, frame_id: FrameId) -> Option<&TextureCacheItem> { let image_id = self.cached_glyphs.get(glyph_key, frame_id); @@ -420,21 +381,11 @@ impl ResourceCache { self.texture_cache.get(image_info.texture_cache_id) } - #[inline] - pub fn get_raster(&self, raster_item: &RasterItem, frame_id: FrameId) -> &TextureCacheItem { - let image_id = self.cached_rasters.get(raster_item, frame_id); - self.texture_cache.get(*image_id) - } - #[inline] pub fn get_webgl_texture(&self, context_id: &WebGLContextId) -> TextureId { self.webgl_textures.get(context_id).unwrap().clone() } - pub fn device_pixel_ratio(&self) -> f32 { - self.device_pixel_ratio - } - pub fn expire_old_resources(&mut self, frame_id: FrameId) { self.cached_glyphs.expire_old_resources(&mut self.texture_cache, frame_id); self.cached_rasters.expire_old_resources(&mut self.texture_cache, frame_id); @@ -442,8 +393,7 @@ impl ResourceCache { } } -fn run_raster_jobs(thread_pool: &mut scoped_threadpool::Pool, - pending_raster_jobs: &mut Vec, +fn run_raster_jobs(pending_raster_jobs: &mut Vec, font_templates: &HashMap>, device_pixel_ratio: f32, enable_aa: bool) { @@ -451,31 +401,27 @@ fn run_raster_jobs(thread_pool: &mut scoped_threadpool::Pool, return } - // Run raster jobs in parallel - thread_pool.scoped(|scope| { - for job in pending_raster_jobs { - scope.execute(|| { - let font_template = &font_templates[&job.glyph_key.font_key]; - FONT_CONTEXT.with(move |font_context| { - let mut font_context = font_context.borrow_mut(); - match *font_template { - FontTemplate::Raw(ref bytes) => { - font_context.add_raw_font(&job.glyph_key.font_key, &**bytes); - } - FontTemplate::Native(ref native_font_handle) => { - font_context.add_native_font(&job.glyph_key.font_key, - (*native_font_handle).clone()); - } - } - job.result = font_context.get_glyph(job.glyph_key.font_key, - job.glyph_key.size, - job.glyph_key.index, - device_pixel_ratio, - enable_aa); - }); - }); - } - }); + // TODO(gw): Run raster jobs in parallel again + for job in pending_raster_jobs { + let font_template = &font_templates[&job.glyph_key.font_key]; + FONT_CONTEXT.with(move |font_context| { + let mut font_context = font_context.borrow_mut(); + match *font_template { + FontTemplate::Raw(ref bytes) => { + font_context.add_raw_font(&job.glyph_key.font_key, &**bytes); + } + FontTemplate::Native(ref native_font_handle) => { + font_context.add_native_font(&job.glyph_key.font_key, + (*native_font_handle).clone()); + } + } + job.result = font_context.get_glyph(job.glyph_key.font_key, + job.glyph_key.size, + job.glyph_key.index, + device_pixel_ratio, + enable_aa); + }); + } } pub trait Resource { diff --git a/src/resource_list.rs b/src/resource_list.rs index 7015c4d658..3af6906392 100644 --- a/src/resource_list.rs +++ b/src/resource_list.rs @@ -2,20 +2,12 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use aabbtree::AABBTreeNode; use app_units::Au; -use batch_builder; -use euclid::{Rect, Size2D}; use fnv::FnvHasher; -use internal_types::{BorderRadiusRasterOp, BoxShadowRasterOp, DrawListItemIndex}; -use internal_types::{Glyph, GlyphKey, RasterItem, DevicePixel}; -use resource_cache::ResourceCache; +use internal_types::{Glyph, GlyphKey, RasterItem}; use std::collections::{HashMap, HashSet}; use std::hash::BuildHasherDefault; -use tessellator; -use webrender_traits::{AuxiliaryLists, BorderRadius, BorderStyle, BoxShadowClipMode}; -use webrender_traits::{FontKey, ImageFormat, ImageKey, ImageRendering, PipelineId}; -use webrender_traits::{SpecificDisplayItem}; +use webrender_traits::{FontKey, ImageKey, ImageRendering}; type RequiredImageSet = HashSet<(ImageKey, ImageRendering), BuildHasherDefault>; type RequiredGlyphMap = HashMap, BuildHasherDefault>; @@ -25,16 +17,14 @@ pub struct ResourceList { required_images: RequiredImageSet, required_glyphs: RequiredGlyphMap, required_rasters: RequiredRasterSet, - device_pixel_ratio: f32, } impl ResourceList { - pub fn new(device_pixel_ratio: f32) -> ResourceList { + pub fn new() -> ResourceList { ResourceList { required_glyphs: HashMap::with_hasher(Default::default()), required_images: HashSet::with_hasher(Default::default()), required_rasters: HashSet::with_hasher(Default::default()), - device_pixel_ratio: device_pixel_ratio, } } @@ -50,64 +40,6 @@ impl ResourceList { .insert(glyph); } - pub fn add_radius_raster(&mut self, - outer_radius: &Size2D, - inner_radius: &Size2D, - inverted: bool, - index: Option, - image_format: ImageFormat) { - let outer_radius_x = DevicePixel::new(outer_radius.width, self.device_pixel_ratio); - let outer_radius_y = DevicePixel::new(outer_radius.height, self.device_pixel_ratio); - let inner_radius_x = DevicePixel::new(inner_radius.width, self.device_pixel_ratio); - let inner_radius_y = DevicePixel::new(inner_radius.height, self.device_pixel_ratio); - if let Some(raster_item) = BorderRadiusRasterOp::create(outer_radius_x, - outer_radius_y, - inner_radius_x, - inner_radius_y, - inverted, - index, - image_format) { - self.required_rasters.insert(RasterItem::BorderRadius(raster_item)); - } - } - - /// NB: Only adds non-tessellated border radii. - pub fn add_radius_raster_for_border_radii(&mut self, radii: &BorderRadius) { - let zero_size = Size2D::new(0.0, 0.0); - self.add_radius_raster(&radii.top_left, &zero_size, false, None, ImageFormat::A8); - self.add_radius_raster(&radii.top_right, &zero_size, false, None, ImageFormat::A8); - self.add_radius_raster(&radii.bottom_left, &zero_size, false, None, ImageFormat::A8); - self.add_radius_raster(&radii.bottom_right, &zero_size, false, None, ImageFormat::A8); - } - - pub fn add_box_shadow_corner(&mut self, - blur_radius: f32, - border_radius: f32, - box_rect: &Rect, - inverted: bool) { - if let Some(raster_item) = BoxShadowRasterOp::create_corner(blur_radius, - border_radius, - box_rect, - inverted, - self.device_pixel_ratio) { - self.required_rasters.insert(RasterItem::BoxShadow(raster_item)); - } - } - - pub fn add_box_shadow_edge(&mut self, - blur_radius: f32, - border_radius: f32, - box_rect: &Rect, - inverted: bool) { - if let Some(raster_item) = BoxShadowRasterOp::create_edge(blur_radius, - border_radius, - box_rect, - inverted, - self.device_pixel_ratio) { - self.required_rasters.insert(RasterItem::BoxShadow(raster_item)); - } - } - pub fn for_each_image(&self, mut f: F) where F: FnMut(ImageKey, ImageRendering) { for &(image_id, image_rendering) in &self.required_images { f(image_id, image_rendering); @@ -134,225 +66,3 @@ impl ResourceList { } } } - -pub trait BuildRequiredResources { - fn build_resource_list(&mut self, - resource_cache: &ResourceCache, - pipeline_auxiliary_lists: &HashMap>); -} - -impl BuildRequiredResources for AABBTreeNode { - fn build_resource_list(&mut self, - resource_cache: &ResourceCache, - pipeline_auxiliary_lists: &HashMap>) { - //let _pf = util::ProfileScope::new(" build_resource_list"); - let mut resource_list = ResourceList::new(resource_cache.device_pixel_ratio()); - - for group in &self.draw_list_group_segments { - for draw_list_index_buffer in &group.index_buffers { - let draw_list = resource_cache.get_draw_list(draw_list_index_buffer.draw_list_id); - - for index in &draw_list_index_buffer.indices { - let DrawListItemIndex(index) = *index; - let display_item = &draw_list.items[index as usize]; - let auxiliary_lists = - pipeline_auxiliary_lists.get(&draw_list.pipeline_id) - .expect("No auxiliary lists for pipeline?!"); - - // Handle border radius for complex clipping regions. - for complex_clip_region in - auxiliary_lists.complex_clip_regions(&display_item.clip.complex) { - resource_list.add_radius_raster_for_border_radii( - &complex_clip_region.radii); - } - - match display_item.item { - SpecificDisplayItem::Image(ref info) => { - resource_list.add_image(info.image_key, info.image_rendering); - } - SpecificDisplayItem::Text(ref info) => { - let glyphs = auxiliary_lists.glyph_instances(&info.glyphs); - for glyph in glyphs { - let glyph = Glyph::new(info.size, info.blur_radius, glyph.index); - resource_list.add_glyph(info.font_key, glyph); - } - } - SpecificDisplayItem::WebGL(..) => {} - SpecificDisplayItem::Rectangle(..) => {} - SpecificDisplayItem::Gradient(..) => {} - SpecificDisplayItem::BoxShadow(ref info) => { - resource_list.add_radius_raster_for_border_radii( - &BorderRadius::uniform(info.border_radius)); - - let box_rect = batch_builder::compute_box_shadow_rect(&info.box_bounds, - &info.offset, - info.spread_radius, - info.clip_mode); - resource_list.add_box_shadow_corner(info.blur_radius, - info.border_radius, - &box_rect, - false); - resource_list.add_box_shadow_edge(info.blur_radius, - info.border_radius, - &box_rect, - false); - if info.clip_mode == BoxShadowClipMode::Inset { - resource_list.add_box_shadow_corner(info.blur_radius, - info.border_radius, - &box_rect, - true); - resource_list.add_box_shadow_edge(info.blur_radius, - info.border_radius, - &box_rect, - true); - } - } - SpecificDisplayItem::Border(ref info) => { - let can_tessellate = tessellator::can_tessellate_border(info); - add_border_radius_raster(&info.radius.top_left, - &info.top_left_inner_radius(), - can_tessellate, - resource_cache, - &mut resource_list); - add_border_radius_raster(&info.radius.top_right, - &info.top_right_inner_radius(), - can_tessellate, - resource_cache, - &mut resource_list); - add_border_radius_raster(&info.radius.bottom_right, - &info.bottom_right_inner_radius(), - can_tessellate, - resource_cache, - &mut resource_list); - add_border_radius_raster(&info.radius.bottom_left, - &info.bottom_left_inner_radius(), - can_tessellate, - resource_cache, - &mut resource_list); - - if info.top.style == BorderStyle::Dotted { - resource_list.add_radius_raster(&Size2D::new(info.top.width / 2.0, - info.top.width / 2.0), - &Size2D::new(0.0, 0.0), - false, - None, - ImageFormat::RGBA8); - } - if info.right.style == BorderStyle::Dotted { - resource_list.add_radius_raster(&Size2D::new(info.right.width / 2.0, - info.right.width / 2.0), - &Size2D::new(0.0, 0.0), - false, - None, - ImageFormat::RGBA8); - } - if info.bottom.style == BorderStyle::Dotted { - resource_list.add_radius_raster(&Size2D::new(info.bottom.width / 2.0, - info.bottom.width / 2.0), - &Size2D::new(0.0, 0.0), - false, - None, - ImageFormat::RGBA8); - } - if info.left.style == BorderStyle::Dotted { - resource_list.add_radius_raster(&Size2D::new(info.left.width / 2.0, - info.left.width / 2.0), - &Size2D::new(0.0, 0.0), - false, - None, - ImageFormat::RGBA8); - } - - if info.top.style == BorderStyle::Double { - resource_list.add_radius_raster(&info.radius.top_left, - &Size2D::zero(), - false, - None, - ImageFormat::A8); - - resource_list.add_radius_raster(&Size2D::zero(), - &info.top_left_inner_radius(), - false, - None, - ImageFormat::A8); - } - if info.right.style == BorderStyle::Double { - resource_list.add_radius_raster(&info.radius.top_right, - &Size2D::zero(), - false, - None, - ImageFormat::A8); - - resource_list.add_radius_raster(&Size2D::zero(), - &info.top_right_inner_radius(), - false, - None, - ImageFormat::A8); - } - if info.bottom.style == BorderStyle::Double { - resource_list.add_radius_raster(&info.radius.bottom_left, - &Size2D::zero(), - false, - None, - ImageFormat::A8); - - resource_list.add_radius_raster(&Size2D::zero(), - &info.bottom_left_inner_radius(), - false, - None, - ImageFormat::A8); - } - if info.left.style == BorderStyle::Double { - resource_list.add_radius_raster(&info.radius.bottom_right, - &Size2D::zero(), - false, - None, - ImageFormat::A8); - - resource_list.add_radius_raster(&Size2D::zero(), - &info.bottom_right_inner_radius(), - false, - None, - ImageFormat::A8); - } - - - } - } - } - } - } - - self.resource_list = Some(resource_list); - } -} - -fn add_border_radius_raster(outer_radius: &Size2D, - inner_radius: &Size2D, - can_tessellate: bool, - resource_cache: &ResourceCache, - resource_list: &mut ResourceList) { - let quad_count = if can_tessellate { - tessellator::quad_count_for_border_corner(outer_radius, - resource_cache.device_pixel_ratio()) - } else { - 1 - }; - for rect_index in 0..quad_count { - let index = if can_tessellate { - Some(rect_index) - } else { - None - }; - resource_list.add_radius_raster(outer_radius, - inner_radius, - false, - index, - ImageFormat::A8); - } -} - diff --git a/src/spring.rs b/src/spring.rs index 1fcbdfec4b..b8e333e05d 100644 --- a/src/spring.rs +++ b/src/spring.rs @@ -101,5 +101,3 @@ fn next(cur: f32, prev: f32, dest: f32, stiffness: f32, damping: f32) -> f32 { fn is_resting(cur: f32, prev: f32, dest: f32) -> bool { (cur - prev).abs() < EPSILON && (cur - dest).abs() < EPSILON } - - diff --git a/src/tessellator.rs b/src/tessellator.rs deleted file mode 100644 index 47824779db..0000000000 --- a/src/tessellator.rs +++ /dev/null @@ -1,113 +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/. */ - -use euclid::{Point2D, Rect, Size2D}; -use internal_types::BasicRotationAngle; -use webrender_traits::BorderDisplayItem; - -pub fn quad_count_for_border_corner(outer_radius: &Size2D, - device_pixel_ratio: f32) -> u32 { - let max = 32.0 / device_pixel_ratio; - if outer_radius.width < max && outer_radius.height < max { - 1 - } else { - 4 - } -} - -pub trait BorderCornerTessellation { - fn tessellate_border_corner(&self, - outer_radius: &Size2D, - inner_radius: &Size2D, - device_pixel_ratio: f32, - rotation_angle: BasicRotationAngle, - index: u32) - -> Rect; -} - -impl BorderCornerTessellation for Rect { - fn tessellate_border_corner(&self, - outer_radius: &Size2D, - inner_radius: &Size2D, - device_pixel_ratio: f32, - rotation_angle: BasicRotationAngle, - index: u32) - -> Rect { - let quad_count = quad_count_for_border_corner(outer_radius, device_pixel_ratio); - if quad_count == 1 { - return *self - } - - /* - // FIXME(pcwalton): This is basically a hack to keep Acid2 working. We don't currently - // render border corners properly when the corner size is greater than zero but less than - // the radius, and we'll have to modify this when we do. - if self.size.width - outer_radius.width > EPSILON || - self.size.height - outer_radius.height > EPSILON { - return Rect::new(Point2D::new(self.origin.x + self.size.width / (quad_count as f32) * - (index as f32), - self.origin.y), - Size2D::new(self.size.width / (quad_count as f32), - self.size.height)) - }*/ - - let delta = outer_radius.width / (quad_count as f32); - let prev_x = (delta * (index as f32)).ceil(); - let prev_outer_y = ellipse_y_coordinate(prev_x, outer_radius); - - let next_x = (prev_x + delta).ceil(); - let next_inner_y = ellipse_y_coordinate(next_x, inner_radius); - - let top_left = Point2D::new(prev_x, prev_outer_y); - let bottom_right = Point2D::new(next_x, next_inner_y); - - let subrect = Rect::new(Point2D::new(top_left.x, bottom_right.y), - Size2D::new(bottom_right.x - top_left.x, - top_left.y - bottom_right.y)); - - let subrect = match rotation_angle { - BasicRotationAngle::Upright => { - Rect::new(Point2D::new(outer_radius.width - subrect.max_x(), - outer_radius.height - subrect.max_y()), - subrect.size) - } - BasicRotationAngle::Clockwise90 => { - Rect::new(Point2D::new(subrect.origin.x, - outer_radius.height - subrect.max_y()), - subrect.size) - } - BasicRotationAngle::Clockwise180 => { - subrect - } - BasicRotationAngle::Clockwise270 => { - Rect::new(Point2D::new(outer_radius.width - subrect.max_x(), - subrect.origin.y), - subrect.size) - } - }; - - subrect.translate(&self.origin) - } -} - -fn ellipse_y_coordinate(x: f32, radius: &Size2D) -> f32 { - if radius.width == 0.0 { - return x - } - let radicand = 1.0 - (x / radius.width) * (x / radius.width); - if radicand < 0.0 { - 0.0 - } else { - radius.height * radicand.sqrt() - } -} - -/// FIXME(pcwalton): For now, we don't tessellate multicolored border radii. -pub fn can_tessellate_border(border: &BorderDisplayItem) -> bool { - border.left.color == border.top.color && - border.top.color == border.right.color && - border.right.color == border.bottom.color && - border.bottom.color == border.left.color -} - diff --git a/src/texture_cache.rs b/src/texture_cache.rs index d8d5cc7494..c1411e495e 100644 --- a/src/texture_cache.rs +++ b/src/texture_cache.rs @@ -10,14 +10,13 @@ use frame::FrameId; use freelist::{FreeList, FreeListItem, FreeListItemId}; use internal_types::{TextureUpdate, TextureUpdateOp, TextureUpdateDetails}; use internal_types::{RasterItem, RenderTargetMode, TextureImage, TextureUpdateList}; -use internal_types::{RectUv, DevicePixel, BasicRotationAngle}; +use internal_types::{RectUv, DevicePixel}; use std::cmp::{self, Ordering}; use std::collections::HashMap; use std::collections::hash_map::Entry; use std::hash::BuildHasherDefault; use std::mem; use std::slice::Iter; -use tessellator::BorderCornerTessellation; use time; use util; use webrender_traits::ImageFormat; @@ -28,9 +27,6 @@ const MAX_BYTES_PER_TEXTURE: u32 = 1024 * 1024 * 256; // 256MB /// The number of RGBA pixels we're allowed to use for a texture. const MAX_RGBA_PIXELS_PER_TEXTURE: u32 = MAX_BYTES_PER_TEXTURE / 4; -/// The total number of RGBA pixels we're allowed to use for our render targets. -const MAX_RGBA_PIXELS_IN_CACHED_RENDER_TARGETS: u32 = 4096 * 4096 * 2; - /// The desired initial size of each texture, in pixels. const INITIAL_TEXTURE_SIZE: u32 = 1024; @@ -317,7 +313,7 @@ impl TexturePage { self.dirty = changed } - fn clear(&mut self) { + pub fn clear(&mut self) { self.free_list = FreeRectList::new(); self.free_list.push(&Rect::new(Point2D::new(0, 0), Size2D::new(self.texture_size, self.texture_size))); @@ -460,6 +456,8 @@ pub struct TextureCacheItem { // bilinear filtering / texture bleeding purposes. pub allocated_rect: Rect, pub requested_rect: Rect, + + pub is_opaque: bool, } // Structure squat the width/height fields to maintain the free list information :) @@ -493,7 +491,8 @@ impl TextureCacheItem { user_x0: i32, user_y0: i32, allocated_rect: Rect, requested_rect: Rect, - texture_size: &Size2D) + texture_size: &Size2D, + is_opaque: bool) -> TextureCacheItem { TextureCacheItem { texture_id: texture_id, @@ -514,6 +513,7 @@ impl TextureCacheItem { }, allocated_rect: allocated_rect, requested_rect: requested_rect, + is_opaque: is_opaque, } } @@ -590,8 +590,6 @@ pub struct TextureCache { // Vec, // BuildHasherDefault>, items: FreeList, - cached_render_targets: Vec, - total_pixel_count_of_cached_render_targets: u32, arena: TextureCacheArena, pending_updates: TextureUpdateList, } @@ -614,9 +612,6 @@ impl TextureCache { free_texture_ids: free_texture_ids, free_texture_levels: HashMap::with_hasher(Default::default()), alternate_free_texture_levels: HashMap::with_hasher(Default::default()), - //render_target_free_texture_levels: HashMap::with_hasher(Default::default()), - cached_render_targets: vec![], - total_pixel_count_of_cached_render_targets: 0, items: FreeList::new(), pending_updates: TextureUpdateList::new(), arena: TextureCacheArena::new(), @@ -646,105 +641,11 @@ impl TextureCache { requested_rect: Rect::zero(), texture_size: Size2D::zero(), texture_id: TextureId::invalid(), + is_opaque: false, }; self.items.insert(new_item) } - /* - pub fn free(&mut self, - _texture_id: TextureId, - _uv: &Rect, - _kind: TextureCacheItemKind) { - panic!("can't free texture items yet!"); - } - */ - - pub fn allocate_render_target(&mut self, - width: u32, - height: u32, - format: ImageFormat, - frame_id: FrameId) - -> TextureId { - let mut cached_render_target_index = None; - for (i, cached_render_target) in self.cached_render_targets.iter().enumerate() { - if cached_render_target.width == width && - cached_render_target.height == height && - cached_render_target.format == format && - cached_render_target.frame_id != frame_id { - cached_render_target_index = Some(i); - break - } - } - if let Some(cached_render_target_index) = cached_render_target_index { - // Push to the end to mark as recently used. - let mut cached_render_target = self.cached_render_targets - .remove(cached_render_target_index); - cached_render_target.frame_id = frame_id; - self.cached_render_targets.push(cached_render_target); - return cached_render_target.texture_id - } - - self.total_pixel_count_of_cached_render_targets += width * height; - - let texture_id = self.free_texture_ids - .pop() - .expect("TODO: Handle running out of texture IDs!"); - let op = TextureUpdateOp::Create(width, - height, - format, - TextureFilter::Linear, - RenderTargetMode::RenderTarget, - None); - let update_op = TextureUpdate { - id: texture_id, - op: op, - }; - self.pending_updates.push(update_op); - - self.cached_render_targets.push(CachedRenderTarget { - texture_id: texture_id, - width: width, - height: height, - format: format, - frame_id: frame_id, - }); - - texture_id - } - - pub fn free_old_render_targets(&mut self) { - if self.total_pixel_count_of_cached_render_targets <= - MAX_RGBA_PIXELS_IN_CACHED_RENDER_TARGETS { - return - } - - let mut cached_render_targets_to_destroy = 0; - for cached_render_target in &self.cached_render_targets { - let op = TextureUpdateOp::Update(0, 0, 0, 0, - TextureUpdateDetails::Blit(Vec::new())); - let update_op = TextureUpdate { - id: cached_render_target.texture_id, - op: op, - }; - self.pending_updates.push(update_op); - self.free_texture_ids.push(cached_render_target.texture_id); - - cached_render_targets_to_destroy += 1; - - self.total_pixel_count_of_cached_render_targets -= cached_render_target.width * - cached_render_target.height; - if self.total_pixel_count_of_cached_render_targets < - MAX_RGBA_PIXELS_IN_CACHED_RENDER_TARGETS { - break - } - } - - self.cached_render_targets = self.cached_render_targets[cached_render_targets_to_destroy..] - .iter() - .cloned() - .collect() - } - pub fn allocate(&mut self, image_id: TextureCacheItemId, user_x0: i32, @@ -754,7 +655,8 @@ impl TextureCache { format: ImageFormat, kind: TextureCacheItemKind, border_type: BorderType, - filter: TextureFilter) + filter: TextureFilter, + is_opaque: bool) -> AllocationResult { let requested_size = Size2D::new(requested_width, requested_height); @@ -773,7 +675,8 @@ impl TextureCache { user_x0, user_y0, Rect::new(Point2D::zero(), requested_size), Rect::new(Point2D::zero(), requested_size), - &requested_size); + &requested_size, + is_opaque); *self.items.get_mut(image_id) = cache_item; return AllocationResult { @@ -835,7 +738,8 @@ impl TextureCache { user_x0, user_y0, allocated_rect, requested_rect, - &Size2D::new(page.texture_size, page.texture_size)); + &Size2D::new(page.texture_size, page.texture_size), + is_opaque); *self.items.get_mut(image_id) = cache_item; return AllocationResult { @@ -900,56 +804,7 @@ impl TextureCache { item: &RasterItem, _device_pixel_ratio: f32) { let update_op = match item { - &RasterItem::BorderRadius(ref op) => { - let rect = Rect::new(Point2D::zero(), - Size2D::new(op.outer_radius_x.as_f32(), - op.outer_radius_y.as_f32())); - let tessellated_rect = match op.index { - Some(index) => { - rect.tessellate_border_corner( - &Size2D::new(op.outer_radius_x.as_f32(), - op.outer_radius_y.as_f32()), - &Size2D::new(op.inner_radius_x.as_f32(), - op.inner_radius_y.as_f32()), - 1.0, - BasicRotationAngle::Upright, - index) - } - None => rect, - }; - - let width = tessellated_rect.size.width.ceil() as u32; - let height = tessellated_rect.size.height.ceil() as u32; - - let allocation = self.allocate(image_id, - 0, - 0, - width, - height, - op.image_format, - TextureCacheItemKind::Standard, - BorderType::SinglePixel, - TextureFilter::Linear); - - assert!(allocation.kind == AllocationKind::TexturePage); // TODO: Handle large border radii not fitting in texture cache page - - TextureUpdate { - id: allocation.item.texture_id, - op: TextureUpdateOp::Update(allocation.item.requested_rect.origin.x, - allocation.item.requested_rect.origin.y, - width, - height, - TextureUpdateDetails::BorderRadius( - op.outer_radius_x, - op.outer_radius_y, - op.inner_radius_x, - op.inner_radius_y, - op.index, - op.inverted, - BorderType::SinglePixel)), - } - } - &RasterItem::BoxShadow(ref op) => { + &RasterItem::_BoxShadow(ref op) => { let allocation = self.allocate(image_id, 0, 0, @@ -958,7 +813,8 @@ impl TextureCache { ImageFormat::RGBA8, TextureCacheItemKind::Standard, BorderType::SinglePixel, - TextureFilter::Linear); + TextureFilter::Linear, + false); // TODO(pcwalton): Handle large box shadows not fitting in texture cache page. assert!(allocation.kind == AllocationKind::TexturePage); @@ -1033,6 +889,23 @@ impl TextureCache { insert_op: TextureInsertOp, border_type: BorderType) { + let is_opaque = match (&insert_op, format) { + (&TextureInsertOp::Blit(ref bytes), ImageFormat::RGBA8) => { + let mut is_opaque = true; + for i in (0..bytes.len()).step_by(4) { + if bytes[i + 3] != 255 { + is_opaque = false; + break; + } + } + is_opaque + } + (&TextureInsertOp::Blit(..), ImageFormat::RGB8) => true, + (&TextureInsertOp::Blit(..), ImageFormat::A8) => false, + (&TextureInsertOp::Blit(..), ImageFormat::Invalid) => unreachable!(), + (&TextureInsertOp::Blur(..), _) => false, + }; + let result = self.allocate(image_id, x0, y0, @@ -1041,7 +914,8 @@ impl TextureCache { format, TextureCacheItemKind::Standard, border_type, - filter); + filter, + is_opaque); let op = match (result.kind, insert_op) { (AllocationKind::TexturePage, TextureInsertOp::Blit(bytes)) => { @@ -1137,14 +1011,16 @@ impl TextureCache { ImageFormat::RGBA8, TextureCacheItemKind::Standard, BorderType::SinglePixel, - TextureFilter::Linear); + TextureFilter::Linear, + false); self.allocate(horizontal_blur_image_id, 0, 0, width, height, ImageFormat::RGBA8, TextureCacheItemKind::Alternate, BorderType::SinglePixel, - TextureFilter::Linear); + TextureFilter::Linear, + false); let unblurred_glyph_item = self.get(unblurred_glyph_image_id); let horizontal_blur_item = self.get(horizontal_blur_image_id); TextureUpdateOp::Update( diff --git a/src/tiling.rs b/src/tiling.rs new file mode 100644 index 0000000000..e046abd8d1 --- /dev/null +++ b/src/tiling.rs @@ -0,0 +1,2936 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use app_units::{Au}; +use batch_builder::{BorderSideHelpers, BoxShadowMetrics}; +use device::{TextureId}; +use euclid::{Point2D, Rect, Matrix4D, Size2D, Point4D}; +use fnv::FnvHasher; +use frame::FrameId; +use internal_types::{Glyph, GlyphKey, DevicePixel, CompositionOp}; +use internal_types::{ANGLE_FLOAT_TO_FIXED, LowLevelFilterOp, RectUv}; +use layer::Layer; +use renderer::{BLUR_INFLATION_FACTOR}; +use resource_cache::ResourceCache; +use resource_list::ResourceList; +use std::cmp; +use std::collections::{HashMap}; +use std::f32; +use std::mem; +use std::hash::{BuildHasherDefault}; +use texture_cache::{TexturePage}; +use util::{self, rect_from_points, rect_from_points_f, MatrixHelpers, subtract_rect}; +use webrender_traits::{ColorF, FontKey, ImageKey, ImageRendering, ComplexClipRegion}; +use webrender_traits::{BorderDisplayItem, BorderStyle, ItemRange, AuxiliaryLists, BorderRadius, BorderSide}; +use webrender_traits::{BoxShadowClipMode, PipelineId, ScrollLayerId, WebGLContextId}; + +pub static SCREEN_MIN: Point2D = Point2D { + x: 10000000.0, + y: 10000000.0, +}; + +pub static SCREEN_MAX: Point2D = Point2D { + x: -10000000.0, + y: -10000000.0, +}; + +#[repr(u32)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum GradientType { + Horizontal, + Vertical, + Rotated, +} + +#[derive(Debug, Copy, Clone)] +struct TaskIndex(usize); + +struct AlphaBatchTask { + items: Vec, + target_rect: Rect, + actual_rect: Rect, + child_rects: Vec>, +} + +pub struct AlphaBatcher { + pub layer_ubos: Vec>, + pub tile_ubos: Vec>, + pub batches: Vec, + layer_to_ubo_map: Vec>, + tile_to_ubo_map: Vec>, + tasks: Vec, +} + +impl AlphaBatcher { + fn new() -> AlphaBatcher { + AlphaBatcher { + layer_ubos: Vec::new(), + tile_ubos: Vec::new(), + batches: Vec::new(), + layer_to_ubo_map: Vec::new(), + tile_to_ubo_map: Vec::new(), + tasks: Vec::new(), + } + } + + fn add_tile_to_ubo(tile_ubos: &mut Vec>, + tile_to_ubo_map: &mut Vec>, + task_index: TaskIndex, + task: &AlphaBatchTask, + ctx: &RenderTargetContext) -> (usize, u32) { + let index_in_ubo = match tile_to_ubo_map[task_index.0] { + Some(index_in_ubo) => { + index_in_ubo + } + None => { + let need_new_ubo = tile_ubos.is_empty() || + tile_ubos.last().unwrap().len() == ctx.alpha_batch_max_tiles; + + if need_new_ubo { + for i in 0..tile_to_ubo_map.len() { + tile_to_ubo_map[i] = None; + } + tile_ubos.push(Vec::new()); + } + + let tile_ubo = tile_ubos.last_mut().unwrap(); + let index = tile_ubo.len(); + tile_ubo.push(PackedTile { + actual_rect: task.actual_rect, + target_rect: task.target_rect, + }); + tile_to_ubo_map[task_index.0] = Some(index); + index + } + }; + + (tile_ubos.len() - 1, index_in_ubo as u32) + } + + fn add_layer_to_ubo(layer_ubos: &mut Vec>, + layer_to_ubo_map: &mut Vec>, + layer_index: StackingContextIndex, + ctx: &RenderTargetContext) -> (usize, u32) { + let index_in_ubo = match layer_to_ubo_map[layer_index.0] { + Some(index_in_ubo) => { + index_in_ubo + } + None => { + let need_new_ubo = layer_ubos.is_empty() || + layer_ubos.last().unwrap().len() == ctx.alpha_batch_max_layers; + + if need_new_ubo { + for i in 0..layer_to_ubo_map.len() { + layer_to_ubo_map[i] = None; + } + layer_ubos.push(Vec::new()); + } + + let layer_ubo = layer_ubos.last_mut().unwrap(); + let index = layer_ubo.len(); + let sc = &ctx.layer_store[layer_index.0]; + layer_ubo.push(PackedLayer { + transform: sc.transform, + inv_transform: sc.transform.invert(), + screen_vertices: sc.xf_rect.as_ref().unwrap().vertices, + world_clip_rect: sc.world_clip_rect.unwrap(), + }); + layer_to_ubo_map[layer_index.0] = Some(index); + index + } + }; + + (layer_ubos.len() - 1, index_in_ubo as u32) + } + + fn add_task(&mut self, task: AlphaBatchTask) { + self.tasks.push(task); + } + + fn build(&mut self, packed_primitive_cache: &PackedPrimitiveCache, ctx: &RenderTargetContext) { + for _ in 0..ctx.layer_store.len() { + self.layer_to_ubo_map.push(None); + } + for _ in 0..self.tasks.len() { + self.tile_to_ubo_map.push(None); + } + + loop { + // Pull next primitive + let mut batch = None; + for (task_index, task) in self.tasks.iter_mut().enumerate() { + let next_item = match task.items.pop() { + Some(next_item) => next_item, + None => continue, + }; + match next_item { + AlphaRenderItem::Composite(info) => { + batch = Some(PrimitiveBatch::composite(task.child_rects[0], + task.child_rects[1], + task.target_rect, + info)); + break; + } + AlphaRenderItem::Blend(child_index, opacity) => { + batch = Some(PrimitiveBatch::blend(task.child_rects[child_index], + task.target_rect, + opacity)); + break; + } + AlphaRenderItem::Primitive(sc_index, prim_index) => { + // See if this task fits into the tile UBO + let layer = &ctx.layer_store[sc_index.0]; + let prim = &ctx.prim_store[prim_index.0]; + let transform_kind = layer.xf_rect.as_ref().unwrap().kind; + let (layer_ubo_index, index_in_layer_ubo) = + AlphaBatcher::add_layer_to_ubo(&mut self.layer_ubos, + &mut self.layer_to_ubo_map, + sc_index, + ctx); + let (tile_ubo_index, index_in_tile_ubo) = + AlphaBatcher::add_tile_to_ubo(&mut self.tile_ubos, + &mut self.tile_to_ubo_map, + TaskIndex(task_index), + task, + ctx); + let mut new_batch = PrimitiveBatch::new(prim, + transform_kind, + layer_ubo_index, + tile_ubo_index); + let ok = packed_primitive_cache.add_to_batch(prim_index, + &mut new_batch, + index_in_layer_ubo, + index_in_tile_ubo, + transform_kind); + debug_assert!(ok); + batch = Some(new_batch); + break; + } + } + } + + let mut batch = match batch { + Some(batch) => batch, + None => break, + }; + for (task_index, task) in self.tasks.iter_mut().enumerate() { + loop { + let next_item = match task.items.pop() { + Some(next_item) => next_item, + None => break, + }; + match next_item { + AlphaRenderItem::Composite(info) => { + if !batch.pack_composite(task.child_rects[0], + task.child_rects[1], + task.target_rect, + info) { + task.items.push(next_item); + break; + } + } + AlphaRenderItem::Blend(child_index, opacity) => { + if !batch.pack_blend(task.child_rects[child_index], + task.target_rect, + opacity) { + task.items.push(next_item); + break; + } + } + AlphaRenderItem::Primitive(sc_index, prim_index) => { + let layer = &ctx.layer_store[sc_index.0]; + let transform_kind = layer.xf_rect.as_ref().unwrap().kind; + let (layer_ubo_index, index_in_layer_ubo) = + AlphaBatcher::add_layer_to_ubo(&mut self.layer_ubos, + &mut self.layer_to_ubo_map, + sc_index, + ctx); + let (tile_ubo_index, index_in_tile_ubo) = + AlphaBatcher::add_tile_to_ubo(&mut self.tile_ubos, + &mut self.tile_to_ubo_map, + TaskIndex(task_index), + task, + ctx); + + if layer_ubo_index != batch.layer_ubo_index || + tile_ubo_index != batch.tile_ubo_index || + !packed_primitive_cache.add_to_batch(prim_index, + &mut batch, + index_in_layer_ubo, + index_in_tile_ubo, + transform_kind) { + task.items.push(next_item); + break; + } + } + } + } + } + + self.batches.push(batch); + } + } +} + +struct RenderTargetContext<'a> { + layer_store: &'a Vec, + prim_store: &'a Vec, + resource_cache: &'a ResourceCache, + device_pixel_ratio: f32, + frame_id: FrameId, + alpha_batch_max_tiles: usize, + alpha_batch_max_layers: usize, +} + +pub struct RenderTarget { + pub is_framebuffer: bool, + page_allocator: TexturePage, + tasks: Vec, + + pub alpha_batchers: Vec, +} + +impl RenderTarget { + fn new(is_framebuffer: bool) -> RenderTarget { + RenderTarget { + is_framebuffer: is_framebuffer, + page_allocator: TexturePage::new(TextureId(0), RENDERABLE_CACHE_SIZE.0 as u32), + tasks: Vec::new(), + + alpha_batchers: Vec::new(), + } + } + + fn add_render_task(&mut self, task: RenderTask) { + self.tasks.push(task); + } + + fn build(&mut self, packed_primitive_cache: &PackedPrimitiveCache, ctx: &RenderTargetContext) { + // Step through each task, adding to batches as appropriate. + + for task in self.tasks.drain(..) { + let target_rect = task.get_target_rect(); + + match task.kind { + RenderTaskKind::Alpha(info) => { + let need_new_batcher = self.alpha_batchers.is_empty() || + self.alpha_batchers.last().unwrap().tasks.len() == 64; + + if need_new_batcher { + self.alpha_batchers.push(AlphaBatcher::new()); + } + + self.alpha_batchers.last_mut().unwrap().add_task(AlphaBatchTask { + target_rect: target_rect, + actual_rect: info.actual_rect, + items: info.items, + child_rects: task.child_locations.clone(), // TODO(gw): Remove clone somehow!? + }); + } + } + } + + for ab in &mut self.alpha_batchers { + ab.build(packed_primitive_cache, ctx); + } + } +} + +pub struct RenderPhase { + pub targets: Vec, +} + +impl RenderPhase { + fn new(max_target_count: usize) -> RenderPhase { + let mut targets = Vec::with_capacity(max_target_count); + for index in 0..max_target_count { + targets.push(RenderTarget::new(index == max_target_count-1)); + } + + RenderPhase { + targets: targets, + } + } + + fn add_compiled_screen_tile(&mut self, + mut tile: CompiledScreenTile) -> Option { + debug_assert!(tile.required_target_count <= self.targets.len()); + + let ok = tile.main_render_task.alloc_if_required(self.targets.len() - 1, + &mut self.targets); + + if ok { + tile.main_render_task.assign_to_targets(self.targets.len() - 1, + &mut self.targets); + None + } else { + Some(tile) + } + } + + fn build(&mut self, packed_primitive_cache: &PackedPrimitiveCache, ctx: &RenderTargetContext) { + for target in &mut self.targets { + target.build(packed_primitive_cache, ctx); + } + } +} + +#[derive(Debug)] +enum RenderTaskLocation { + Fixed(Rect), + Dynamic(Option>, Size2D), +} + +#[derive(Debug)] +enum AlphaRenderItem { + Primitive(StackingContextIndex, PrimitiveIndex), + Blend(usize, f32), + Composite(PackedCompositeInfo), +} + +#[derive(Debug)] +struct AlphaRenderTask { + actual_rect: Rect, + items: Vec, + children: Vec, +} + +impl AlphaRenderTask { + fn new(actual_rect: Rect) -> AlphaRenderTask { + AlphaRenderTask { + actual_rect: actual_rect, + items: Vec::new(), + children: Vec::new(), + } + } +} + +#[derive(Debug)] +enum RenderTaskKind { + Alpha(AlphaRenderTask), +} + +#[derive(Debug)] +struct RenderTask { + location: RenderTaskLocation, + children: Vec, + child_locations: Vec>, + kind: RenderTaskKind, +} + +impl RenderTask { + fn from_primitives(mut task: AlphaRenderTask, + location: RenderTaskLocation, + size: Size2D) -> RenderTask { + let mut children = Vec::new(); + for child in task.children.drain(..) { + let location = RenderTaskLocation::Dynamic(None, size); + children.push(RenderTask::from_primitives(child, location, size)); + } + + task.items.reverse(); + + RenderTask { + children: children, + child_locations: Vec::new(), + location: location, + kind: RenderTaskKind::Alpha(task), + } + } + + fn get_target_rect(&self) -> Rect { + match self.location { + RenderTaskLocation::Fixed(rect) => rect, + RenderTaskLocation::Dynamic(origin, size) => { + Rect::new(origin.expect("Should have been allocated by now!"), + size) + } + } + } + + fn assign_to_targets(mut self, + target_index: usize, + targets: &mut Vec) { + for child in self.children.drain(..) { + self.child_locations.push(child.get_target_rect()); + child.assign_to_targets(target_index - 1, targets); + } + + // Sanity check - can be relaxed if needed + match self.location { + RenderTaskLocation::Fixed(..) => { + debug_assert!(target_index == targets.len() - 1); + } + RenderTaskLocation::Dynamic(..) => { + debug_assert!(target_index < targets.len() - 1); + } + } + + let target = &mut targets[target_index]; + target.add_render_task(self); + } + + fn alloc_if_required(&mut self, + target_index: usize, + targets: &mut Vec) -> bool { + match self.location { + RenderTaskLocation::Fixed(..) => {} + RenderTaskLocation::Dynamic(ref mut origin, ref size) => { + let target = &mut targets[target_index]; + + let alloc_size = Size2D::new(size.width.0 as u32, + size.height.0 as u32); + + let alloc_origin = target.page_allocator.allocate(&alloc_size); + + match alloc_origin { + Some(alloc_origin) => { + *origin = Some(Point2D::new(DevicePixel(alloc_origin.x as i32), + DevicePixel(alloc_origin.y as i32))); + } + None => { + return false; + } + } + } + } + + for child in &mut self.children { + if !child.alloc_if_required(target_index - 1, + targets) { + return false; + } + } + + true + } + + fn max_depth(&self, + depth: usize, + max_depth: &mut usize) { + let depth = depth + 1; + *max_depth = cmp::max(*max_depth, depth); + for child in &self.children { + child.max_depth(depth, max_depth); + } + } +} + +pub const SCREEN_TILE_SIZE: i32 = 64; +pub const RENDERABLE_CACHE_SIZE: DevicePixel = DevicePixel(2048); +const MAX_STOPS_PER_ANGLE_GRADIENT: usize = 8; + +#[derive(Debug, Clone)] +pub struct DebugRect { + pub label: String, + pub color: ColorF, + pub rect: Rect, +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub enum TransformedRectKind { + AxisAligned, + Complex, +} + +#[derive(Debug, Clone)] +struct TransformedRect { + local_rect: Rect, + bounding_rect: Rect, + vertices: [Point4D; 4], + kind: TransformedRectKind, +} + +impl TransformedRect { + fn new(rect: &Rect, + transform: &Matrix4D, + device_pixel_ratio: f32) -> TransformedRect { + + let kind = if transform.can_losslessly_transform_and_perspective_project_a_2d_rect() { + TransformedRectKind::AxisAligned + } else { + TransformedRectKind::Complex + }; + +/* + match kind { + TransformedRectKind::AxisAligned => { + let v0 = transform.transform_point(&rect.origin); + let v1 = transform.transform_point(&rect.top_right()); + let v2 = transform.transform_point(&rect.bottom_left()); + let v3 = transform.transform_point(&rect.bottom_right()); + + let screen_min_dp = Point2D::new(DevicePixel((v0.x * device_pixel_ratio).floor() as i32), + DevicePixel((v0.y * device_pixel_ratio).floor() as i32)); + let screen_max_dp = Point2D::new(DevicePixel((v3.x * device_pixel_ratio).ceil() as i32), + DevicePixel((v3.y * device_pixel_ratio).ceil() as i32)); + + let screen_rect_dp = Rect::new(screen_min_dp, Size2D::new(screen_max_dp.x - screen_min_dp.x, + screen_max_dp.y - screen_min_dp.y)); + + TransformedRect { + local_rect: *rect, + vertices: [ + Point4D::new(v0.x, v0.y, 0.0, 1.0), + Point4D::new(v1.x, v1.y, 0.0, 1.0), + Point4D::new(v2.x, v2.y, 0.0, 1.0), + Point4D::new(v3.x, v3.y, 0.0, 1.0), + ], + bounding_rect: screen_rect_dp, + kind: kind, + } + } + TransformedRectKind::Complex => { + */ + let vertices = [ + transform.transform_point4d(&Point4D::new(rect.origin.x, + rect.origin.y, + 0.0, + 1.0)), + transform.transform_point4d(&Point4D::new(rect.bottom_left().x, + rect.bottom_left().y, + 0.0, + 1.0)), + transform.transform_point4d(&Point4D::new(rect.bottom_right().x, + rect.bottom_right().y, + 0.0, + 1.0)), + transform.transform_point4d(&Point4D::new(rect.top_right().x, + rect.top_right().y, + 0.0, + 1.0)), + ]; + + let mut screen_min: Point2D = SCREEN_MIN; + let mut screen_max: Point2D = SCREEN_MAX; + + for vertex in &vertices { + let inv_w = 1.0 / vertex.w; + let vx = vertex.x * inv_w; + let vy = vertex.y * inv_w; + screen_min.x = screen_min.x.min(vx); + screen_min.y = screen_min.y.min(vy); + screen_max.x = screen_max.x.max(vx); + screen_max.y = screen_max.y.max(vy); + } + + let screen_min_dp = Point2D::new(DevicePixel((screen_min.x * device_pixel_ratio).floor() as i32), + DevicePixel((screen_min.y * device_pixel_ratio).floor() as i32)); + let screen_max_dp = Point2D::new(DevicePixel((screen_max.x * device_pixel_ratio).ceil() as i32), + DevicePixel((screen_max.y * device_pixel_ratio).ceil() as i32)); + + let screen_rect_dp = Rect::new(screen_min_dp, Size2D::new(screen_max_dp.x - screen_min_dp.x, + screen_max_dp.y - screen_min_dp.y)); + + TransformedRect { + local_rect: *rect, + vertices: vertices, + bounding_rect: screen_rect_dp, + kind: kind, + } + /* + } + }*/ + } +} + +#[derive(Debug)] +struct RectanglePrimitive { + color: ColorF, +} + +#[derive(Debug)] +struct TextPrimitive { + color: ColorF, + font_key: FontKey, + size: Au, + blur_radius: Au, + glyph_range: ItemRange, +} + +#[derive(Debug)] +struct BoxShadowPrimitive { + src_rect: Rect, + bs_rect: Rect, + color: ColorF, + blur_radius: f32, + spread_radius: f32, + border_radius: f32, + clip_mode: BoxShadowClipMode, + metrics: BoxShadowMetrics, +} + +#[derive(Debug)] +struct BorderPrimitive { + tl_outer: Point2D, + tl_inner: Point2D, + tr_outer: Point2D, + tr_inner: Point2D, + bl_outer: Point2D, + bl_inner: Point2D, + br_outer: Point2D, + br_inner: Point2D, + left_width: f32, + top_width: f32, + right_width: f32, + bottom_width: f32, + radius: BorderRadius, + left_color: ColorF, + top_color: ColorF, + right_color: ColorF, + bottom_color: ColorF, + left_style: BorderStyle, + top_style: BorderStyle, + right_style: BorderStyle, + bottom_style: BorderStyle, +} + +#[derive(Debug)] +enum ImagePrimitiveKind { + Image(ImageKey, ImageRendering, Size2D), + WebGL(WebGLContextId), +} + +#[derive(Debug)] +struct ImagePrimitive { + kind: ImagePrimitiveKind, +} + +#[derive(Debug)] +struct GradientPrimitive { + stops_range: ItemRange, + kind: GradientType, + start_point: Point2D, + end_point: Point2D, +} + +#[derive(Debug)] +enum PrimitiveDetails { + Rectangle(RectanglePrimitive), + Text(TextPrimitive), + Image(ImagePrimitive), + Border(BorderPrimitive), + Gradient(GradientPrimitive), + BoxShadow(BoxShadowPrimitive), +} + +#[derive(Copy, Clone, Debug)] +struct LayerPackedPrimitiveRangeStartOffsets { + rectangles: usize, + rectangles_clip: usize, + borders: usize, + box_shadows: usize, + text: usize, + images: usize, + gradients: usize, +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub struct PrimitiveIndex(usize); + +#[derive(Debug)] +struct Primitive { + rect: Rect, + local_clip_rect: Rect, + complex_clip: Option>, + details: PrimitiveDetails, +} + +impl Primitive { + fn pack(&self, + index: PrimitiveIndex, + cache: &mut PackedPrimitiveCache, + auxiliary_lists: &AuxiliaryLists, + ctx: &RenderTargetContext) { + // TODO(pcwalton): Only pack visible primitives! + cache.init_packed_primitive(index); + match self.details { + PrimitiveDetails::Rectangle(ref details) => { + match self.complex_clip { + Some(ref clip) => { + let packed = PackedPrimitive::RectangleClip(PackedRectanglePrimitiveClip { + common: PackedPrimitiveInfo { + padding: 0, + tile_index: 0, + layer_index: 0, + part: PrimitivePart::Invalid, + local_clip_rect: self.local_clip_rect, + local_rect: self.rect, + }, + color: details.color, + clip: (**clip).clone(), + }); + cache.add_packed_primitive(index, packed, TextureId(0)) + } + None => { + let packed = PackedPrimitive::Rectangle(PackedRectanglePrimitive { + common: PackedPrimitiveInfo { + padding: 0, + tile_index: 0, + layer_index: 0, + part: PrimitivePart::Invalid, + local_clip_rect: self.local_clip_rect, + local_rect: self.rect, + }, + color: details.color, + }); + cache.add_packed_primitive(index, packed, TextureId(0)) + } + } + } + PrimitiveDetails::Image(ref details) => { + let (texture_id, uv_rect, stretch_size) = match details.kind { + ImagePrimitiveKind::Image(image_key, image_rendering, stretch_size) => { + let info = ctx.resource_cache.get_image(image_key, + image_rendering, + ctx.frame_id); + (info.texture_id, info.uv_rect(), stretch_size) + } + ImagePrimitiveKind::WebGL(context_id) => { + let texture_id = ctx.resource_cache.get_webgl_texture(&context_id); + let uv = RectUv { + top_left: Point2D::new(0.0, 1.0), + top_right: Point2D::new(1.0, 1.0), + bottom_left: Point2D::zero(), + bottom_right: Point2D::new(1.0, 0.0), + }; + (texture_id, uv, self.rect.size) + } + }; + + let packed = match self.complex_clip { + Some(ref clip) => { + PackedPrimitive::ImageClip(PackedImagePrimitiveClip { + common: PackedPrimitiveInfo { + padding: 0, + tile_index: 0, + layer_index: 0, + part: PrimitivePart::Invalid, + local_clip_rect: self.local_clip_rect, + local_rect: self.rect, + }, + st0: uv_rect.top_left, + st1: uv_rect.bottom_right, + stretch_size: stretch_size, + padding: [0, 0], + clip: (**clip).clone(), + }) + } + None => { + PackedPrimitive::Image(PackedImagePrimitive { + common: PackedPrimitiveInfo { + padding: 0, + tile_index: 0, + layer_index: 0, + part: PrimitivePart::Invalid, + local_clip_rect: self.local_clip_rect, + local_rect: self.rect, + }, + st0: uv_rect.top_left, + st1: uv_rect.bottom_right, + stretch_size: stretch_size, + padding: [0, 0], + }) + } + }; + + cache.add_packed_primitive(index, packed, texture_id) + } + PrimitiveDetails::Border(ref details) => { + let inner_radius = BorderRadius { + top_left: Size2D::new(details.radius.top_left.width - details.left_width, + details.radius.top_left.width - details.left_width), + top_right: Size2D::new(details.radius.top_right.width - details.right_width, + details.radius.top_right.width - details.right_width), + bottom_left: + Size2D::new(details.radius.bottom_left.width - details.left_width, + details.radius.bottom_left.width - details.left_width), + bottom_right: + Size2D::new(details.radius.bottom_right.width - details.right_width, + details.radius.bottom_right.width - details.right_width), + }; + + cache.add_packed_primitive(index, PackedPrimitive::Border(PackedBorderPrimitive { + common: PackedPrimitiveInfo { + padding: 0, + tile_index: 0, + layer_index: 0, + part: PrimitivePart::TopLeft, + local_clip_rect: self.local_clip_rect, + local_rect: rect_from_points_f(details.tl_outer.x, + details.tl_outer.y, + details.tl_inner.x, + details.tl_inner.y), + }, + vertical_color: details.top_color, + horizontal_color: details.left_color, + outer_radius_x: details.radius.top_left.width, + outer_radius_y: details.radius.top_left.height, + inner_radius_x: inner_radius.top_left.width, + inner_radius_y: inner_radius.top_left.height, + top_style: details.top_style as u32, + right_style: details.right_style as u32, + bottom_style: details.bottom_style as u32, + left_style: details.bottom_style as u32, + }), TextureId(0)); + + cache.add_packed_primitive(index, PackedPrimitive::Border(PackedBorderPrimitive { + common: PackedPrimitiveInfo { + padding: 0, + tile_index: 0, + layer_index: 0, + part: PrimitivePart::TopRight, + local_clip_rect: self.local_clip_rect, + local_rect: rect_from_points_f(details.tr_inner.x, + details.tr_outer.y, + details.tr_outer.x, + details.tr_inner.y), + }, + vertical_color: details.top_color, + horizontal_color: details.right_color, + outer_radius_x: details.radius.top_right.width, + outer_radius_y: details.radius.top_right.height, + inner_radius_x: inner_radius.top_right.width, + inner_radius_y: inner_radius.top_right.height, + top_style: details.top_style as u32, + right_style: details.right_style as u32, + bottom_style: details.bottom_style as u32, + left_style: details.bottom_style as u32, + }), TextureId(0)); + + cache.add_packed_primitive(index, PackedPrimitive::Border(PackedBorderPrimitive { + common: PackedPrimitiveInfo { + padding: 0, + tile_index: 0, + layer_index: 0, + part: PrimitivePart::BottomLeft, + local_clip_rect: self.local_clip_rect, + local_rect: rect_from_points_f(details.bl_outer.x, + details.bl_inner.y, + details.bl_inner.x, + details.bl_outer.y), + }, + vertical_color: details.bottom_color, + horizontal_color: details.left_color, + outer_radius_x: details.radius.bottom_left.width, + outer_radius_y: details.radius.bottom_left.height, + inner_radius_x: inner_radius.bottom_left.width, + inner_radius_y: inner_radius.bottom_left.height, + top_style: details.top_style as u32, + right_style: details.right_style as u32, + bottom_style: details.bottom_style as u32, + left_style: details.bottom_style as u32, + }), TextureId(0)); + + cache.add_packed_primitive(index, PackedPrimitive::Border(PackedBorderPrimitive { + common: PackedPrimitiveInfo { + padding: 0, + tile_index: 0, + layer_index: 0, + part: PrimitivePart::BottomRight, + local_clip_rect: self.local_clip_rect, + local_rect: rect_from_points_f(details.br_inner.x, + details.br_inner.y, + details.br_outer.x, + details.br_outer.y), + }, + vertical_color: details.bottom_color, + horizontal_color: details.right_color, + outer_radius_x: details.radius.bottom_right.width, + outer_radius_y: details.radius.bottom_right.height, + inner_radius_x: inner_radius.bottom_right.width, + inner_radius_y: inner_radius.bottom_right.height, + top_style: details.top_style as u32, + right_style: details.right_style as u32, + bottom_style: details.bottom_style as u32, + left_style: details.bottom_style as u32, + }), TextureId(0)); + + cache.add_packed_primitive(index, PackedPrimitive::Border(PackedBorderPrimitive { + common: PackedPrimitiveInfo { + padding: 0, + tile_index: 0, + layer_index: 0, + part: PrimitivePart::Left, + local_clip_rect: self.local_clip_rect, + local_rect: rect_from_points_f(details.tl_outer.x, + details.tl_inner.y, + details.tl_outer.x + details.left_width, + details.bl_inner.y), + }, + vertical_color: details.left_color, + horizontal_color: details.left_color, + outer_radius_x: 0.0, + outer_radius_y: 0.0, + inner_radius_x: 0.0, + inner_radius_y: 0.0, + top_style: details.top_style as u32, + right_style: details.right_style as u32, + bottom_style: details.bottom_style as u32, + left_style: details.bottom_style as u32, + }), TextureId(0)); + + cache.add_packed_primitive(index, PackedPrimitive::Border(PackedBorderPrimitive { + common: PackedPrimitiveInfo { + padding: 0, + tile_index: 0, + layer_index: 0, + part: PrimitivePart::Right, + local_clip_rect: self.local_clip_rect, + local_rect: rect_from_points_f(details.tr_outer.x - details.right_width, + details.tr_inner.y, + details.br_outer.x, + details.br_inner.y), + }, + vertical_color: details.right_color, + horizontal_color: details.right_color, + outer_radius_x: 0.0, + outer_radius_y: 0.0, + inner_radius_x: 0.0, + inner_radius_y: 0.0, + top_style: details.top_style as u32, + right_style: details.right_style as u32, + bottom_style: details.bottom_style as u32, + left_style: details.bottom_style as u32, + }), TextureId(0)); + + cache.add_packed_primitive(index, PackedPrimitive::Border(PackedBorderPrimitive { + common: PackedPrimitiveInfo { + padding: 0, + tile_index: 0, + layer_index: 0, + part: PrimitivePart::Top, + local_clip_rect: self.local_clip_rect, + local_rect: rect_from_points_f(details.tl_inner.x, + details.tl_outer.y, + details.tr_inner.x, + details.tr_outer.y + details.top_width), + }, + vertical_color: details.top_color, + horizontal_color: details.top_color, + outer_radius_x: 0.0, + outer_radius_y: 0.0, + inner_radius_x: 0.0, + inner_radius_y: 0.0, + top_style: details.top_style as u32, + right_style: details.right_style as u32, + bottom_style: details.bottom_style as u32, + left_style: details.bottom_style as u32, + }), TextureId(0)); + + cache.add_packed_primitive(index, PackedPrimitive::Border(PackedBorderPrimitive { + common: PackedPrimitiveInfo { + padding: 0, + tile_index: 0, + layer_index: 0, + part: PrimitivePart::Bottom, + local_clip_rect: self.local_clip_rect, + local_rect: rect_from_points_f(details.bl_inner.x, + details.bl_outer.y - details.bottom_width, + details.br_inner.x, + details.br_outer.y), + }, + vertical_color: details.bottom_color, + horizontal_color: details.bottom_color, + outer_radius_x: 0.0, + outer_radius_y: 0.0, + inner_radius_x: 0.0, + inner_radius_y: 0.0, + top_style: details.top_style as u32, + right_style: details.right_style as u32, + bottom_style: details.bottom_style as u32, + left_style: details.bottom_style as u32, + }), TextureId(0)); + } + PrimitiveDetails::Gradient(ref details) => { + match details.kind { + GradientType::Horizontal | GradientType::Vertical => { + let stops = auxiliary_lists.gradient_stops(&details.stops_range); + for i in 0..(stops.len() - 1) { + let (prev_stop, next_stop) = (&stops[i], &stops[i + 1]); + let piece_origin; + let piece_size; + match details.kind { + GradientType::Horizontal => { + let prev_x = util::lerp(details.start_point.x, + details.end_point.x, + prev_stop.offset); + let next_x = util::lerp(details.start_point.x, + details.end_point.x, + next_stop.offset); + piece_origin = Point2D::new(prev_x, self.rect.origin.y); + piece_size = Size2D::new(next_x - prev_x, + self.rect.size.height); + } + GradientType::Vertical => { + let prev_y = util::lerp(details.start_point.y, + details.end_point.y, + prev_stop.offset); + let next_y = util::lerp(details.start_point.y, + details.end_point.y, + next_stop.offset); + piece_origin = Point2D::new(self.rect.origin.x, prev_y); + piece_size = Size2D::new(self.rect.size.width, next_y - prev_y); + } + GradientType::Rotated => unreachable!(), + } + + let piece_rect = Rect::new(piece_origin, piece_size); + let mut clip = Clip::invalid(piece_rect); + + if let Some(ref prim_clip) = self.complex_clip { + if i == 0 { + clip.top_left.outer_radius_x = prim_clip.top_left + .outer_radius_x; + clip.top_left.outer_radius_y = prim_clip.top_left + .outer_radius_y; + + match details.kind { + GradientType::Horizontal => { + clip.bottom_left.outer_radius_x = + prim_clip.bottom_left.outer_radius_x; + clip.bottom_left.outer_radius_y = + prim_clip.bottom_left.outer_radius_y; + } + GradientType::Vertical => { + clip.top_right.outer_radius_x = + prim_clip.top_right.outer_radius_x; + clip.top_right.outer_radius_y = + prim_clip.top_right.outer_radius_y; + } + GradientType::Rotated => unreachable!(), + } + } + + if i == stops.len() - 2 { + clip.bottom_right.outer_radius_x = prim_clip.bottom_right + .outer_radius_x; + clip.bottom_right.outer_radius_y = prim_clip.bottom_right + .outer_radius_y; + + match details.kind { + GradientType::Horizontal => { + clip.top_right.outer_radius_x = + prim_clip.top_right.outer_radius_x; + clip.top_right.outer_radius_y = + prim_clip.top_right.outer_radius_y; + } + GradientType::Vertical => { + clip.bottom_left.outer_radius_x = + prim_clip.bottom_left.outer_radius_x; + clip.bottom_left.outer_radius_y = + prim_clip.bottom_left.outer_radius_y; + } + GradientType::Rotated => unreachable!(), + } + } + } + + cache.add_packed_primitive( + index, + PackedPrimitive::AlignedGradient(PackedAlignedGradientPrimitive { + common: PackedPrimitiveInfo { + padding: 0, + tile_index: 0, + layer_index: 0, + part: PrimitivePart::Bottom, + local_clip_rect: self.local_clip_rect, + local_rect: piece_rect, + }, + color0: prev_stop.color, + color1: next_stop.color, + padding: [0, 0, 0], + kind: details.kind, + clip: clip, + }), + TextureId(0)); + } + } + GradientType::Rotated => { + let src_stops = auxiliary_lists.gradient_stops(&details.stops_range); + if src_stops.len() > MAX_STOPS_PER_ANGLE_GRADIENT { + println!("TODO: Angle gradients with > {} stops", + MAX_STOPS_PER_ANGLE_GRADIENT); + return + } + + let mut stops: [f32; MAX_STOPS_PER_ANGLE_GRADIENT] = unsafe { + mem::uninitialized() + }; + let mut colors: [ColorF; MAX_STOPS_PER_ANGLE_GRADIENT] = unsafe { + mem::uninitialized() + }; + + let sx = details.start_point.x; + let ex = details.end_point.x; + + let (sp, ep) = if sx > ex { + for (stop_index, stop) in src_stops.iter().rev().enumerate() { + stops[stop_index] = 1.0 - stop.offset; + colors[stop_index] = stop.color; + } + + (details.end_point, details.start_point) + } else { + for (stop_index, stop) in src_stops.iter().enumerate() { + stops[stop_index] = stop.offset; + colors[stop_index] = stop.color; + } + + (details.start_point, details.end_point) + }; + + cache.add_packed_primitive( + index, + PackedPrimitive::AngleGradient(PackedAngleGradientPrimitive { + common: PackedPrimitiveInfo { + padding: 0, + tile_index: 0, + layer_index: 0, + part: PrimitivePart::Invalid, + local_clip_rect: self.local_clip_rect, + local_rect: self.rect, + }, + padding: [0, 0, 0], + start_point: sp, + end_point: ep, + stop_count: src_stops.len() as u32, + stops: stops, + colors: colors, + }), + TextureId(0)); + } + } + } + PrimitiveDetails::BoxShadow(ref details) => { + let mut rects = Vec::new(); + let inverted = match details.clip_mode { + BoxShadowClipMode::None | BoxShadowClipMode::Outset => { + subtract_rect(&self.rect, &details.src_rect, &mut rects); + 0.0 + } + BoxShadowClipMode::Inset => { + subtract_rect(&self.rect, &details.bs_rect, &mut rects); + 1.0 + } + }; + + for rect in rects { + cache.add_packed_primitive( + index, + PackedPrimitive::BoxShadow(PackedBoxShadowPrimitive { + common: PackedPrimitiveInfo { + padding: 0, + tile_index: 0, + layer_index: 0, + part: PrimitivePart::Invalid, + local_clip_rect: self.local_clip_rect, + local_rect: rect, + }, + color: details.color, + + border_radii: Point2D::new(details.border_radius, + details.border_radius), + blur_radius: details.blur_radius, + inverted: inverted, + bs_rect: details.bs_rect, + src_rect: details.src_rect, + }), + TextureId(0)) + } + } + PrimitiveDetails::Text(ref details) => { + let src_glyphs = auxiliary_lists.glyph_instances(&details.glyph_range); + let mut glyph_key = GlyphKey::new(details.font_key, + details.size, + details.blur_radius, + src_glyphs[0].index); + let blur_offset = details.blur_radius.to_f32_px() * + (BLUR_INFLATION_FACTOR as f32) / 2.0; + + for glyph in src_glyphs { + glyph_key.index = glyph.index; + + let image_info = match ctx.resource_cache.get_glyph(&glyph_key, ctx.frame_id) { + None => continue, + Some(image_info) => image_info, + }; + + // TODO(gw): Need a general solution to handle multiple texture pages per tile + // in WR2! + let texture_id = image_info.texture_id; + assert!(texture_id == TextureId(0) || texture_id == image_info.texture_id); + + let x = glyph.x + image_info.user_data.x0 as f32 / ctx.device_pixel_ratio - + blur_offset; + let y = glyph.y - image_info.user_data.y0 as f32 / ctx.device_pixel_ratio - + blur_offset; + + let width = image_info.requested_rect.size.width as f32 / + ctx.device_pixel_ratio; + let height = image_info.requested_rect.size.height as f32 / + ctx.device_pixel_ratio; + + let uv_rect = image_info.uv_rect(); + let local_rect = Rect::new(Point2D::new(x, y), Size2D::new(width, height)); + + cache.add_packed_primitive( + index, + PackedPrimitive::Text(PackedGlyphPrimitive { + common: PackedPrimitiveInfo { + padding: 0, + tile_index: 0, + layer_index: 0, + part: PrimitivePart::Invalid, + local_clip_rect: self.local_clip_rect, + local_rect: local_rect, + }, + color: details.color, + st0: uv_rect.top_left, + st1: uv_rect.bottom_right, + }), + texture_id) + } + } + } + } +} + +#[repr(u32)] +#[derive(Debug, Copy, Clone)] +enum PrimitivePart { + Invalid = 0, + TopLeft, + TopRight, + BottomLeft, + BottomRight, + Top, + Left, + Bottom, + Right, +} + +// All Packed Primitives below must be 16 byte aligned. +#[derive(Debug)] +pub struct PackedTile { + actual_rect: Rect, + target_rect: Rect, +} + +#[derive(Debug)] +pub struct PackedLayer { + transform: Matrix4D, + inv_transform: Matrix4D, + world_clip_rect: Rect, + screen_vertices: [Point4D; 4], +} + +#[derive(Debug, Clone)] +pub struct PackedPrimitiveInfo { + layer_index: u32, + tile_index: u32, + part: PrimitivePart, + padding: u32, + local_clip_rect: Rect, + local_rect: Rect, +} + +#[derive(Debug, Clone)] +pub struct PackedRectanglePrimitiveClip { + common: PackedPrimitiveInfo, + color: ColorF, + clip: Clip, +} + +#[derive(Debug, Clone)] +pub struct PackedRectanglePrimitive { + common: PackedPrimitiveInfo, + color: ColorF, +} + +#[derive(Debug, Clone)] +pub struct PackedGlyphPrimitive { + common: PackedPrimitiveInfo, + color: ColorF, + st0: Point2D, + st1: Point2D, +} + +#[derive(Debug, Clone)] +pub struct PackedImagePrimitive { + common: PackedPrimitiveInfo, + st0: Point2D, + st1: Point2D, + stretch_size: Size2D, + padding: [u32; 2], +} + +#[derive(Debug, Clone)] +pub struct PackedImagePrimitiveClip { + common: PackedPrimitiveInfo, + st0: Point2D, + st1: Point2D, + stretch_size: Size2D, + padding: [u32; 2], + clip: Clip, +} + +#[derive(Debug, Clone)] +pub struct PackedAlignedGradientPrimitive { + common: PackedPrimitiveInfo, + color0: ColorF, + color1: ColorF, + kind: GradientType, + padding: [u32; 3], + clip: Clip, +} + +// TODO(gw): Angle gradient only support 8 stops due +// to limits of interpolators. FIXME! +#[derive(Debug, Clone)] +pub struct PackedAngleGradientPrimitive { + common: PackedPrimitiveInfo, + start_point: Point2D, + end_point: Point2D, + stop_count: u32, + padding: [u32; 3], + colors: [ColorF; MAX_STOPS_PER_ANGLE_GRADIENT], + stops: [f32; MAX_STOPS_PER_ANGLE_GRADIENT], +} + +#[derive(Debug, Clone)] +pub struct PackedBorderPrimitive { + common: PackedPrimitiveInfo, + vertical_color: ColorF, + horizontal_color: ColorF, + outer_radius_x: f32, + outer_radius_y: f32, + inner_radius_x: f32, + inner_radius_y: f32, + top_style: u32, + right_style: u32, + bottom_style: u32, + left_style: u32, +} + +#[derive(Debug, Clone)] +pub struct PackedBoxShadowPrimitive { + common: PackedPrimitiveInfo, + color: ColorF, + border_radii: Point2D, + blur_radius: f32, + inverted: f32, + bs_rect: Rect, + src_rect: Rect, +} + +#[derive(Debug, Clone)] +pub struct PackedBlendPrimitive { + target_rect: Rect, + src_rect: Rect, + opacity: f32, + padding: [u32; 3], +} + +#[derive(Debug, Copy, Clone)] +struct PackedCompositeInfo { + kind: u32, + op: u32, + padding: [u32; 2], + amount: f32, + padding1: [u32; 3], +} + +impl PackedCompositeInfo { + fn new(ops: &Vec) -> PackedCompositeInfo { + // TODO(gw): Support chained filters + let op = &ops[0]; + + let (kind, op, amount) = match op { + &CompositionOp::MixBlend(mode) => { + (0, mode as u32, 0.0) + } + &CompositionOp::Filter(filter) => { + let (filter_mode, amount) = match filter { + LowLevelFilterOp::Blur(..) => (0, 0.0), + LowLevelFilterOp::Contrast(amount) => (1, amount.to_f32_px()), + LowLevelFilterOp::Grayscale(amount) => (2, amount.to_f32_px()), + LowLevelFilterOp::HueRotate(angle) => (3, (angle as f32) / ANGLE_FLOAT_TO_FIXED), + LowLevelFilterOp::Invert(amount) => (4, amount.to_f32_px()), + LowLevelFilterOp::Saturate(amount) => (5, amount.to_f32_px()), + LowLevelFilterOp::Sepia(amount) => (6, amount.to_f32_px()), + LowLevelFilterOp::Brightness(_) | + LowLevelFilterOp::Opacity(_) => { + // Expressible using GL blend modes, so not handled + // here. + unreachable!() + } + }; + + (1, filter_mode, amount) + } + }; + + PackedCompositeInfo { + kind: kind, + op: op, + padding: [0, 0], + amount: amount, + padding1: [0, 0, 0], + } + } +} + +#[derive(Debug)] +pub struct PackedCompositePrimitive { + rect0: Rect, + rect1: Rect, + target_rect: Rect, + info: PackedCompositeInfo, +} + +#[derive(Debug)] +pub enum PrimitiveBatchData { + Rectangles(Vec), + RectanglesClip(Vec), + Borders(Vec), + BoxShadows(Vec), + Text(Vec), + Image(Vec), + ImageClip(Vec), + Blend(Vec), + Composite(Vec), + AlignedGradient(Vec), + AngleGradient(Vec), +} + +enum PackedPrimitive { + Rectangle(PackedRectanglePrimitive), + RectangleClip(PackedRectanglePrimitiveClip), + Border(PackedBorderPrimitive), + BoxShadow(PackedBoxShadowPrimitive), + Text(PackedGlyphPrimitive), + Image(PackedImagePrimitive), + ImageClip(PackedImagePrimitiveClip), + AlignedGradient(PackedAlignedGradientPrimitive), + AngleGradient(PackedAngleGradientPrimitive), +} + +#[derive(Debug)] +pub struct PrimitiveBatch { + pub transform_kind: TransformedRectKind, + pub color_texture_id: TextureId, // TODO(gw): Expand to sampler array to handle all glyphs! + pub layer_ubo_index: usize, + pub tile_ubo_index: usize, + pub data: PrimitiveBatchData, +} + +impl PrimitiveBatch { + fn blend(src_rect: Rect, + target_rect: Rect, + opacity: f32) -> PrimitiveBatch { + let blend = PackedBlendPrimitive { + src_rect: src_rect, + target_rect: target_rect, + opacity: opacity, + padding: [0, 0, 0], + }; + + PrimitiveBatch { + color_texture_id: TextureId(0), + transform_kind: TransformedRectKind::AxisAligned, + layer_ubo_index: 0, + tile_ubo_index: 0, + data: PrimitiveBatchData::Blend(vec![blend]), + } + } + + fn composite(first_src_rect: Rect, + second_src_rect: Rect, + target_rect: Rect, + info: PackedCompositeInfo) -> PrimitiveBatch { + let composite = PackedCompositePrimitive { + rect0: first_src_rect, + rect1: second_src_rect, + target_rect: target_rect, + info: info, + }; + + PrimitiveBatch { + color_texture_id: TextureId(0), + transform_kind: TransformedRectKind::AxisAligned, + layer_ubo_index: 0, + tile_ubo_index: 0, + data: PrimitiveBatchData::Composite(vec![composite]), + } + } + + fn pack_blend(&mut self, + src_rect: Rect, + target_rect: Rect, + opacity: f32) -> bool { + match &mut self.data { + &mut PrimitiveBatchData::Blend(ref mut ubo_data) => { + ubo_data.push(PackedBlendPrimitive { + opacity: opacity, + padding: [0, 0, 0], + src_rect: src_rect, + target_rect: target_rect, + }); + + true + } + _ => false + } + } + + fn pack_composite(&mut self, + rect0: Rect, + rect1: Rect, + target_rect: Rect, + info: PackedCompositeInfo) -> bool { + match &mut self.data { + &mut PrimitiveBatchData::Composite(ref mut ubo_data) => { + ubo_data.push(PackedCompositePrimitive { + rect0: rect0, + rect1: rect1, + target_rect: target_rect, + info: info, + }); + + true + } + _ => false + } + } + + fn new(prim: &Primitive, + transform_kind: TransformedRectKind, + layer_ubo_index: usize, + tile_ubo_index: usize) -> PrimitiveBatch { + let data = match prim.details { + PrimitiveDetails::Rectangle(..) => { + match prim.complex_clip { + Some(..) => PrimitiveBatchData::RectanglesClip(Vec::new()), + None => PrimitiveBatchData::Rectangles(Vec::new()), + } + } + PrimitiveDetails::Border(..) => { + PrimitiveBatchData::Borders(Vec::new()) + } + PrimitiveDetails::BoxShadow(..) => { + PrimitiveBatchData::BoxShadows(Vec::new()) + } + PrimitiveDetails::Text(..) => { + PrimitiveBatchData::Text(Vec::new()) + } + PrimitiveDetails::Image(..) => { + match prim.complex_clip { + Some(..) => PrimitiveBatchData::ImageClip(Vec::new()), + None => PrimitiveBatchData::Image(Vec::new()), + } + } + PrimitiveDetails::Gradient(ref details) => { + match details.kind { + GradientType::Rotated => { + PrimitiveBatchData::AngleGradient(Vec::new()) + } + GradientType::Horizontal | GradientType::Vertical => { + PrimitiveBatchData::AlignedGradient(Vec::new()) + } + } + } + }; + + PrimitiveBatch { + color_texture_id: TextureId(0), + transform_kind: transform_kind, + layer_ubo_index: layer_ubo_index, + tile_ubo_index: tile_ubo_index, + data: data, + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct ScreenTileLayerIndex(usize); + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub struct StackingContextIndex(usize); + +enum StackingContextItem { + StackingContext(StackingContextIndex), + Primitive(PrimitiveIndex), +} + +struct StackingContext { + pipeline_id: PipelineId, + local_transform: Matrix4D, + local_rect: Rect, + local_offset: Point2D, + items: Vec, + scroll_layer_id: ScrollLayerId, + transform: Matrix4D, + xf_rect: Option, + is_valid: bool, + composition_ops: Vec, + local_clip_rect: Rect, + world_clip_rect: Option>, + parent: Option, +} + +#[derive(Debug, Copy, Clone)] +enum CompositeKind { + None, + Simple(f32), + Complex(PackedCompositeInfo), +} + +impl StackingContext { + fn build_resource_list(&self, + resource_list: &mut ResourceList, + auxiliary_lists: &AuxiliaryLists, + prim_store: &Vec) { + for item in &self.items { + let prim_index = match item { + &StackingContextItem::Primitive(prim_index) => prim_index, + &StackingContextItem::StackingContext(..) => continue, + }; + let prim = &prim_store[prim_index.0]; + match prim.details { + PrimitiveDetails::Rectangle(..) => {} + PrimitiveDetails::Gradient(..) => {} + PrimitiveDetails::Border(..) => {} + PrimitiveDetails::BoxShadow(..) => {} + PrimitiveDetails::Image(ref details) => { + match details.kind { + ImagePrimitiveKind::Image(image_key, image_rendering, _) => { + resource_list.add_image(image_key, image_rendering); + } + ImagePrimitiveKind::WebGL(..) => {} + } + } + PrimitiveDetails::Text(ref details) => { + let glyphs = auxiliary_lists.glyph_instances(&details.glyph_range); + for glyph in glyphs { + let glyph = Glyph::new(details.size, details.blur_radius, glyph.index); + resource_list.add_glyph(details.font_key, glyph); + } + } + } + } + } + + fn can_contribute_to_scene(&self) -> bool { + for op in &self.composition_ops { + match op { + &CompositionOp::Filter(filter_op) => { + match filter_op { + LowLevelFilterOp::Opacity(opacity) => { + if opacity == Au(0) { + return false + } + } + _ => {} + } + } + _ => {} + } + } + + true + } + + fn composite_kind(&self) -> CompositeKind { + if self.composition_ops.is_empty() { + return CompositeKind::None; + } + + if self.composition_ops.len() == 1 { + match self.composition_ops.first().unwrap() { + &CompositionOp::Filter(filter_op) => { + match filter_op { + LowLevelFilterOp::Opacity(opacity) => { + let opacity = opacity.to_f32_px(); + if opacity == 1.0 { + return CompositeKind::None; + } else { + return CompositeKind::Simple(opacity); + } + } + _ => {} + } + } + _ => {} + } + } + + let info = PackedCompositeInfo::new(&self.composition_ops); + CompositeKind::Complex(info) + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +struct ClipIndex(usize); + +#[derive(Debug, Clone)] +pub struct ClipCorner { + rect: Rect, + outer_radius_x: f32, + outer_radius_y: f32, + inner_radius_x: f32, + inner_radius_y: f32, +} + +impl ClipCorner { + fn invalid(rect: Rect) -> ClipCorner { + ClipCorner { + rect: rect, + outer_radius_x: 0.0, + outer_radius_y: 0.0, + inner_radius_x: 0.0, + inner_radius_y: 0.0, + } + } +} + +#[derive(Debug, Clone)] +pub struct Clip { + rect: Rect, + top_left: ClipCorner, + top_right: ClipCorner, + bottom_left: ClipCorner, + bottom_right: ClipCorner, +} + +#[derive(Debug, Clone)] +pub struct ClearTile { + pub rect: Rect, +} + +#[derive(Clone, Copy)] +pub struct FrameBuilderConfig { + max_prim_layers: usize, + max_prim_tiles: usize, +} + +impl FrameBuilderConfig { + pub fn new(max_prim_layers: usize, + max_prim_tiles: usize) -> FrameBuilderConfig { + FrameBuilderConfig { + max_prim_layers: max_prim_layers, + max_prim_tiles: max_prim_tiles, + } + } +} + +pub struct FrameBuilder { + screen_rect: Rect, + prim_store: Vec, + layer_store: Vec, + layer_stack: Vec, + device_pixel_ratio: f32, + debug: bool, + config: FrameBuilderConfig, +} + +pub struct Frame { + pub debug_rects: Vec, + pub cache_size: Size2D, + pub phases: Vec, + pub clear_tiles: Vec, +} + +impl Clip { + pub fn from_clip_region(clip: &ComplexClipRegion) -> Clip { + Clip { + rect: clip.rect, + top_left: ClipCorner { + rect: Rect::new(Point2D::new(clip.rect.origin.x, clip.rect.origin.y), + Size2D::new(clip.radii.top_left.width, clip.radii.top_left.height)), + outer_radius_x: clip.radii.top_left.width, + outer_radius_y: clip.radii.top_left.height, + inner_radius_x: 0.0, + inner_radius_y: 0.0, + }, + top_right: ClipCorner { + rect: Rect::new(Point2D::new(clip.rect.origin.x + clip.rect.size.width - clip.radii.top_right.width, + clip.rect.origin.y), + Size2D::new(clip.radii.top_right.width, clip.radii.top_right.height)), + outer_radius_x: clip.radii.top_right.width, + outer_radius_y: clip.radii.top_right.height, + inner_radius_x: 0.0, + inner_radius_y: 0.0, + }, + bottom_left: ClipCorner { + rect: Rect::new(Point2D::new(clip.rect.origin.x, + clip.rect.origin.y + clip.rect.size.height - clip.radii.bottom_left.height), + Size2D::new(clip.radii.bottom_left.width, clip.radii.bottom_left.height)), + outer_radius_x: clip.radii.bottom_left.width, + outer_radius_y: clip.radii.bottom_left.height, + inner_radius_x: 0.0, + inner_radius_y: 0.0, + }, + bottom_right: ClipCorner { + rect: Rect::new(Point2D::new(clip.rect.origin.x + clip.rect.size.width - clip.radii.bottom_right.width, + clip.rect.origin.y + clip.rect.size.height - clip.radii.bottom_right.height), + Size2D::new(clip.radii.bottom_right.width, clip.radii.bottom_right.height)), + outer_radius_x: clip.radii.bottom_right.width, + outer_radius_y: clip.radii.bottom_right.height, + inner_radius_x: 0.0, + inner_radius_y: 0.0, + }, + } + } + + fn invalid(rect: Rect) -> Clip { + Clip { + rect: rect, + top_left: ClipCorner::invalid(rect), + top_right: ClipCorner::invalid(rect), + bottom_left: ClipCorner::invalid(rect), + bottom_right: ClipCorner::invalid(rect), + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub struct ScreenTileIndex(usize); + +#[derive(Debug)] +struct CompiledScreenTile { + main_render_task: RenderTask, + required_target_count: usize, +} + +impl CompiledScreenTile { + fn new(main_render_task: RenderTask) -> CompiledScreenTile { + let mut required_target_count = 0; + main_render_task.max_depth(0, &mut required_target_count); + + CompiledScreenTile { + main_render_task: main_render_task, + required_target_count: required_target_count, + } + } +} + +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +enum TileCommand { + PushLayer(StackingContextIndex), + PopLayer, + DrawPrimitive(PrimitiveIndex), +} + +#[derive(Debug)] +struct ScreenTile { + rect: Rect, + cmds: Vec, + prim_count: usize, +} + +impl ScreenTile { + fn new(rect: Rect) -> ScreenTile { + ScreenTile { + rect: rect, + cmds: Vec::new(), + prim_count: 0, + } + } + + #[inline(always)] + fn push_layer(&mut self, sc_index: StackingContextIndex) { + self.cmds.push(TileCommand::PushLayer(sc_index)); + } + + #[inline(always)] + fn push_primitive(&mut self, prim_index: PrimitiveIndex) { + self.cmds.push(TileCommand::DrawPrimitive(prim_index)); + self.prim_count += 1; + } + + #[inline(always)] + fn pop_layer(&mut self, sc_index: StackingContextIndex) { + let last_cmd = *self.cmds.last().unwrap(); + if last_cmd == TileCommand::PushLayer(sc_index) { + self.cmds.pop(); + } else { + self.cmds.push(TileCommand::PopLayer); + } + } + + fn compile(self, layer_store: &Vec) -> Option { + if self.prim_count == 0 { + return None; + } + + let mut sc_stack = Vec::new(); + let mut current_task = AlphaRenderTask::new(self.rect); + let mut alpha_task_stack = Vec::new(); + + for cmd in self.cmds { + match cmd { + TileCommand::PushLayer(sc_index) => { + sc_stack.push(sc_index); + + let layer = &layer_store[sc_index.0]; + match layer.composite_kind() { + CompositeKind::None => {} + CompositeKind::Simple(..) | CompositeKind::Complex(..) => { + let prev_task = mem::replace(&mut current_task, AlphaRenderTask::new(self.rect)); + alpha_task_stack.push(prev_task); + } + } + } + TileCommand::PopLayer => { + let sc_index = sc_stack.pop().unwrap(); + + let layer = &layer_store[sc_index.0]; + match layer.composite_kind() { + CompositeKind::None => {} + CompositeKind::Simple(opacity) => { + let mut prev_task = alpha_task_stack.pop().unwrap(); + prev_task.items.push(AlphaRenderItem::Blend(prev_task.children.len(), + opacity)); + prev_task.children.push(current_task); + current_task = prev_task; + } + CompositeKind::Complex(info) => { + let backdrop = alpha_task_stack.pop().unwrap(); + + let mut composite_task = AlphaRenderTask::new(self.rect); + composite_task.children.push(backdrop); + composite_task.children.push(current_task); + + composite_task.items.push(AlphaRenderItem::Composite(info)); + + current_task = composite_task; + } + } + } + TileCommand::DrawPrimitive(prim_index) => { + let sc_index = *sc_stack.last().unwrap(); + current_task.items.push(AlphaRenderItem::Primitive(sc_index, prim_index)); + } + } + } + + debug_assert!(alpha_task_stack.is_empty()); + + let task = RenderTask::from_primitives(current_task, + RenderTaskLocation::Fixed(self.rect), + self.rect.size); + Some(CompiledScreenTile::new(task)) + } +} + +impl FrameBuilder { + pub fn new(viewport_size: Size2D, + device_pixel_ratio: f32, + debug: bool, + config: FrameBuilderConfig) -> FrameBuilder { + let viewport_size = Size2D::new(viewport_size.width as i32, viewport_size.height as i32); + FrameBuilder { + screen_rect: Rect::new(Point2D::zero(), viewport_size), + layer_store: Vec::new(), + prim_store: Vec::new(), + layer_stack: Vec::new(), + device_pixel_ratio: device_pixel_ratio, + debug: debug, + config: config, + } + } + + fn add_primitive(&mut self, + rect: &Rect, + clip_rect: &Rect, + clip: Option>, + details: PrimitiveDetails) { + let current_layer = *self.layer_stack.last().unwrap(); + let StackingContextIndex(layer_index) = current_layer; + let layer = &mut self.layer_store[layer_index as usize]; + + let prim = Primitive { + rect: *rect, + complex_clip: clip, + local_clip_rect: *clip_rect, + details: details, + }; + let prim_index = self.prim_store.len(); + self.prim_store.push(prim); + + layer.items.push(StackingContextItem::Primitive(PrimitiveIndex(prim_index))); + } + + pub fn push_layer(&mut self, + rect: Rect, + clip_rect: Rect, + transform: Matrix4D, + pipeline_id: PipelineId, + scroll_layer_id: ScrollLayerId, + offset: Point2D, + composition_operations: Vec) { + let sc_index = StackingContextIndex(self.layer_store.len()); + + let sc = StackingContext { + items: Vec::new(), + local_rect: rect, + local_transform: transform, + local_offset: offset, + scroll_layer_id: scroll_layer_id, + pipeline_id: pipeline_id, + xf_rect: None, + transform: Matrix4D::identity(), + is_valid: false, + composition_ops: composition_operations, + local_clip_rect: clip_rect, + world_clip_rect: None, + parent: self.layer_stack.last().map(|index| *index), + }; + self.layer_store.push(sc); + + if !self.layer_stack.is_empty() { + let current_layer = *self.layer_stack.last().unwrap(); + let StackingContextIndex(layer_index) = current_layer; + let layer = &mut self.layer_store[layer_index as usize]; + layer.items.push(StackingContextItem::StackingContext(sc_index)); + } + + self.layer_stack.push(sc_index); + } + + pub fn pop_layer(&mut self) { + self.layer_stack.pop(); + } + + pub fn add_solid_rectangle(&mut self, + rect: &Rect, + clip_rect: &Rect, + clip: Option>, + color: &ColorF) { + if color.a == 0.0 { + return; + } + + let prim = RectanglePrimitive { + color: *color, + }; + + self.add_primitive(rect, + clip_rect, + clip, + PrimitiveDetails::Rectangle(prim)); + } + + pub fn supported_style(&mut self, border: &BorderSide) -> bool { + match border.style { + BorderStyle::Solid | + BorderStyle::None | + BorderStyle::Dotted | + BorderStyle::Dashed | + BorderStyle::Inset | + BorderStyle::Outset => { + return true; + } + _ => { + println!("TODO: Other border styles {:?}", border.style); + return false; + } + } + } + + pub fn add_border(&mut self, + rect: Rect, + clip_rect: &Rect, + clip: Option>, + border: &BorderDisplayItem) { + let radius = &border.radius; + let left = &border.left; + let right = &border.right; + let top = &border.top; + let bottom = &border.bottom; + + if !self.supported_style(left) || !self.supported_style(right) || + !self.supported_style(top) || !self.supported_style(bottom) { + println!("Unsupported border style, not rendering border"); + return; + } + + let tl_outer = Point2D::new(rect.origin.x, rect.origin.y); + let tl_inner = tl_outer + Point2D::new(radius.top_left.width.max(left.width), + radius.top_left.height.max(top.width)); + + let tr_outer = Point2D::new(rect.origin.x + rect.size.width, rect.origin.y); + let tr_inner = tr_outer + Point2D::new(-radius.top_right.width.max(right.width), + radius.top_right.height.max(top.width)); + + let bl_outer = Point2D::new(rect.origin.x, rect.origin.y + rect.size.height); + let bl_inner = bl_outer + Point2D::new(radius.bottom_left.width.max(left.width), + -radius.bottom_left.height.max(bottom.width)); + + let br_outer = Point2D::new(rect.origin.x + rect.size.width, + rect.origin.y + rect.size.height); + let br_inner = br_outer - Point2D::new(radius.bottom_right.width.max(right.width), + radius.bottom_right.height.max(bottom.width)); + + // These colors are used during inset/outset scaling. + let left_color = left.border_color(1.0, 2.0/3.0, 0.3, 0.7); + let top_color = top.border_color(1.0, 2.0/3.0, 0.3, 0.7); + let right_color = right.border_color(2.0/3.0, 1.0, 0.7, 0.3); + let bottom_color = bottom.border_color(2.0/3.0, 1.0, 0.7, 0.3); + + let prim = BorderPrimitive { + tl_outer: tl_outer, + tl_inner: tl_inner, + tr_outer: tr_outer, + tr_inner: tr_inner, + bl_outer: bl_outer, + bl_inner: bl_inner, + br_outer: br_outer, + br_inner: br_inner, + radius: radius.clone(), + left_width: left.width, + top_width: top.width, + bottom_width: bottom.width, + right_width: right.width, + left_color: left_color, + top_color: top_color, + bottom_color: bottom_color, + right_color: right_color, + left_style: left.style, + top_style: top.style, + right_style: right.style, + bottom_style: bottom.style, + }; + + self.add_primitive(&rect, + clip_rect, + clip, + PrimitiveDetails::Border(prim)); + } + + pub fn add_gradient(&mut self, + rect: Rect, + clip_rect: &Rect, + clip: Option>, + start_point: Point2D, + end_point: Point2D, + stops: ItemRange) { + // Fast paths for axis-aligned gradients: + if start_point.x == end_point.x { + let prim = GradientPrimitive { + stops_range: stops, + kind: GradientType::Vertical, + start_point: start_point, + end_point: end_point, + }; + self.add_primitive(&rect, + clip_rect, + clip, + PrimitiveDetails::Gradient(prim)); + } else if start_point.y == end_point.y { + let prim = GradientPrimitive { + stops_range: stops, + kind: GradientType::Horizontal, + start_point: start_point, + end_point: end_point, + }; + self.add_primitive(&rect, + clip_rect, + clip, + PrimitiveDetails::Gradient(prim)); + } else { + let prim = GradientPrimitive { + stops_range: stops, + kind: GradientType::Rotated, + start_point: start_point, + end_point: end_point, + }; + self.add_primitive(&rect, + clip_rect, + clip, + PrimitiveDetails::Gradient(prim)); + } + } + + pub fn add_text(&mut self, + rect: Rect, + clip_rect: &Rect, + clip: Option>, + font_key: FontKey, + size: Au, + blur_radius: Au, + color: &ColorF, + glyph_range: ItemRange) { + if color.a == 0.0 { + return + } + + let prim = TextPrimitive { + color: *color, + font_key: font_key, + size: size, + blur_radius: blur_radius, + glyph_range: glyph_range, + }; + + self.add_primitive(&rect, + clip_rect, + clip, + PrimitiveDetails::Text(prim)); + } + + pub fn add_box_shadow(&mut self, + box_bounds: &Rect, + clip_rect: &Rect, + clip: Option>, + box_offset: &Point2D, + color: &ColorF, + blur_radius: f32, + spread_radius: f32, + border_radius: f32, + clip_mode: BoxShadowClipMode) { + if color.a == 0.0 { + return + } + + // Fast path. + if blur_radius == 0.0 && spread_radius == 0.0 && clip_mode == BoxShadowClipMode::None { + self.add_solid_rectangle(&box_bounds, + clip_rect, + None, + color); + return; + } + + let bs_rect = compute_box_shadow_rect(box_bounds, + box_offset, + spread_radius); + + let metrics = BoxShadowMetrics::new(&bs_rect, + border_radius, + blur_radius); + + let prim_rect = match clip_mode { + BoxShadowClipMode::Outset | BoxShadowClipMode::None => { + Rect::new(metrics.tl_outer, + Size2D::new(metrics.br_outer.x - metrics.tl_outer.x, + metrics.br_outer.y - metrics.tl_outer.y)) + } + BoxShadowClipMode::Inset => { + *box_bounds + } + }; + + let prim = BoxShadowPrimitive { + metrics: metrics, + src_rect: *box_bounds, + bs_rect: bs_rect, + color: *color, + blur_radius: blur_radius, + spread_radius: spread_radius, + border_radius: border_radius, + clip_mode: clip_mode, + }; + + self.add_primitive(&prim_rect, + clip_rect, + clip, + PrimitiveDetails::BoxShadow(prim)); + } + + pub fn add_webgl_rectangle(&mut self, + rect: Rect, + clip_rect: &Rect, + clip: Option>, + context_id: WebGLContextId) { + let prim = ImagePrimitive { + kind: ImagePrimitiveKind::WebGL(context_id), + }; + + self.add_primitive(&rect, + clip_rect, + clip, + PrimitiveDetails::Image(prim)); + } + + pub fn add_image(&mut self, + rect: Rect, + clip_rect: &Rect, + clip: Option>, + stretch_size: &Size2D, + image_key: ImageKey, + image_rendering: ImageRendering) { + let prim = ImagePrimitive { + kind: ImagePrimitiveKind::Image(image_key, + image_rendering, + stretch_size.clone()), + }; + + self.add_primitive(&rect, + clip_rect, + clip, + PrimitiveDetails::Image(prim)); + } + + fn cull_layers(&mut self, + screen_rect: &Rect, + layer_map: &HashMap>) { + // Remove layers that are transparent. + + // Build layer screen rects. + // TODO(gw): This can be done earlier once update_layer_transforms() is fixed. + for layer_index in 0..self.layer_store.len() { + let parent_index = self.layer_store[layer_index].parent; + let parent_clip_rect = parent_index.map_or(Some(*screen_rect), |parent_index| { + self.layer_store[parent_index.0].world_clip_rect + }); + if parent_clip_rect.is_none() { + continue; + } + let layer = &mut self.layer_store[layer_index]; + + if layer.can_contribute_to_scene() { + let scroll_layer = &layer_map[&layer.scroll_layer_id]; + let offset_transform = Matrix4D::identity().translate(layer.local_offset.x, + layer.local_offset.y, + 0.0); + let transform = scroll_layer.world_transform + .as_ref() + .unwrap() + .mul(&layer.local_transform) + .mul(&offset_transform); + layer.transform = transform; + layer.xf_rect = Some(TransformedRect::new(&layer.local_rect, + &transform, + self.device_pixel_ratio)); + + let world_clip_rect = TransformedRect::new(&layer.local_clip_rect, + &transform, + self.device_pixel_ratio); + + // TODO(gw): This gets the iframe reftests passing but is questionable. + // Need to refactor the whole layer viewport_rect code once + // WR2 lands since it can be simplified now. + let origin = Point2D::new(DevicePixel::new(scroll_layer.viewport_rect.origin.x, + self.device_pixel_ratio), + DevicePixel::new(scroll_layer.viewport_rect.origin.y, + self.device_pixel_ratio)); + let size = Size2D::new(DevicePixel::new(scroll_layer.viewport_rect.size.width, + self.device_pixel_ratio), + DevicePixel::new(scroll_layer.viewport_rect.size.height, + self.device_pixel_ratio)); + let viewport_rect = Rect::new(origin, size); + + layer.world_clip_rect = world_clip_rect.bounding_rect + .intersection(&parent_clip_rect.unwrap()) + .and_then(|cr| { + cr.intersection(&viewport_rect) + }); + + if layer.world_clip_rect.is_some() { + layer.is_valid = layer.xf_rect + .as_ref() + .unwrap() + .bounding_rect + .intersects(&screen_rect); + } + } + } + } + + fn create_screen_tiles(&self) -> (i32, i32, Vec) { + let dp_size = Size2D::new(DevicePixel::new(self.screen_rect.size.width as f32, + self.device_pixel_ratio), + DevicePixel::new(self.screen_rect.size.height as f32, + self.device_pixel_ratio)); + + let x_tile_size = DevicePixel(SCREEN_TILE_SIZE); + let y_tile_size = DevicePixel(SCREEN_TILE_SIZE); + let x_tile_count = (dp_size.width + x_tile_size - DevicePixel(1)).0 / x_tile_size.0; + let y_tile_count = (dp_size.height + y_tile_size - DevicePixel(1)).0 / y_tile_size.0; + + // Build screen space tiles, which are individual BSP trees. + let mut screen_tiles = Vec::new(); + for y in 0..y_tile_count { + let y0 = DevicePixel(y * y_tile_size.0); + let y1 = y0 + y_tile_size; + + for x in 0..x_tile_count { + let x0 = DevicePixel(x * x_tile_size.0); + let x1 = x0 + x_tile_size; + + let tile_rect = rect_from_points(x0, y0, x1, y1); + + screen_tiles.push(ScreenTile::new(tile_rect)); + } + } + + (x_tile_count, y_tile_count, screen_tiles) + } + + + fn assign_prims_to_screen_tiles(&self, + stacking_context_index: StackingContextIndex, + x_tile_count: i32, + y_tile_count: i32, + screen_tiles: &mut Vec) { + let layer = &self.layer_store[stacking_context_index.0]; + if !layer.is_valid { + return; + } + + let l_rect = &layer.xf_rect.as_ref().unwrap().bounding_rect; + + let l_tile_x0 = l_rect.origin.x.0 / SCREEN_TILE_SIZE; + let l_tile_y0 = l_rect.origin.y.0 / SCREEN_TILE_SIZE; + let l_tile_x1 = (l_rect.origin.x.0 + l_rect.size.width.0 + SCREEN_TILE_SIZE - 1) / SCREEN_TILE_SIZE; + let l_tile_y1 = (l_rect.origin.y.0 + l_rect.size.height.0 + SCREEN_TILE_SIZE - 1) / SCREEN_TILE_SIZE; + + let l_tile_x0 = cmp::min(l_tile_x0, x_tile_count); + let l_tile_x0 = cmp::max(l_tile_x0, 0); + let l_tile_x1 = cmp::min(l_tile_x1, x_tile_count); + let l_tile_x1 = cmp::max(l_tile_x1, 0); + + let l_tile_y0 = cmp::min(l_tile_y0, y_tile_count); + let l_tile_y0 = cmp::max(l_tile_y0, 0); + let l_tile_y1 = cmp::min(l_tile_y1, y_tile_count); + let l_tile_y1 = cmp::max(l_tile_y1, 0); + + for ly in l_tile_y0..l_tile_y1 { + for lx in l_tile_x0..l_tile_x1 { + let tile = &mut screen_tiles[(ly * x_tile_count + lx) as usize]; + tile.push_layer(stacking_context_index); + } + } + + for item in &layer.items { + match item { + &StackingContextItem::StackingContext(sc_index) => { + self.assign_prims_to_screen_tiles(sc_index, + x_tile_count, + y_tile_count, + screen_tiles); + } + &StackingContextItem::Primitive(prim_index) => { + let prim = &self.prim_store[prim_index.0]; + + let p_rect = TransformedRect::new(&prim.rect, + &layer.transform, + self.device_pixel_ratio); + let p_rect = &p_rect.bounding_rect; + + // TODO(gw): Ensure that certain primitives (such as background-image) only get + // assigned to tiles where their containing layer intersects with. + // Does this cause any problems / demonstrate other bugs? + // Restrict the tiles by clamping to the layer tile indices... + + let p_tile_x0 = p_rect.origin.x.0 / SCREEN_TILE_SIZE; + let p_tile_y0 = p_rect.origin.y.0 / SCREEN_TILE_SIZE; + let p_tile_x1 = (p_rect.origin.x.0 + p_rect.size.width.0 + SCREEN_TILE_SIZE - 1) / SCREEN_TILE_SIZE; + let p_tile_y1 = (p_rect.origin.y.0 + p_rect.size.height.0 + SCREEN_TILE_SIZE - 1) / SCREEN_TILE_SIZE; + + let p_tile_x0 = cmp::min(p_tile_x0, l_tile_x1); + let p_tile_x0 = cmp::max(p_tile_x0, l_tile_x0); + let p_tile_x1 = cmp::min(p_tile_x1, l_tile_x1); + let p_tile_x1 = cmp::max(p_tile_x1, l_tile_x0); + + let p_tile_y0 = cmp::min(p_tile_y0, l_tile_y1); + let p_tile_y0 = cmp::max(p_tile_y0, l_tile_y0); + let p_tile_y1 = cmp::min(p_tile_y1, l_tile_y1); + let p_tile_y1 = cmp::max(p_tile_y1, l_tile_y0); + + for py in p_tile_y0..p_tile_y1 { + for px in p_tile_x0..p_tile_x1 { + let tile = &mut screen_tiles[(py * x_tile_count + px) as usize]; + tile.push_primitive(prim_index); + } + } + } + } + } + + for ly in l_tile_y0..l_tile_y1 { + for lx in l_tile_x0..l_tile_x1 { + let tile = &mut screen_tiles[(ly * x_tile_count + lx) as usize]; + tile.pop_layer(stacking_context_index); + } + } + } + + fn build_resource_list(&mut self, + resource_cache: &mut ResourceCache, + frame_id: FrameId, + pipeline_auxiliary_lists: &HashMap>) { + let mut resource_list = ResourceList::new(); + + // Non-visible layers have been marked invalid by now + for layer in &self.layer_store { + if layer.is_valid { + let auxiliary_lists = pipeline_auxiliary_lists.get(&layer.pipeline_id) + .expect("No auxiliary lists?!"); + + // Non-visible chunks have also been removed by now + layer.build_resource_list(&mut resource_list, + auxiliary_lists, + &self.prim_store); + } + } + + resource_cache.add_resource_list(&resource_list, + frame_id); + resource_cache.raster_pending_glyphs(frame_id); + } + + fn pack_primitives_for_layer( + &self, + stacking_context_index: StackingContextIndex, + packed_primitive_cache: &mut PackedPrimitiveCache, + pipeline_auxiliary_lists: &HashMap>, + render_target_context: &RenderTargetContext) { + let layer = &self.layer_store[stacking_context_index.0]; + if !layer.is_valid { + return; + } + + for item in &layer.items { + match item { + &StackingContextItem::StackingContext(sc_index) => { + self.pack_primitives_for_layer(sc_index, + packed_primitive_cache, + pipeline_auxiliary_lists, + render_target_context) + } + &StackingContextItem::Primitive(prim_index) => { + let prim = &self.prim_store[prim_index.0]; + let auxiliary_lists = pipeline_auxiliary_lists.get(&layer.pipeline_id) + .expect("No auxiliary lists?!"); + prim.pack(prim_index, + packed_primitive_cache, + auxiliary_lists, + render_target_context) + } + } + } + } + + fn pack_primitives(&self, + pipeline_auxiliary_lists: &HashMap>, + render_target_context: &RenderTargetContext) + -> PackedPrimitiveCache { + let mut packed_primitive_cache = PackedPrimitiveCache::new(); + if !self.layer_store.is_empty() { + self.pack_primitives_for_layer(StackingContextIndex(0), + &mut packed_primitive_cache, + pipeline_auxiliary_lists, + render_target_context); + } + packed_primitive_cache + } + + pub fn build(&mut self, + resource_cache: &mut ResourceCache, + frame_id: FrameId, + pipeline_auxiliary_lists: &HashMap>, + layer_map: &HashMap>) -> Frame { + let screen_rect = Rect::new(Point2D::zero(), + Size2D::new(DevicePixel::new(self.screen_rect.size.width as f32, self.device_pixel_ratio), + DevicePixel::new(self.screen_rect.size.height as f32, self.device_pixel_ratio))); + + self.cull_layers(&screen_rect, layer_map); + + let mut debug_rects = Vec::new(); + + self.build_resource_list(resource_cache, frame_id, pipeline_auxiliary_lists); + let (x_tile_count, y_tile_count, mut screen_tiles) = self.create_screen_tiles(); + + let ctx = RenderTargetContext { + layer_store: &self.layer_store, + prim_store: &self.prim_store, + resource_cache: resource_cache, + device_pixel_ratio: self.device_pixel_ratio, + frame_id: frame_id, + alpha_batch_max_layers: self.config.max_prim_layers, + alpha_batch_max_tiles: self.config.max_prim_tiles, + }; + + let packed_primitive_cache = self.pack_primitives(pipeline_auxiliary_lists, &ctx); + if !self.layer_store.is_empty() { + let root_sc_index = StackingContextIndex(0); + self.assign_prims_to_screen_tiles(root_sc_index, + x_tile_count, + y_tile_count, + &mut screen_tiles); + } + + if self.debug { + for r in &screen_tiles { + debug_rects.push(DebugRect { + label: format!("{}|{}", r.cmds.len(), r.prim_count), + color: ColorF::new(1.0, 0.0, 0.0, 1.0), + rect: r.rect, + }); + } + } + + let mut clear_tiles = Vec::new(); + + // Build list of passes, target allocs that each tile needs. + let mut compiled_screen_tiles = Vec::new(); + for screen_tile in screen_tiles { + let rect = screen_tile.rect; // TODO(gw): Remove clone here + match screen_tile.compile(&self.layer_store) { + Some(compiled_screen_tile) => { + compiled_screen_tiles.push(compiled_screen_tile); + } + None => { + clear_tiles.push(ClearTile { + rect: rect, + }); + } + } + } + + let mut phases = Vec::new(); + + if !compiled_screen_tiles.is_empty() { + // Sort by pass count to minimize render target switches. + compiled_screen_tiles.sort_by(|a, b| { + let a_passes = a.required_target_count; + let b_passes = b.required_target_count; + b_passes.cmp(&a_passes) + }); + + // Do the allocations now, assigning each tile to a render + // phase as required. + + let mut current_phase = RenderPhase::new(compiled_screen_tiles[0].required_target_count); + + for compiled_screen_tile in compiled_screen_tiles { + if let Some(failed_tile) = current_phase.add_compiled_screen_tile(compiled_screen_tile) { + let full_phase = mem::replace(&mut current_phase, + RenderPhase::new(failed_tile.required_target_count)); + phases.push(full_phase); + + let result = current_phase.add_compiled_screen_tile(failed_tile); + assert!(result.is_none(), "TODO: Handle single tile not fitting in render phase."); + } + } + + phases.push(current_phase); + + for phase in &mut phases { + phase.build(&packed_primitive_cache, &ctx); + } + } + + Frame { + debug_rects: debug_rects, + phases: phases, + clear_tiles: clear_tiles, + cache_size: Size2D::new(RENDERABLE_CACHE_SIZE.0 as f32, + RENDERABLE_CACHE_SIZE.0 as f32), + } + } + +} + +fn compute_box_shadow_rect(box_bounds: &Rect, + box_offset: &Point2D, + spread_radius: f32) + -> Rect { + let mut rect = (*box_bounds).clone(); + rect.origin.x += box_offset.x; + rect.origin.y += box_offset.y; + rect.inflate(spread_radius, spread_radius) +} + +#[derive(Clone, Copy, PartialEq, Debug)] +struct PackedPrimitiveMetadata { + start: usize, + end: usize, + texture_id: TextureId, +} + +impl PackedPrimitiveMetadata { + fn new(start: usize, end: usize, texture_id: TextureId) -> PackedPrimitiveMetadata { + PackedPrimitiveMetadata { + start: start, + end: end, + texture_id: texture_id, + } + } + + fn none() -> PackedPrimitiveMetadata { + PackedPrimitiveMetadata::new(0, 0, TextureId(0)) + } +} + +/// To find the packed primitives for a primitive index, first look up the metadata within the +/// `metadata` field, and then consult the indices defined by the range specified by that metadata +/// in the `primitives` field. +struct PackedPrimitiveCache { + /// A mapping from primitive index to range in the `primitives` vector below. + metadata: Vec, + /// A list of packed primitives. + primitives: Vec, +} + +impl PackedPrimitiveCache { + fn new() -> PackedPrimitiveCache { + PackedPrimitiveCache { + metadata: vec![], + primitives: vec![], + } + } + + /// Reserves space for the packed primitive with the given index. + /// + /// This must be called before `add_packed_primitive` below. + fn init_packed_primitive(&mut self, primitive_index: PrimitiveIndex) { + while self.metadata.len() < primitive_index.0 + 1 { + self.metadata.push(PackedPrimitiveMetadata::none()) + } + } + + fn add_packed_primitive(&mut self, + primitive_index: PrimitiveIndex, + packed_primitive: PackedPrimitive, + texture_id: TextureId) { + let mut metadata = &mut self.metadata[primitive_index.0]; + if *metadata == PackedPrimitiveMetadata::none() { + metadata.start = self.primitives.len(); + } else { + debug_assert!(metadata.end == self.primitives.len()); + } + metadata.end = self.primitives.len() + 1; + metadata.texture_id = texture_id; + + self.primitives.push(packed_primitive) + } + + fn add_to_batch(&self, + primitive_index: PrimitiveIndex, + batch: &mut PrimitiveBatch, + layer_index_in_ubo: u32, + tile_index_in_ubo: u32, + transform_kind: TransformedRectKind) + -> bool { + if transform_kind != batch.transform_kind { + return false + } + + // TODO(gw): Tidy the support for batch breaks up... + let metadata = self.metadata[primitive_index.0]; + if metadata.texture_id != TextureId(0) { + if batch.color_texture_id != TextureId(0) && + batch.color_texture_id != metadata.texture_id { + return false + } + batch.color_texture_id = metadata.texture_id; + } + + for packed_primitive_index in metadata.start..metadata.end { + match (&mut batch.data, &self.primitives[packed_primitive_index]) { + (&mut PrimitiveBatchData::Blend(..), _) => return false, + (&mut PrimitiveBatchData::Composite(..), _) => return false, + (&mut PrimitiveBatchData::Rectangles(ref mut data), + &PackedPrimitive::Rectangle(ref rectangle)) => { + // FIXME(pcwalton): Don't clone here! + // TODO(pcwalton): Check rect intersection. + let mut rectangle = (*rectangle).clone(); + rectangle.common.tile_index = tile_index_in_ubo; + rectangle.common.layer_index = layer_index_in_ubo; + data.push(rectangle) + } + (&mut PrimitiveBatchData::Rectangles(..), _) => return false, + (&mut PrimitiveBatchData::RectanglesClip(ref mut data), + &PackedPrimitive::RectangleClip(ref rectangle)) => { + // FIXME(pcwalton): Don't clone here! + // TODO(pcwalton): Check rect intersection. + let mut rectangle = (*rectangle).clone(); + rectangle.common.tile_index = tile_index_in_ubo; + rectangle.common.layer_index = layer_index_in_ubo; + data.push(rectangle) + } + (&mut PrimitiveBatchData::RectanglesClip(..), _) => return false, + (&mut PrimitiveBatchData::Image(ref mut data), + &PackedPrimitive::Image(ref image)) => { + // FIXME(pcwalton): Don't clone here! + let mut image = (*image).clone(); + image.common.tile_index = tile_index_in_ubo; + image.common.layer_index = layer_index_in_ubo; + data.push(image) + } + (&mut PrimitiveBatchData::Image(..), _) => return false, + (&mut PrimitiveBatchData::ImageClip(ref mut data), + &PackedPrimitive::ImageClip(ref image)) => { + // FIXME(pcwalton): Don't clone here! + // TODO(pcwalton): Check rect intersection. + let mut image = (*image).clone(); + image.common.tile_index = tile_index_in_ubo; + image.common.layer_index = layer_index_in_ubo; + data.push(image) + } + (&mut PrimitiveBatchData::ImageClip(..), _) => return false, + (&mut PrimitiveBatchData::Borders(ref mut data), + &PackedPrimitive::Border(ref border)) => { + // FIXME(pcwalton): Don't clone here! + // TODO(pcwalton): Check rect intersection. + let mut border = (*border).clone(); + border.common.tile_index = tile_index_in_ubo; + border.common.layer_index = layer_index_in_ubo; + data.push(border) + } + (&mut PrimitiveBatchData::Borders(..), _) => return false, + (&mut PrimitiveBatchData::AlignedGradient(ref mut data), + &PackedPrimitive::AlignedGradient(ref gradient)) => { + // FIXME(pcwalton): Don't clone here! + // TODO(pcwalton): Check rect intersection. + let mut gradient = (*gradient).clone(); + gradient.common.tile_index = tile_index_in_ubo; + gradient.common.layer_index = layer_index_in_ubo; + data.push(gradient) + } + (&mut PrimitiveBatchData::AlignedGradient(..), _) => return false, + (&mut PrimitiveBatchData::AngleGradient(ref mut data), + &PackedPrimitive::AngleGradient(ref gradient)) => { + // FIXME(pcwalton): Don't clone here! + let mut gradient = (*gradient).clone(); + gradient.common.tile_index = tile_index_in_ubo; + gradient.common.layer_index = layer_index_in_ubo; + data.push(gradient) + } + (&mut PrimitiveBatchData::AngleGradient(..), _) => return false, + (&mut PrimitiveBatchData::BoxShadows(ref mut data), + &PackedPrimitive::BoxShadow(ref shadow)) => { + // FIXME(pcwalton): Don't clone here! + // TODO(pcwalton): Check rect intersection. + let mut shadow = (*shadow).clone(); + shadow.common.tile_index = tile_index_in_ubo; + shadow.common.layer_index = layer_index_in_ubo; + data.push(shadow) + } + (&mut PrimitiveBatchData::BoxShadows(..), _) => return false, + (&mut PrimitiveBatchData::Text(ref mut data), + &PackedPrimitive::Text(ref glyph)) => { + // FIXME(pcwalton): Don't clone here! + // TODO(pcwalton): Check rect intersection. Binary search to find the start + // point, maybe? + let mut glyph = (*glyph).clone(); + glyph.common.tile_index = tile_index_in_ubo; + glyph.common.layer_index = layer_index_in_ubo; + data.push(glyph) + } + (&mut PrimitiveBatchData::Text(..), _) => return false, + } + } + true + } +} + diff --git a/src/util.rs b/src/util.rs index eeda129405..729aa6c5db 100644 --- a/src/util.rs +++ b/src/util.rs @@ -3,10 +3,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use euclid::{Matrix4D, Point2D, Point4D, Rect, Size2D}; -use internal_types::{RectColors}; +use internal_types::DevicePixel; use num_traits::Zero; use time::precise_time_ns; -use webrender_traits::ColorF; #[allow(dead_code)] pub struct ProfileScope { @@ -26,12 +25,9 @@ impl ProfileScope { impl Drop for ProfileScope { fn drop(&mut self) { - /* - if self.name.chars().next() != Some(' ') { let t1 = precise_time_ns(); let ms = (t1 - self.t0) as f64 / 1000000f64; println!("{} {}", self.name, ms); - }*/ } } @@ -88,6 +84,8 @@ impl MatrixHelpers for Matrix4D { pub trait RectHelpers { fn from_points(a: &Point2D, b: &Point2D, c: &Point2D, d: &Point2D) -> Self; fn contains_rect(&self, other: &Rect) -> bool; + fn from_floats(x0: f32, y0: f32, x1: f32, y1: f32) -> Rect; + fn is_well_formed_and_nonempty(&self) -> bool; } impl RectHelpers for Rect { @@ -119,78 +117,14 @@ impl RectHelpers for Rect { self.max_x() >= other.max_x() && self.max_y() >= other.max_y() } -} - -pub fn lerp(a: f32, b: f32, t: f32) -> f32 { - (b - a) * t + a -} - -pub fn bilerp(point: &Point2D, quad: &Rect, varyings: &V) -> V::Element - where V: RectVaryings, V::Element: VaryingElement { - let (x1, y1, x2, y2) = (quad.origin.x, quad.origin.y, quad.max_x(), quad.max_y()); - let top_left = varyings.top_left().scale((x2 - point.x) * (y2 - point.y)); - let top_right = varyings.top_right().scale((point.x - x1) * (y2 - point.y)); - let bottom_left = varyings.bottom_left().scale((x2 - point.x) * (point.y - y1)); - let bottom_right = varyings.bottom_right().scale((point.x - x1) * (point.y - y1)); - let sum = VaryingElement::accumulate(&[top_left, top_right, bottom_left, bottom_right]); - sum.scale(1.0 / ((x2 - x1) * (y2 - y1))) -} - -pub fn bilerp_rect(clipped_rect: &Rect, quad: &Rect, varyings: &V) -> V - where V: RectVaryings, V::Element: VaryingElement { - let top_left = bilerp(&clipped_rect.origin, quad, varyings); - let top_right = bilerp(&clipped_rect.top_right(), quad, varyings); - let bottom_right = bilerp(&clipped_rect.bottom_right(), quad, varyings); - let bottom_left = bilerp(&clipped_rect.bottom_left(), quad, varyings); - V::from_elements(&[top_left, top_right, bottom_right, bottom_left]) -} -pub trait RectVaryings { - type Element; - fn top_left(&self) -> Self::Element; - fn top_right(&self) -> Self::Element; - fn bottom_right(&self) -> Self::Element; - fn bottom_left(&self) -> Self::Element; - fn from_elements(elements: &[Self::Element; 4]) -> Self; -} - -impl RectVaryings for RectColors { - type Element = ColorF; - fn top_left(&self) -> ColorF { self.top_left } - fn top_right(&self) -> ColorF { self.top_right } - fn bottom_right(&self) -> ColorF { self.bottom_right } - fn bottom_left(&self) -> ColorF { self.bottom_left } - fn from_elements(elements: &[ColorF; 4]) -> RectColors { - RectColors { - top_left: elements[0], - top_right: elements[1], - bottom_right: elements[2], - bottom_left: elements[3], - } + fn from_floats(x0: f32, y0: f32, x1: f32, y1: f32) -> Rect { + Rect::new(Point2D::new(x0, y0), + Size2D::new(x1 - x0, y1 - y0)) } -} -pub trait VaryingElement : Sized { - fn scale(&self, factor: f32) -> Self; - fn accumulate(values: &[Self; 4]) -> Self; -} - -impl VaryingElement for ColorF { - fn scale(&self, factor: f32) -> ColorF { - ColorF { - r: self.r * factor, - g: self.g * factor, - b: self.b * factor, - a: self.a * factor, - } - } - fn accumulate(values: &[ColorF; 4]) -> ColorF { - ColorF { - r: values[0].r + values[1].r + values[2].r + values[3].r, - g: values[0].g + values[1].g + values[2].g + values[3].g, - b: values[0].b + values[1].b + values[2].b + values[3].b, - a: values[0].a + values[1].a + values[2].a + values[3].a, - } + fn is_well_formed_and_nonempty(&self) -> bool { + self.size.width > 0.0 && self.size.height > 0.0 } } @@ -200,59 +134,65 @@ pub fn rect_is_empty(rect: &Rect) -> bool { rect.size.width == Zero::zero() || rect.size.height == Zero::zero() } -/// Returns true if the rectangle's width and height are both strictly positive and false -/// otherwise. -pub fn rect_is_well_formed_and_nonempty(rect: &Rect) -> bool { - rect.size.width > 0.0 && rect.size.height > 0.0 +#[inline] +pub fn rect_from_points(x0: DevicePixel, + y0: DevicePixel, + x1: DevicePixel, + y1: DevicePixel) -> Rect { + Rect::new(Point2D::new(x0, y0), + Size2D::new(x1 - x0, y1 - y0)) } -/// Multiplies all non-alpha components of a color by the given value. -pub fn scale_color(color: &ColorF, factor: f32) -> ColorF { - ColorF { - r: color.r * factor, - g: color.g * factor, - b: color.b * factor, - a: color.a, - } +#[inline] +pub fn rect_from_points_f(x0: f32, + y0: f32, + x1: f32, + y1: f32) -> Rect { + Rect::new(Point2D::new(x0, y0), + Size2D::new(x1 - x0, y1 - y0)) } -/// Subdivides a rectangle into quadrants formed by a point. The quadrants are returned in the -/// order of: top left, top right, bottom right, and bottom left. -pub fn subdivide_rect_into_quadrants(rect: &Rect, point: &Point2D) -> (Rect, Rect, Rect, Rect) { - let point = Point2D::new(clamp(point.x, rect.origin.x, rect.max_x()), - clamp(point.y, rect.origin.y, rect.max_y())); - let tl_rect = Rect::new(rect.origin, - Size2D::new(point.x - rect.origin.x, point.y - rect.origin.y)); - let tr_rect = Rect::new(Point2D::new(point.x, rect.origin.y), - Size2D::new(rect.max_x() - point.x, point.y - rect.origin.y)); - let br_rect = Rect::new(point, - Size2D::new(rect.max_x() - point.x, rect.max_y() - point.y)); - let bl_rect = Rect::new(Point2D::new(rect.origin.x, point.y), - Size2D::new(point.x - rect.origin.x, rect.max_y() - point.y)); - return (tl_rect, tr_rect, br_rect, bl_rect); - - fn clamp(x: f32, lo: f32, hi: f32) -> f32 { - if x < lo { - lo - } else if x > hi { - hi - } else { - x - } - } +pub fn lerp(a: f32, b: f32, t: f32) -> f32 { + (b - a) * t + a } -/// Returns the center point of the given rect. -pub fn rect_center(rect: &Rect) -> Point2D { - Point2D::new(rect.origin.x + rect.size.width / 2.0, rect.origin.y + rect.size.height / 2.0) -} +pub fn subtract_rect(rect: &Rect, + other: &Rect, + results: &mut Vec>) { + results.clear(); -pub fn distance(a: &Point2D, b: &Point2D) -> f32 { - let (x, y) = (b.x - a.x, b.y - a.y); - (x * x + y * y).sqrt() -} + let int = rect.intersection(other); + match int { + Some(int) => { + let rx0 = rect.origin.x; + let ry0 = rect.origin.y; + let rx1 = rx0 + rect.size.width; + let ry1 = ry0 + rect.size.height; -pub fn lerp_points(a: &Point2D, b: &Point2D, t: f32) -> Point2D { - Point2D::new(lerp(a.x, b.x, t), lerp(a.y, b.y, t)) -} + let ox0 = int.origin.x; + let oy0 = int.origin.y; + let ox1 = ox0 + int.size.width; + let oy1 = oy0 + int.size.height; + let r = rect_from_points_f(rx0, ry0, ox0, ry1); + if r.size.width > 0.0 && r.size.height > 0.0 { + results.push(r); + } + let r = rect_from_points_f(ox0, ry0, ox1, oy0); + if r.size.width > 0.0 && r.size.height > 0.0 { + results.push(r); + } + let r = rect_from_points_f(ox0, oy1, ox1, ry1); + if r.size.width > 0.0 && r.size.height > 0.0 { + results.push(r); + } + let r = rect_from_points_f(ox1, ry0, rx1, ry1); + if r.size.width > 0.0 && r.size.height > 0.0 { + results.push(r); + } + } + None => { + results.push(*rect); + } + } +} \ No newline at end of file