summaryrefslogtreecommitdiffstats
path: root/gfx/wr/webrender/res/transform.glsl
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--gfx/wr/webrender/res/transform.glsl140
1 files changed, 140 insertions, 0 deletions
diff --git a/gfx/wr/webrender/res/transform.glsl b/gfx/wr/webrender/res/transform.glsl
new file mode 100644
index 0000000000..d068b26c0e
--- /dev/null
+++ b/gfx/wr/webrender/res/transform.glsl
@@ -0,0 +1,140 @@
+/* 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