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 shader renders radial graidents in a color or alpha target.
/* 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/. */
/// The common infrastructure for ps_quad_* shaders.
///
/// # Memory layout
///
/// The diagram below shows the the various pieces of data fectched in the vertex shader:
///
///```ascii
/// (int gpu buffer)
/// +---------------+ (sGpuCache)
/// (instance-step vertex attr) | Int header | +-----------+
/// +-----------------------------+ | | | Transform |
/// | Quad instance (uvec4) | +--> | transform id +--> +-----------+
/// | | | | z id |
/// | x: int prim address +---+ +---------------+ (float gpu buffer)
/// | y: float prim address +--------------------------> +-----------+--------------+-+-+
/// | z: quad flags | (sGpuCache) | Quad Prim | Quad Segment | | |
/// | edge flags | +--------------------+ | | | | |
/// | part index | | Picture task | | bounds | rect | | |
/// | segment index | | | | clip | uv rect | | |
/// | w: picture task address +--> | task rect | | color | | | |
/// +-----------------------------+ | device pixel scale | +-----------+--------------+-+-+
/// | content origin |
/// +--------------------+
///
/// To use the quad infrastructure, a shader must define the following entry
/// points in the corresponding shader stages:
/// - void pattern_vertex(PrimitiveInfo prim)
/// - vec4 pattern_fragment(vec4 base_color)
///```
#define WR_FEATURE_TEXTURE_2D
/* 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/. */
flat varying highp vec4 vTransformBounds;
#ifdef WR_VERTEX_SHADER
#define VECS_PER_TRANSFORM 8U
uniform HIGHP_SAMPLER_FLOAT sampler2D sTransformPalette;
void init_transform_vs(vec4 local_bounds) {
vTransformBounds = local_bounds;
}
struct Transform {
mat4 m;
mat4 inv_m;
bool is_axis_aligned;
};
Transform fetch_transform(int id) {
Transform transform;
transform.is_axis_aligned = (id >> 23) == 0;
int index = id & 0x007fffff;
// Create a UV base coord for each 8 texels.
// This is required because trying to use an offset
// of more than 8 texels doesn't work on some versions
// of macOS.
ivec2 uv = get_fetch_uv(index, VECS_PER_TRANSFORM);
ivec2 uv0 = ivec2(uv.x + 0, uv.y);
transform.m[0] = TEXEL_FETCH(sTransformPalette, uv0, 0, ivec2(0, 0));
transform.m[1] = TEXEL_FETCH(sTransformPalette, uv0, 0, ivec2(1, 0));
transform.m[2] = TEXEL_FETCH(sTransformPalette, uv0, 0, ivec2(2, 0));
transform.m[3] = TEXEL_FETCH(sTransformPalette, uv0, 0, ivec2(3, 0));
transform.inv_m[0] = TEXEL_FETCH(sTransformPalette, uv0, 0, ivec2(4, 0));
transform.inv_m[1] = TEXEL_FETCH(sTransformPalette, uv0, 0, ivec2(5, 0));
transform.inv_m[2] = TEXEL_FETCH(sTransformPalette, uv0, 0, ivec2(6, 0));
transform.inv_m[3] = TEXEL_FETCH(sTransformPalette, uv0, 0, ivec2(7, 0));
return transform;
}
// Return the intersection of the plane (set up by "normal" and "point")
// with the ray (set up by "ray_origin" and "ray_dir"),
// writing the resulting scaler into "t".
bool ray_plane(vec3 normal, vec3 pt, vec3 ray_origin, vec3 ray_dir, out float t)
{
float denom = dot(normal, ray_dir);
if (abs(denom) > 1e-6) {
vec3 d = pt - ray_origin;
t = dot(d, normal) / denom;
return t >= 0.0;
}
return false;
}
// Apply the inverse transform "inv_transform"
// to the reference point "ref" in CSS space,
// producing a local point on a Transform plane,
// set by a base point "a" and a normal "n".
vec4 untransform(vec2 ref, vec3 n, vec3 a, mat4 inv_transform) {
vec3 p = vec3(ref, -10000.0);
vec3 d = vec3(0, 0, 1.0);
float t = 0.0;
// get an intersection of the Transform plane with Z axis vector,
// originated from the "ref" point
ray_plane(n, a, p, d, t);
float z = p.z + d.z * t; // Z of the visible point on the Transform
vec4 r = inv_transform * vec4(ref, z, 1.0);
return r;
}
// Given a CSS space position, transform it back into the Transform space.
vec4 get_node_pos(vec2 pos, Transform transform) {
// get a point on the scroll node plane
vec4 ah = transform.m * vec4(0.0, 0.0, 0.0, 1.0);
vec3 a = ah.xyz / ah.w;
// get the normal to the scroll node plane
vec3 n = transpose(mat3(transform.inv_m)) * vec3(0.0, 0.0, 1.0);
return untransform(pos, n, a, transform.inv_m);
}
#endif //WR_VERTEX_SHADER
#ifdef WR_FRAGMENT_SHADER
// Assume transform bounds are set to a large scale to signal they are invalid.
bool has_valid_transform_bounds() {
return vTransformBounds.w < 1.0e15;
}
float init_transform_fs(vec2 local_pos) {
// Ideally we want to track distances in screen space after transformation
// as signed distance calculations lose context about the direction vector
// to exit the geometry, merely remembering the minimum distance to the
// exit. However, we can't always sanely track distances in screen space
// due to perspective transforms, clipping, and other concerns, so we do
// this in local space. However, this causes problems tracking distances
// in local space when attempting to scale by a uniform AA range later in
// the presence of a transform which actually has non-uniform scaling.
//
// To work around this, we independently track the distances on the local
// space X and Y axes and then scale them by the independent AA ranges (as
// computed from fwidth derivatives) for the X and Y axes. This can break
// down at certain angles (45 degrees or close to it), but still gives a
// better approximation of screen-space distances in the presence of non-
// uniform scaling for other rotations.
//
// Get signed distance from local rect bounds.
vec2 d = signed_distance_rect_xy(
local_pos,
vTransformBounds.xy,
vTransformBounds.zw
);
// Find the appropriate distance to apply the AA smoothstep over.
vec2 aa_range = compute_aa_range_xy(local_pos);
// Only apply AA to fragments outside the signed distance field.
return distance_aa_xy(aa_range, d);
}
float init_transform_rough_fs(vec2 local_pos) {
return point_inside_rect(
local_pos,
vTransformBounds.xy,
vTransformBounds.zw
);
}
#endif //WR_FRAGMENT_SHADER
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifdef WR_VERTEX_SHADER
#define VECS_PER_RENDER_TASK 2U
uniform HIGHP_SAMPLER_FLOAT sampler2D sRenderTasks;
struct RenderTaskData {
RectWithEndpoint task_rect;
vec4 user_data;
};
// See RenderTaskData in render_task.rs
RenderTaskData fetch_render_task_data(int index) {
ivec2 uv = get_fetch_uv(index, VECS_PER_RENDER_TASK);
vec4 texel0 = TEXEL_FETCH(sRenderTasks, uv, 0, ivec2(0, 0));
vec4 texel1 = TEXEL_FETCH(sRenderTasks, uv, 0, ivec2(1, 0));
RectWithEndpoint task_rect = RectWithEndpoint(
texel0.xy,
texel0.zw
);
RenderTaskData data = RenderTaskData(
task_rect,
texel1
);
return data;
}
RectWithEndpoint fetch_render_task_rect(int index) {
ivec2 uv = get_fetch_uv(index, VECS_PER_RENDER_TASK);
vec4 texel0 = TEXEL_FETCH(sRenderTasks, uv, 0, ivec2(0, 0));
vec4 texel1 = TEXEL_FETCH(sRenderTasks, uv, 0, ivec2(1, 0));
RectWithEndpoint task_rect = RectWithEndpoint(
texel0.xy,
texel0.zw
);
return task_rect;
}
#define PIC_TYPE_IMAGE 1
#define PIC_TYPE_TEXT_SHADOW 2
/*
The dynamic picture that this brush exists on. Right now, it
contains minimal information. In the future, it will describe
the transform mode of primitives on this picture, among other things.
*/
struct PictureTask {
RectWithEndpoint task_rect;
float device_pixel_scale;
vec2 content_origin;
};
PictureTask fetch_picture_task(int address) {
RenderTaskData task_data = fetch_render_task_data(address);
PictureTask task = PictureTask(
task_data.task_rect,
task_data.user_data.x,
task_data.user_data.yz
);
return task;
}
#define CLIP_TASK_EMPTY 0x7FFFFFFF
struct ClipArea {
RectWithEndpoint task_rect;
float device_pixel_scale;
vec2 screen_origin;
};
ClipArea fetch_clip_area(int index) {
RenderTaskData task_data;
if (index >= CLIP_TASK_EMPTY) {
// We deliberately create a dummy RenderTaskData here then convert to a
// ClipArea after this if-else statement, rather than initialize the
// ClipArea in separate branches, to avoid a miscompile in some Adreno
// drivers. See bug 1884791. Unfortunately the specific details of the bug
// are unknown, so please take extra care not to regress this when
// refactoring.
task_data = RenderTaskData(RectWithEndpoint(vec2(0.0), vec2(0.0)),
vec4(0.0));
} else {
task_data = fetch_render_task_data(index);
}
return ClipArea(task_data.task_rect, task_data.user_data.x,
task_data.user_data.yz);
}
#endif //WR_VERTEX_SHADER
/* 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 HIGHP_SAMPLER_FLOAT sampler2D sGpuBufferF;
uniform HIGHP_SAMPLER_FLOAT isampler2D sGpuBufferI;
ivec2 get_gpu_buffer_uv(HIGHP_FS_ADDRESS int address) {
return ivec2(uint(address) % WR_MAX_VERTEX_TEXTURE_WIDTH,
uint(address) / WR_MAX_VERTEX_TEXTURE_WIDTH);
}
vec4 fetch_from_gpu_buffer_1f(HIGHP_FS_ADDRESS int address) {
ivec2 uv = get_gpu_buffer_uv(address);
return texelFetch(sGpuBufferF, uv, 0);
}
vec4[2] fetch_from_gpu_buffer_2f(HIGHP_FS_ADDRESS int address) {
ivec2 uv = get_gpu_buffer_uv(address);
return vec4[2](
TEXEL_FETCH(sGpuBufferF, uv, 0, ivec2(0, 0)),
TEXEL_FETCH(sGpuBufferF, uv, 0, ivec2(1, 0))
);
}
vec4[3] fetch_from_gpu_buffer_3f(HIGHP_FS_ADDRESS int address) {
ivec2 uv = get_gpu_buffer_uv(address);
return vec4[3](
TEXEL_FETCH(sGpuBufferF, uv, 0, ivec2(0, 0)),
TEXEL_FETCH(sGpuBufferF, uv, 0, ivec2(1, 0)),
TEXEL_FETCH(sGpuBufferF, uv, 0, ivec2(2, 0))
);
}
vec4[4] fetch_from_gpu_buffer_4f(HIGHP_FS_ADDRESS int address) {
ivec2 uv = get_gpu_buffer_uv(address);
return vec4[4](
TEXEL_FETCH(sGpuBufferF, uv, 0, ivec2(0, 0)),
TEXEL_FETCH(sGpuBufferF, uv, 0, ivec2(1, 0)),
TEXEL_FETCH(sGpuBufferF, uv, 0, ivec2(2, 0)),
TEXEL_FETCH(sGpuBufferF, uv, 0, ivec2(3, 0))
);
}
ivec4 fetch_from_gpu_buffer_1i(HIGHP_FS_ADDRESS int address) {
ivec2 uv = get_gpu_buffer_uv(address);
return texelFetch(sGpuBufferI, uv, 0);
}
flat varying mediump vec4 v_color;
// w: has edge flags
// x,y,z are avaible for patterns to use.
flat varying lowp ivec4 v_flags;
#ifndef SWGL_ANTIALIAS
varying highp vec2 vLocalPos;
#endif
#ifdef WR_VERTEX_SHADER
#define EDGE_AA_LEFT 1
#define EDGE_AA_TOP 2
#define EDGE_AA_RIGHT 4
#define EDGE_AA_BOTTOM 8
#define PART_CENTER 0
#define PART_LEFT 1
#define PART_TOP 2
#define PART_RIGHT 3
#define PART_BOTTOM 4
#define PART_ALL 5
#define QF_IS_OPAQUE 1
#define QF_APPLY_DEVICE_CLIP 2
#define QF_IGNORE_DEVICE_SCALE 4
#define QF_USE_AA_SEGMENTS 8
#define QF_SAMPLE_AS_MASK 16
#define INVALID_SEGMENT_INDEX 0xff
#define AA_PIXEL_RADIUS 2.0
PER_INSTANCE in ivec4 aData;
struct QuadSegment {
RectWithEndpoint rect;
RectWithEndpoint uv_rect;
};
struct PrimitiveInfo {
vec2 local_pos;
RectWithEndpoint local_prim_rect;
RectWithEndpoint local_clip_rect;
QuadSegment segment;
int edge_flags;
int quad_flags;
ivec2 pattern_input;
};
struct QuadPrimitive {
RectWithEndpoint bounds;
RectWithEndpoint clip;
vec4 color;
};
QuadSegment fetch_segment(int base, int index) {
QuadSegment seg;
vec4 texels[2] = fetch_from_gpu_buffer_2f(base + 3 + index * 2);
seg.rect = RectWithEndpoint(texels[0].xy, texels[0].zw);
seg.uv_rect = RectWithEndpoint(texels[1].xy, texels[1].zw);
return seg;
}
QuadPrimitive fetch_primitive(int index) {
QuadPrimitive prim;
vec4 texels[3] = fetch_from_gpu_buffer_3f(index);
prim.bounds = RectWithEndpoint(texels[0].xy, texels[0].zw);
prim.clip = RectWithEndpoint(texels[1].xy, texels[1].zw);
prim.color = texels[2];
return prim;
}
struct QuadHeader {
int transform_id;
int z_id;
ivec2 pattern_input;
};
QuadHeader fetch_header(int address) {
ivec4 header = fetch_from_gpu_buffer_1i(address);
QuadHeader qh = QuadHeader(
header.x,
header.y,
header.zw
);
return qh;
}
struct QuadInstance {
// x
int prim_address_i;
// y
int prim_address_f;
// z
int quad_flags;
int edge_flags;
int part_index;
int segment_index;
// w
int picture_task_address;
};
QuadInstance decode_instance() {
QuadInstance qi = QuadInstance(
aData.x,
aData.y,
(aData.z >> 24) & 0xff,
(aData.z >> 16) & 0xff,
(aData.z >> 8) & 0xff,
(aData.z >> 0) & 0xff,
aData.w
);
return qi;
}
struct VertexInfo {
vec2 local_pos;
};
VertexInfo write_vertex(vec2 local_pos,
float z,
Transform transform,
vec2 content_origin,
RectWithEndpoint task_rect,
float device_pixel_scale,
int quad_flags) {
VertexInfo vi;
// Transform the current vertex to world space.
vec4 world_pos = transform.m * vec4(local_pos, 0.0, 1.0);
// Convert the world positions to device pixel space.
vec2 device_pos = world_pos.xy * device_pixel_scale;
if ((quad_flags & QF_APPLY_DEVICE_CLIP) != 0) {
RectWithEndpoint device_clip_rect = RectWithEndpoint(
content_origin,
content_origin + task_rect.p1 - task_rect.p0
);
// Clip to task rect
device_pos = rect_clamp(device_clip_rect, device_pos);
vi.local_pos = (transform.inv_m * vec4(device_pos / device_pixel_scale, 0.0, 1.0)).xy;
} else {
vi.local_pos = local_pos;
}
// Apply offsets for the render task to get correct screen location.
vec2 final_offset = -content_origin + task_rect.p0;
gl_Position = uTransform * vec4(device_pos + final_offset * world_pos.w, z * world_pos.w, world_pos.w);
return vi;
}
float edge_aa_offset(int edge, int flags) {
return ((flags & edge) != 0) ? AA_PIXEL_RADIUS : 0.0;
}
#ifdef WR_VERTEX_SHADER
void pattern_vertex(PrimitiveInfo prim);
#endif
PrimitiveInfo quad_primive_info(void) {
QuadInstance qi = decode_instance();
QuadHeader qh = fetch_header(qi.prim_address_i);
Transform transform = fetch_transform(qh.transform_id);
PictureTask task = fetch_picture_task(qi.picture_task_address);
QuadPrimitive prim = fetch_primitive(qi.prim_address_f);
float z = float(qh.z_id);
QuadSegment seg;
if (qi.segment_index == INVALID_SEGMENT_INDEX) {
seg.rect = prim.bounds;
seg.uv_rect = RectWithEndpoint(vec2(0.0), vec2(0.0));
} else {
seg = fetch_segment(qi.prim_address_f, qi.segment_index);
}
// The local space rect that we will draw, which is effectively:
// - The tile within the primitive we will draw
// - Intersected with any local-space clip rect(s)
// - Expanded for AA edges where appropriate
RectWithEndpoint local_coverage_rect = seg.rect;
// Apply local clip rect
local_coverage_rect.p0 = max(local_coverage_rect.p0, prim.clip.p0);
local_coverage_rect.p1 = min(local_coverage_rect.p1, prim.clip.p1);
local_coverage_rect.p1 = max(local_coverage_rect.p0, local_coverage_rect.p1);
switch (qi.part_index) {
case PART_LEFT:
local_coverage_rect.p1.x = local_coverage_rect.p0.x + AA_PIXEL_RADIUS;
#ifdef SWGL_ANTIALIAS
swgl_antiAlias(EDGE_AA_LEFT);
#else
local_coverage_rect.p0.x -= AA_PIXEL_RADIUS;
local_coverage_rect.p0.y -= AA_PIXEL_RADIUS;
local_coverage_rect.p1.y += AA_PIXEL_RADIUS;
#endif
break;
case PART_TOP:
local_coverage_rect.p0.x = local_coverage_rect.p0.x + AA_PIXEL_RADIUS;
local_coverage_rect.p1.x = local_coverage_rect.p1.x - AA_PIXEL_RADIUS;
local_coverage_rect.p1.y = local_coverage_rect.p0.y + AA_PIXEL_RADIUS;
#ifdef SWGL_ANTIALIAS
swgl_antiAlias(EDGE_AA_TOP);
#else
local_coverage_rect.p0.y -= AA_PIXEL_RADIUS;
#endif
break;
case PART_RIGHT:
local_coverage_rect.p0.x = local_coverage_rect.p1.x - AA_PIXEL_RADIUS;
#ifdef SWGL_ANTIALIAS
swgl_antiAlias(EDGE_AA_RIGHT);
#else
local_coverage_rect.p1.x += AA_PIXEL_RADIUS;
local_coverage_rect.p0.y -= AA_PIXEL_RADIUS;
local_coverage_rect.p1.y += AA_PIXEL_RADIUS;
#endif
break;
case PART_BOTTOM:
local_coverage_rect.p0.x = local_coverage_rect.p0.x + AA_PIXEL_RADIUS;
local_coverage_rect.p1.x = local_coverage_rect.p1.x - AA_PIXEL_RADIUS;
local_coverage_rect.p0.y = local_coverage_rect.p1.y - AA_PIXEL_RADIUS;
#ifdef SWGL_ANTIALIAS
swgl_antiAlias(EDGE_AA_BOTTOM);
#else
local_coverage_rect.p1.y += AA_PIXEL_RADIUS;
#endif
break;
case PART_CENTER:
local_coverage_rect.p0.x += edge_aa_offset(EDGE_AA_LEFT, qi.edge_flags);
local_coverage_rect.p1.x -= edge_aa_offset(EDGE_AA_RIGHT, qi.edge_flags);
local_coverage_rect.p0.y += edge_aa_offset(EDGE_AA_TOP, qi.edge_flags);
local_coverage_rect.p1.y -= edge_aa_offset(EDGE_AA_BOTTOM, qi.edge_flags);
break;
case PART_ALL:
default:
#ifdef SWGL_ANTIALIAS
swgl_antiAlias(qi.edge_flags);
#else
local_coverage_rect.p0.x -= edge_aa_offset(EDGE_AA_LEFT, qi.edge_flags);
local_coverage_rect.p1.x += edge_aa_offset(EDGE_AA_RIGHT, qi.edge_flags);
local_coverage_rect.p0.y -= edge_aa_offset(EDGE_AA_TOP, qi.edge_flags);
local_coverage_rect.p1.y += edge_aa_offset(EDGE_AA_BOTTOM, qi.edge_flags);
#endif
break;
}
vec2 local_pos = mix(local_coverage_rect.p0, local_coverage_rect.p1, aPosition);
float device_pixel_scale = task.device_pixel_scale;
if ((qi.quad_flags & QF_IGNORE_DEVICE_SCALE) != 0) {
device_pixel_scale = 1.0f;
}
VertexInfo vi = write_vertex(
local_pos,
z,
transform,
task.content_origin,
task.task_rect,
device_pixel_scale,
qi.quad_flags
);
v_color = prim.color;
return PrimitiveInfo(
vi.local_pos,
prim.bounds,
prim.clip,
seg,
qi.edge_flags,
qi.quad_flags,
qh.pattern_input
);
}
void antialiasing_vertex(PrimitiveInfo prim) {
#ifndef SWGL_ANTIALIAS
// This does the setup that is required for init_tranform_vs.
RectWithEndpoint xf_bounds = RectWithEndpoint(
max(prim.local_prim_rect.p0, prim.local_clip_rect.p0),
min(prim.local_prim_rect.p1, prim.local_clip_rect.p1)
);
vTransformBounds = vec4(xf_bounds.p0, xf_bounds.p1);
vLocalPos = prim.local_pos;
if (prim.edge_flags == 0) {
v_flags.w = 0;
} else {
v_flags.w = 1;
}
#endif
}
void main() {
PrimitiveInfo prim = quad_primive_info();
antialiasing_vertex(prim);
pattern_vertex(prim);
}
#endif
#ifdef WR_FRAGMENT_SHADER
vec4 pattern_fragment(vec4 base_color);
float antialiasing_fragment() {
float alpha = 1.0;
#ifndef SWGL_ANTIALIAS
if (v_flags.w != 0) {
alpha = init_transform_fs(vLocalPos);
}
#endif
return alpha;
}
void main() {
vec4 base_color = v_color;
base_color *= antialiasing_fragment();
oFragColor = pattern_fragment(base_color);
}
#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/. */
// Gradient GPU cache address.
// Packed in to a vector to work around bug 1630356.
flat varying highp ivec2 v_gradient_address;
// Repetition along the gradient stops.
// Packed in to a vector to work around bug 1630356.
flat varying mediump vec2 v_gradient_repeat;
#ifdef WR_FRAGMENT_SHADER
#ifdef WR_FEATURE_DITHERING
vec4 dither(vec4 color) {
const int matrix_mask = 7;
ivec2 pos = ivec2(gl_FragCoord.xy) & ivec2(matrix_mask);
float noise_normalized = (texelFetch(sDither, pos, 0).r * 255.0 + 0.5) / 64.0;
float noise = (noise_normalized - 0.5) / 256.0; // scale down to the unit length
return color + vec4(noise, noise, noise, 0);
}
#else
vec4 dither(vec4 color) {
return color;
}
#endif //WR_FEATURE_DITHERING
#define GRADIENT_ENTRIES 128.0
float clamp_gradient_entry(float offset) {
// Calculate the color entry index to use for this offset:
// offsets < 0 use the first color entry, 0
// offsets from [0, 1) use the color entries in the range of [1, N-1)
// offsets >= 1 use the last color entry, N-1
// so transform the range [0, 1) -> [1, N-1)
// TODO(gw): In the future we might consider making the size of the
// LUT vary based on number / distribution of stops in the gradient.
// Ensure we don't fetch outside the valid range of the LUT.
return clamp(1.0 + offset * GRADIENT_ENTRIES, 0.0, 1.0 + GRADIENT_ENTRIES);
}
vec4 sample_gradient(float offset) {
// Modulo the offset if the gradient repeats.
offset -= floor(offset) * v_gradient_repeat.x;
// Calculate the texel to index into the gradient color entries:
// floor(x) is the gradient color entry index
// fract(x) is the linear filtering factor between start and end
float x = clamp_gradient_entry(offset);
float entry_index = floor(x);
float entry_fract = x - entry_index;
// Fetch the start and end color. There is a [start, end] color per entry.
vec4 texels[2] = fetch_from_gpu_buffer_2f(v_gradient_address.x + 2 * int(entry_index));
// Finally interpolate and apply dithering
return dither(texels[0] + texels[1] * entry_fract);
}
#endif //WR_FRAGMENT_SHADER
#define PI 3.141592653589793
// x: start offset, y: offset scale, z: angle
// Packed in to a vector to work around bug 1630356.
flat varying highp vec3 v_start_offset_offset_scale_angle_vec;
#define v_start_offset v_start_offset_offset_scale_angle_vec.x
#define v_offset_scale v_start_offset_offset_scale_angle_vec.y
#define v_angle v_start_offset_offset_scale_angle_vec.z
varying highp vec2 v_dir;
#ifdef WR_VERTEX_SHADER
struct ConicGradient {
vec2 center;
vec2 scale;
float start_offset;
float end_offset;
float angle;
// 1.0 if the gradient should be repeated, 0.0 otherwise.
float repeat;
};
ConicGradient fetch_conic_gradient(int address) {
vec4[2] data = fetch_from_gpu_buffer_2f(address);
return ConicGradient(
data[0].xy,
data[0].zw,
data[1].x,
data[1].y,
data[1].z,
data[1].w
);
}
void pattern_vertex(PrimitiveInfo info) {
ConicGradient gradient = fetch_conic_gradient(info.pattern_input.x);
v_gradient_address.x = info.pattern_input.y;
v_gradient_repeat.x = gradient.repeat;
// Store 1/d where d = end_offset - start_offset
// If d = 0, we can't get its reciprocal. Instead, just use a zero scale.
float d = gradient.end_offset - gradient.start_offset;
v_offset_scale = d != 0.0 ? 1.0 / d : 0.0;
v_angle = PI / 2.0 - gradient.angle;
v_start_offset = gradient.start_offset * v_offset_scale;
v_dir = ((info.local_pos - info.local_prim_rect.p0) * gradient.scale - gradient.center);
}
#endif
#ifdef WR_FRAGMENT_SHADER
float approx_atan2(float y, float x) {
vec2 a = abs(vec2(x, y));
float slope = min(a.x, a.y) / max(a.x, a.y);
float s2 = slope * slope;
float r = ((-0.0464964749 * s2 + 0.15931422) * s2 - 0.327622764) * s2 * slope + slope;
r = if_then_else(float(a.y > a.x), 1.57079637 - r, r);
r = if_then_else(float(x < 0.0), 3.14159274 - r, r);
// To match atan2's behavior, -0.0 should count as negative and flip the sign of r.
// Does this matter in practice in the context of conic gradients?
r = r * sign(y);
return r;
}
vec4 pattern_fragment(vec4 color) {
// Use inverse trig to find the angle offset from the relative position.
vec2 current_dir = v_dir;
float current_angle = approx_atan2(current_dir.y, current_dir.x) + v_angle;
float offset = fract(current_angle / (2.0 * PI)) * v_offset_scale - v_start_offset;
color *= sample_gradient(offset);
return color;
}
#endif