Copy as Markdown

Other Tools

#define SWGL 1
#define __VERSION__ 150
#define WR_MAX_VERTEX_TEXTURE_WIDTH 1024U
/* 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/. */
#ifdef WR_FEATURE_TEXTURE_EXTERNAL
// for this extension.
#endif
#ifdef WR_FEATURE_TEXTURE_EXTERNAL_ESSL1
// Some GLES 3 devices do not support GL_OES_EGL_image_external_essl3, so we
// must use GL_OES_EGL_image_external instead and make the shader ESSL1
// compatible.
#endif
#ifdef WR_FEATURE_TEXTURE_EXTERNAL_BT709
#endif
#ifdef WR_FEATURE_ADVANCED_BLEND
#endif
#ifdef WR_FEATURE_DUAL_SOURCE_BLENDING
#ifdef GL_ES
#else
#endif
#endif
/* 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/. */
#if defined(GL_ES)
#if GL_ES == 1
// Sampler default precision is lowp on mobile GPUs.
// This causes RGBA32F texture data to be clamped to 16 bit floats on some GPUs (e.g. Mali-T880).
// Define highp precision macro to allow lossless FLOAT texture sampling.
#define HIGHP_SAMPLER_FLOAT highp
// Default int precision in GLES 3 is highp (32 bits) in vertex shaders
// and mediump (16 bits) in fragment shaders. If an int is being used as
// a texel address in a fragment shader it, and therefore requires > 16
// bits, it must be qualified with this.
#define HIGHP_FS_ADDRESS highp
// texelFetchOffset is buggy on some Android GPUs (see issue #1694).
// Fallback to texelFetch on mobile GPUs.
#define TEXEL_FETCH(sampler, position, lod, offset) texelFetch(sampler, position + offset, lod)
#else
#define HIGHP_SAMPLER_FLOAT
#define HIGHP_FS_ADDRESS
#define TEXEL_FETCH(sampler, position, lod, offset) texelFetchOffset(sampler, position, lod, offset)
#endif
#else
#define HIGHP_SAMPLER_FLOAT
#define HIGHP_FS_ADDRESS
#if defined(PLATFORM_MACOS) && !defined(SWGL)
// texelFetchOffset introduces a variety of shader compilation bugs on macOS Intel so avoid it.
#define TEXEL_FETCH(sampler, position, lod, offset) texelFetch(sampler, position + offset, lod)
#else
#define TEXEL_FETCH(sampler, position, lod, offset) texelFetchOffset(sampler, position, lod, offset)
#endif
#endif
#ifdef SWGL
#define SWGL_DRAW_SPAN
#define SWGL_CLIP_MASK
#define SWGL_ANTIALIAS
#define SWGL_BLEND
#define SWGL_CLIP_DIST
#endif
#ifdef WR_VERTEX_SHADER
#ifdef SWGL
// Annotate a vertex attribute as being flat per each drawn primitive instance.
// SWGL can use this information to avoid redundantly loading the attribute in all SIMD lanes.
#define PER_INSTANCE flat
#else
#define PER_INSTANCE
#endif
#if __VERSION__ != 100
#define varying out
#define attribute in
#endif
#endif
#ifdef WR_FRAGMENT_SHADER
precision highp float;
#if __VERSION__ != 100
#define varying in
#endif
#endif
// Flat interpolation is not supported on ESSL 1
#if __VERSION__ == 100
#define flat
#endif
#if defined(WR_FEATURE_TEXTURE_EXTERNAL_ESSL1)
#define TEX_SAMPLE(sampler, tex_coord) texture2D(sampler, tex_coord.xy)
#elif defined(WR_FEATURE_TEXTURE_EXTERNAL_BT709)
// Force conversion from yuv to rgb using BT709 colorspace
#define TEX_SAMPLE(sampler, tex_coord) vec4(yuv_2_rgb(texture(sampler, tex_coord.xy).xyz, itu_709), 1.0)
#else
#define TEX_SAMPLE(sampler, tex_coord) texture(sampler, tex_coord.xy)
#endif
#if defined(WR_FEATURE_TEXTURE_EXTERNAL) && defined(PLATFORM_ANDROID)
// On some Mali GPUs we have encountered crashes in glDrawElements when using
// textureSize(samplerExternalOES) in a vertex shader without potentially
// sampling from the texture. This tricks the driver in to thinking the texture
// may be sampled from, avoiding the crash. See bug 1692848.
uniform bool u_mali_workaround_dummy;
#define TEX_SIZE(sampler) (u_mali_workaround_dummy ? ivec2(texture(sampler, vec2(0.0, 0.0)).rr) : textureSize(sampler, 0))
#else
#define TEX_SIZE(sampler) textureSize(sampler, 0)
#endif
//======================================================================================
// Vertex shader attributes and uniforms
//======================================================================================
#ifdef WR_VERTEX_SHADER
// Uniform inputs
uniform mat4 uTransform; // Orthographic projection
// Attribute inputs
attribute vec2 aPosition;
// get_fetch_uv is a macro to work around a macOS Intel driver parsing bug.
// TODO: convert back to a function once the driver issues are resolved, if ever.
// Do the division with unsigned ints because that's more efficient with D3D
#define get_fetch_uv(i, vpi) ivec2(int(vpi * (uint(i) % (WR_MAX_VERTEX_TEXTURE_WIDTH/vpi))), int(uint(i) / (WR_MAX_VERTEX_TEXTURE_WIDTH/vpi)))
#endif
//======================================================================================
// Fragment shader attributes and uniforms
//======================================================================================
#ifdef WR_FRAGMENT_SHADER
// Uniform inputs
// Fragment shader outputs
#ifdef WR_FEATURE_ADVANCED_BLEND
layout(blend_support_all_equations) out;
#endif
#if __VERSION__ == 100
#define oFragColor gl_FragColor
#elif defined(WR_FEATURE_DUAL_SOURCE_BLENDING)
layout(location = 0, index = 0) out vec4 oFragColor;
layout(location = 0, index = 1) out vec4 oFragBlend;
#else
out vec4 oFragColor;
#endif
// Write an output color in normal shaders.
void write_output(vec4 color) {
oFragColor = color;
}
#define EPSILON 0.0001
// "Show Overdraw" color. Premultiplied.
#define WR_DEBUG_OVERDRAW_COLOR vec4(0.110, 0.077, 0.027, 0.125)
float distance_to_line(vec2 p0, vec2 perp_dir, vec2 p) {
vec2 dir_to_p0 = p0 - p;
return dot(normalize(perp_dir), dir_to_p0);
}
// fwidth is not defined in ESSL 1, but that's okay because we don't need
// it for any ESSL 1 shader variants.
#if __VERSION__ != 100
/// Find the appropriate half range to apply the AA approximation over.
/// This range represents a coefficient to go from one CSS pixel to half a device pixel.
vec2 compute_aa_range_xy(vec2 position) {
return fwidth(position);
}
float compute_aa_range(vec2 position) {
// The constant factor is chosen to compensate for the fact that length(fw) is equal
// to sqrt(2) times the device pixel ratio in the typical case.
//
// This coefficient is chosen to ensure that any sample 0.5 pixels or more inside of
// the shape has no anti-aliasing applied to it (since pixels are sampled at their center,
// such a pixel (axis aligned) is fully inside the border). We need this so that antialiased
// curves properly connect with non-antialiased vertical or horizontal lines, among other things.
//
// Lines over a half-pixel away from the pixel center *can* intersect with the pixel square;
// indeed, unless they are horizontal or vertical, they are guaranteed to. However, choosing
// a nonzero area for such pixels causes noticeable artifacts at the junction between an anti-
// aliased corner and a straight edge.
//
// We may want to adjust this constant in specific scenarios (for example keep the principled
// value for straight edges where we want pixel-perfect equivalence with non antialiased lines
// when axis aligned, while selecting a larger and smoother aa range on curves).
//
// As a further optimization, we compute the reciprocal of this range, such that we
// can then use the cheaper inversesqrt() instead of length(). This also elides a
// division that would otherwise be necessary inside distance_aa.
#ifdef SWGL
// SWGL uses an approximation for fwidth() such that it returns equal x and y.
// Thus, sqrt(2)/length(w) = sqrt(2)/sqrt(x*x + x*x) = recip(x).
return recip(fwidth(position).x);
#else
// sqrt(2)/length(w) = inversesqrt(0.5 * dot(w, w))
vec2 w = fwidth(position);
return inversesqrt(0.5 * dot(w, w));
#endif
}
#endif
/// Return the blending coefficient for distance antialiasing.
///
/// 0.0 means inside the shape, 1.0 means outside.
///
/// This makes the simplifying assumption that the area of a 1x1 pixel square
/// under a line is reasonably similar to just the signed Euclidian distance
/// from the center of the square to that line. This diverges slightly from
/// better approximations of the exact area, but the difference between the
/// methods is not perceptibly noticeable, while this approximation is much
/// faster to compute.
///
/// See the comments in `compute_aa_range()` for more information on the
/// cutoff values of -0.5 and 0.5.
float distance_aa_xy(vec2 aa_range, vec2 signed_distance) {
// The aa_range is the raw per-axis filter width, so we need to divide
// the local signed distance by the filter width to get an approximation
// of screen distance.
#ifdef SWGL
// The SWGL fwidth() approximation returns uniform X and Y ranges.
vec2 dist = signed_distance * recip(aa_range.x);
#else
vec2 dist = signed_distance / aa_range;
#endif
// Choose whichever axis is further outside the rectangle for AA.
return clamp(0.5 - max(dist.x, dist.y), 0.0, 1.0);
}
float distance_aa(float aa_range, float signed_distance) {
// The aa_range is already stored as a reciprocal with uniform scale,
// so just multiply it, then use that for AA.
float dist = signed_distance * aa_range;
return clamp(0.5 - dist, 0.0, 1.0);
}
/// Component-wise selection.
///
/// The idea of using this is to ensure both potential branches are executed before
/// selecting the result, to avoid observable timing differences based on the condition.
///
/// Example usage: color = if_then_else(LessThanEqual(color, vec3(0.5)), vec3(0.0), vec3(1.0));
///
/// The above example sets each component to 0.0 or 1.0 independently depending on whether
/// their values are below or above 0.5.
///
/// This is written as a macro in order to work with vectors of any dimension.
///
/// Note: Some older android devices don't support mix with bvec. If we ever run into them
/// the only option we have is to polyfill it with a branch per component.
#define if_then_else(cond, then_branch, else_branch) mix(else_branch, then_branch, cond)
#endif
//======================================================================================
// Shared shader uniforms
//======================================================================================
#ifdef WR_FEATURE_TEXTURE_2D
uniform sampler2D sColor0;
uniform sampler2D sColor1;
uniform sampler2D sColor2;
#elif defined WR_FEATURE_TEXTURE_RECT
uniform sampler2DRect sColor0;
uniform sampler2DRect sColor1;
uniform sampler2DRect sColor2;
#elif defined(WR_FEATURE_TEXTURE_EXTERNAL) || defined(WR_FEATURE_TEXTURE_EXTERNAL_ESSL1)
uniform samplerExternalOES sColor0;
uniform samplerExternalOES sColor1;
uniform samplerExternalOES sColor2;
#elif defined(WR_FEATURE_TEXTURE_EXTERNAL_BT709)
uniform __samplerExternal2DY2YEXT sColor0;
uniform __samplerExternal2DY2YEXT sColor1;
uniform __samplerExternal2DY2YEXT sColor2;
#endif
#ifdef WR_FEATURE_DITHERING
uniform sampler2D sDither;
#endif
//======================================================================================
// Interpolator definitions
//======================================================================================
//======================================================================================
// VS only types and UBOs
//======================================================================================
//======================================================================================
// VS only functions
//======================================================================================
/* 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 RectWithSize {
vec2 p0;
vec2 size;
};
struct RectWithEndpoint {
vec2 p0;
vec2 p1;
};
float point_inside_rect(vec2 p, vec2 p0, vec2 p1) {
vec2 s = step(p0, p) - step(p1, p);
return s.x * s.y;
}
vec2 signed_distance_rect_xy(vec2 pos, vec2 p0, vec2 p1) {
// Instead of using a true signed distance to rect here, we just use the
// simpler approximation of the maximum distance on either axis from the
// outside of the rectangle. This avoids expensive use of length() and only
// causes mostly imperceptible differences at corner pixels.
return max(p0 - pos, pos - p1);
}
float signed_distance_rect(vec2 pos, vec2 p0, vec2 p1) {
// Collapse the per-axis distances to edges to a single approximate value.
vec2 d = signed_distance_rect_xy(pos, p0, p1);
return max(d.x, d.y);
}
vec2 rect_clamp(RectWithEndpoint rect, vec2 pt) {
return clamp(pt, rect.p0, rect.p1);
}
vec2 rect_size(RectWithEndpoint rect) {
return rect.p1 - rect.p0;
}
/* 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/. */
// Preprocess the radii for computing the distance approximation. This should
// be used in the vertex shader if possible to avoid doing expensive division
// in the fragment shader. When dealing with a point (zero radii), approximate
// it as an ellipse with very small radii so that we don't need to branch.
vec2 inverse_radii_squared(vec2 radii) {
return 1.0 / max(radii * radii, 1.0e-6);
}
#ifdef WR_FRAGMENT_SHADER
// One iteration of Newton's method on the 2D equation of an ellipse:
//
// E(x, y) = x^2/a^2 + y^2/b^2 - 1
//
// The Jacobian of this equation is:
//
// J(E(x, y)) = [ 2*x/a^2 2*y/b^2 ]
//
// We approximate the distance with:
//
// E(x, y) / ||J(E(x, y))||
//
// See G. Taubin, "Distance Approximations for Rasterizing Implicit
// Curves", section 3.
//
// A scale relative to the unit scale of the ellipse may be passed in to cause
// the math to degenerate to length(p) when scale is 0, or otherwise give the
// normal distance approximation if scale is 1.
float distance_to_ellipse_approx(vec2 p, vec2 inv_radii_sq, float scale) {
vec2 p_r = p * inv_radii_sq;
float g = dot(p, p_r) - scale;
vec2 dG = (1.0 + scale) * p_r;
return g * inversesqrt(dot(dG, dG));
}
// Slower but more accurate version that uses the exact distance when dealing
// with a 0-radius point distance and otherwise uses the faster approximation
// when dealing with non-zero radii.
float distance_to_ellipse(vec2 p, vec2 radii) {
return distance_to_ellipse_approx(p, inverse_radii_squared(radii),
float(all(greaterThan(radii, vec2(0.0)))));
}
float distance_to_rounded_rect(
vec2 pos,
vec3 plane_tl,
vec4 center_radius_tl,
vec3 plane_tr,
vec4 center_radius_tr,
vec3 plane_br,
vec4 center_radius_br,
vec3 plane_bl,
vec4 center_radius_bl,
vec4 rect_bounds
) {
// Clip against each ellipse. If the fragment is in a corner, one of the
// branches below will select it as the corner to calculate the distance
// to. We use half-space planes to detect which corner's ellipse the
// fragment is inside, where the plane is defined by a normal and offset.
// If outside any ellipse, default to a small offset so a negative distance
// is returned for it.
vec4 corner = vec4(vec2(1.0e-6), vec2(1.0));
// Calculate the ellipse parameters for each corner.
center_radius_tl.xy = center_radius_tl.xy - pos;
center_radius_tr.xy = (center_radius_tr.xy - pos) * vec2(-1.0, 1.0);
center_radius_br.xy = pos - center_radius_br.xy;
center_radius_bl.xy = (center_radius_bl.xy - pos) * vec2(1.0, -1.0);
// Evaluate each half-space plane in turn to select a corner.
if (dot(pos, plane_tl.xy) > plane_tl.z) {
corner = center_radius_tl;
}
if (dot(pos, plane_tr.xy) > plane_tr.z) {
corner = center_radius_tr;
}
if (dot(pos, plane_br.xy) > plane_br.z) {
corner = center_radius_br;
}
if (dot(pos, plane_bl.xy) > plane_bl.z) {
corner = center_radius_bl;
}
// Calculate the distance of the selected corner and the rectangle bounds,
// whichever is greater.
return max(distance_to_ellipse_approx(corner.xy, corner.zw, 1.0),
signed_distance_rect(pos, rect_bounds.xy, rect_bounds.zw));
}
#endif
#define DONT_MIX 0
#define MIX_AA 1
#define MIX_NO_AA 2
// For edges, the colors are the same. For corners, these
// are the colors of each edge making up the corner.
flat varying mediump vec4 vColor0;
flat varying mediump vec4 vColor1;
// A point + tangent defining the line where the edge
// transition occurs. Used for corners only.
flat varying highp vec4 vColorLine;
// A boolean indicating that we should be mixing between edge colors.
// Packed in to a vector to work around bug 1630356.
flat varying mediump ivec2 vMixColors;
// xy = Local space position of the clip center.
// zw = Scale the rect origin by this to get the outer
// corner from the segment rectangle.
flat varying highp vec4 vClipCenter_Sign;
// An outer and inner elliptical radii for border
// corner clipping.
flat varying highp vec4 vClipRadii;
// Position, scale, and radii of horizontally and vertically adjacent corner clips.
flat varying highp vec4 vHorizontalClipCenter_Sign;
flat varying highp vec2 vHorizontalClipRadii;
flat varying highp vec4 vVerticalClipCenter_Sign;
flat varying highp vec2 vVerticalClipRadii;
// Local space position
varying highp vec2 vPos;
#define SEGMENT_TOP_LEFT 0
#define SEGMENT_TOP_RIGHT 1
#define SEGMENT_BOTTOM_RIGHT 2
#define SEGMENT_BOTTOM_LEFT 3
#ifdef WR_VERTEX_SHADER
PER_INSTANCE in vec2 aTaskOrigin;
PER_INSTANCE in vec4 aRect;
PER_INSTANCE in vec4 aColor0;
PER_INSTANCE in vec4 aColor1;
PER_INSTANCE in int aFlags;
PER_INSTANCE in vec2 aWidths;
PER_INSTANCE in vec2 aRadii;
PER_INSTANCE in vec4 aClipParams1;
PER_INSTANCE in vec4 aClipParams2;
vec2 get_outer_corner_scale(int segment) {
vec2 p;
switch (segment) {
case SEGMENT_TOP_LEFT:
p = vec2(0.0, 0.0);
break;
case SEGMENT_TOP_RIGHT:
p = vec2(1.0, 0.0);
break;
case SEGMENT_BOTTOM_RIGHT:
p = vec2(1.0, 1.0);
break;
case SEGMENT_BOTTOM_LEFT:
p = vec2(0.0, 1.0);
break;
default:
// The result is only used for non-default segment cases
p = vec2(0.0);
break;
}
return p;
}
void main(void) {
int segment = aFlags & 0xff;
bool do_aa = ((aFlags >> 24) & 0xf0) != 0;
vec2 outer_scale = get_outer_corner_scale(segment);
vec2 size = aRect.zw - aRect.xy;
vec2 outer = outer_scale * size;
vec2 clip_sign = 1.0 - 2.0 * outer_scale;
int mix_colors;
switch (segment) {
case SEGMENT_TOP_LEFT:
case SEGMENT_TOP_RIGHT:
case SEGMENT_BOTTOM_RIGHT:
case SEGMENT_BOTTOM_LEFT: {
mix_colors = do_aa ? MIX_AA : MIX_NO_AA;
break;
}
default:
mix_colors = DONT_MIX;
break;
}
vMixColors.x = mix_colors;
vPos = size * aPosition.xy;
vColor0 = aColor0;
vColor1 = aColor1;
vClipCenter_Sign = vec4(outer + clip_sign * aRadii, clip_sign);
vClipRadii = vec4(aRadii, max(aRadii - aWidths, 0.0));
vColorLine = vec4(outer, aWidths.y * -clip_sign.y, aWidths.x * clip_sign.x);
vec2 horizontal_clip_sign = vec2(-clip_sign.x, clip_sign.y);
vHorizontalClipCenter_Sign = vec4(aClipParams1.xy +
horizontal_clip_sign * aClipParams1.zw,
horizontal_clip_sign);
vHorizontalClipRadii = aClipParams1.zw;
vec2 vertical_clip_sign = vec2(clip_sign.x, -clip_sign.y);
vVerticalClipCenter_Sign = vec4(aClipParams2.xy +
vertical_clip_sign * aClipParams2.zw,
vertical_clip_sign);
vVerticalClipRadii = aClipParams2.zw;
gl_Position = uTransform * vec4(aTaskOrigin + aRect.xy + vPos, 0.0, 1.0);
}
#endif
#ifdef WR_FRAGMENT_SHADER
void main(void) {
float aa_range = compute_aa_range(vPos);
bool do_aa = vMixColors.x != MIX_NO_AA;
float mix_factor = 0.0;
if (vMixColors.x != DONT_MIX) {
float d_line = distance_to_line(vColorLine.xy, vColorLine.zw, vPos);
if (do_aa) {
mix_factor = distance_aa(aa_range, -d_line);
} else {
mix_factor = d_line + EPSILON >= 0. ? 1.0 : 0.0;
}
}
// Check if inside main corner clip-region
vec2 clip_relative_pos = vPos - vClipCenter_Sign.xy;
bool in_clip_region = all(lessThan(vClipCenter_Sign.zw * clip_relative_pos, vec2(0.0)));
float d = -1.0;
if (in_clip_region) {
float d_radii_a = distance_to_ellipse(clip_relative_pos, vClipRadii.xy);
float d_radii_b = distance_to_ellipse(clip_relative_pos, vClipRadii.zw);
d = max(d_radii_a, -d_radii_b);
}
// And again for horizontally-adjacent corner
clip_relative_pos = vPos - vHorizontalClipCenter_Sign.xy;
in_clip_region = all(lessThan(vHorizontalClipCenter_Sign.zw * clip_relative_pos, vec2(0.0)));
if (in_clip_region) {
float d_radii = distance_to_ellipse(clip_relative_pos, vHorizontalClipRadii.xy);
d = max(d_radii, d);
}
// And finally for vertically-adjacent corner
clip_relative_pos = vPos - vVerticalClipCenter_Sign.xy;
in_clip_region = all(lessThan(vVerticalClipCenter_Sign.zw * clip_relative_pos, vec2(0.0)));
if (in_clip_region) {
float d_radii = distance_to_ellipse(clip_relative_pos, vVerticalClipRadii.xy);
d = max(d_radii, d);
}
float alpha = do_aa ? distance_aa(aa_range, d) : 1.0;
vec4 color = mix(vColor0, vColor1, mix_factor);
oFragColor = color * alpha;
}
#endif