summaryrefslogtreecommitdiffstats
path: root/gfx/wr/webrender/res/ps_text_run.glsl
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/wr/webrender/res/ps_text_run.glsl')
-rw-r--r--gfx/wr/webrender/res/ps_text_run.glsl354
1 files changed, 354 insertions, 0 deletions
diff --git a/gfx/wr/webrender/res/ps_text_run.glsl b/gfx/wr/webrender/res/ps_text_run.glsl
new file mode 100644
index 0000000000..e7faa1247b
--- /dev/null
+++ b/gfx/wr/webrender/res/ps_text_run.glsl
@@ -0,0 +1,354 @@
+/* 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/. */
+
+#include shared,prim_shared
+
+flat varying vec4 v_color;
+flat varying vec3 v_mask_swizzle;
+// Normalized bounds of the source image in the texture.
+flat varying vec4 v_uv_bounds;
+
+// Interpolated UV coordinates to sample.
+varying vec2 v_uv;
+
+
+#if defined(WR_FEATURE_GLYPH_TRANSFORM) && !defined(SWGL_CLIP_DIST)
+varying vec4 v_uv_clip;
+#endif
+
+#ifdef WR_VERTEX_SHADER
+
+#define VECS_PER_TEXT_RUN 2
+#define GLYPHS_PER_GPU_BLOCK 2U
+
+#ifdef WR_FEATURE_GLYPH_TRANSFORM
+RectWithEndpoint transform_rect(RectWithEndpoint rect, mat2 transform) {
+ vec2 size = rect_size(rect);
+ vec2 center = transform * (rect.p0 + size * 0.5);
+ vec2 radius = mat2(abs(transform[0]), abs(transform[1])) * (size * 0.5);
+ return RectWithEndpoint(center - radius, center + radius);
+}
+
+bool rect_inside_rect(RectWithEndpoint little, RectWithEndpoint big) {
+ return all(lessThanEqual(vec4(big.p0, little.p1), vec4(little.p0, big.p1)));
+}
+#endif //WR_FEATURE_GLYPH_TRANSFORM
+
+struct Glyph {
+ vec2 offset;
+};
+
+Glyph fetch_glyph(int specific_prim_address,
+ int glyph_index) {
+ // Two glyphs are packed in each texel in the GPU cache.
+ int glyph_address = specific_prim_address +
+ VECS_PER_TEXT_RUN +
+ int(uint(glyph_index) / GLYPHS_PER_GPU_BLOCK);
+ vec4 data = fetch_from_gpu_cache_1(glyph_address);
+ // Select XY or ZW based on glyph index.
+ vec2 glyph = mix(data.xy, data.zw,
+ bvec2(uint(glyph_index) % GLYPHS_PER_GPU_BLOCK == 1U));
+
+ return Glyph(glyph);
+}
+
+struct GlyphResource {
+ vec4 uv_rect;
+ vec2 offset;
+ float scale;
+};
+
+GlyphResource fetch_glyph_resource(int address) {
+ vec4 data[2] = fetch_from_gpu_cache_2(address);
+ return GlyphResource(data[0], data[1].xy, data[1].z);
+}
+
+struct TextRun {
+ vec4 color;
+ vec4 bg_color;
+};
+
+TextRun fetch_text_run(int address) {
+ vec4 data[2] = fetch_from_gpu_cache_2(address);
+ return TextRun(data[0], data[1]);
+}
+
+vec2 get_snap_bias(int subpx_dir) {
+ // In subpixel mode, the subpixel offset has already been
+ // accounted for while rasterizing the glyph. However, we
+ // must still round with a subpixel bias rather than rounding
+ // to the nearest whole pixel, depending on subpixel direciton.
+ switch (subpx_dir) {
+ case SUBPX_DIR_NONE:
+ default:
+ return vec2(0.5);
+ case SUBPX_DIR_HORIZONTAL:
+ // Glyphs positioned [-0.125, 0.125] get a
+ // subpx position of zero. So include that
+ // offset in the glyph position to ensure
+ // we round to the correct whole position.
+ return vec2(0.125, 0.5);
+ case SUBPX_DIR_VERTICAL:
+ return vec2(0.5, 0.125);
+ case SUBPX_DIR_MIXED:
+ return vec2(0.125);
+ }
+}
+
+void main() {
+ Instance instance = decode_instance_attributes();
+ PrimitiveHeader ph = fetch_prim_header(instance.prim_header_address);
+ Transform transform = fetch_transform(ph.transform_id);
+ ClipArea clip_area = fetch_clip_area(instance.clip_address);
+ PictureTask task = fetch_picture_task(instance.picture_task_address);
+
+ int glyph_index = instance.segment_index;
+ int subpx_dir = (instance.flags >> 8) & 0xff;
+ int color_mode = instance.flags & 0xff;
+
+ // Note that the reference frame relative offset is stored in the prim local
+ // rect size during batching, instead of the actual size of the primitive.
+ TextRun text = fetch_text_run(ph.specific_prim_address);
+ vec2 text_offset = ph.local_rect.p1;
+
+ if (color_mode == COLOR_MODE_FROM_PASS) {
+ color_mode = uMode;
+ }
+
+ // Note that the unsnapped reference frame relative offset has already
+ // been subtracted from the prim local rect origin during batching.
+ // It was done this way to avoid pushing both the snapped and the
+ // unsnapped offsets to the shader.
+ Glyph glyph = fetch_glyph(ph.specific_prim_address, glyph_index);
+ glyph.offset += ph.local_rect.p0;
+
+ GlyphResource res = fetch_glyph_resource(instance.resource_address);
+
+ vec2 snap_bias = get_snap_bias(subpx_dir);
+
+ // Glyph space refers to the pixel space used by glyph rasterization during frame
+ // building. If a non-identity transform was used, WR_FEATURE_GLYPH_TRANSFORM will
+ // be set. Otherwise, regardless of whether the raster space is LOCAL or SCREEN,
+ // we ignored the transform during glyph rasterization, and need to snap just using
+ // the device pixel scale and the raster scale.
+#ifdef WR_FEATURE_GLYPH_TRANSFORM
+ // Transform from local space to glyph space.
+ mat2 glyph_transform = mat2(transform.m) * task.device_pixel_scale;
+ vec2 glyph_translation = transform.m[3].xy * task.device_pixel_scale;
+
+ // Transform from glyph space back to local space.
+ mat2 glyph_transform_inv = inverse(glyph_transform);
+
+ // Glyph raster pixels include the impact of the transform. This path can only be
+ // entered for 3d transforms that can be coerced into a 2d transform; they have no
+ // perspective, and have a 2d inverse. This is a looser condition than axis aligned
+ // transforms because it also allows 2d rotations.
+ vec2 raster_glyph_offset = floor(glyph_transform * glyph.offset + snap_bias);
+
+ // We want to eliminate any subpixel translation in device space to ensure glyph
+ // snapping is stable for equivalent glyph subpixel positions. Note that we must take
+ // into account the translation from the transform for snapping purposes.
+ vec2 raster_text_offset = floor(glyph_transform * text_offset + glyph_translation + 0.5) - glyph_translation;
+
+ vec2 glyph_origin = res.offset + raster_glyph_offset + raster_text_offset;
+ // Compute the glyph rect in glyph space.
+ RectWithEndpoint glyph_rect = RectWithEndpoint(
+ glyph_origin,
+ glyph_origin + res.uv_rect.zw - res.uv_rect.xy
+ );
+
+ // The glyph rect is in glyph space, so transform it back to local space.
+ RectWithEndpoint local_rect = transform_rect(glyph_rect, glyph_transform_inv);
+
+ // Select the corner of the glyph's local space rect that we are processing.
+ vec2 local_pos = mix(local_rect.p0, local_rect.p1, aPosition.xy);
+
+ // If the glyph's local rect would fit inside the local clip rect, then select a corner from
+ // the device space glyph rect to reduce overdraw of clipped pixels in the fragment shader.
+ // Otherwise, fall back to clamping the glyph's local rect to the local clip rect.
+ if (rect_inside_rect(local_rect, ph.local_clip_rect)) {
+ local_pos = glyph_transform_inv * mix(glyph_rect.p0, glyph_rect.p1, aPosition.xy);
+ }
+#else
+ float raster_scale = float(ph.user_data.x) / 65535.0;
+
+ // Scale in which the glyph is snapped when rasterized.
+ float glyph_raster_scale = raster_scale * task.device_pixel_scale;
+
+ // Scale from glyph space to local space.
+ float glyph_scale_inv = res.scale / glyph_raster_scale;
+
+ // Glyph raster pixels do not include the impact of the transform. Instead it was
+ // replaced with an identity transform during glyph rasterization. As such only the
+ // impact of the raster scale (if in local space) and the device pixel scale (for both
+ // local and screen space) are included.
+ //
+ // This implies one or more of the following conditions:
+ // - The transform is an identity. In that case, setting WR_FEATURE_GLYPH_TRANSFORM
+ // should have the same output result as not. We just distingush which path to use
+ // based on the transform used during glyph rasterization. (Screen space).
+ // - The transform contains an animation. We will imply local raster space in such
+ // cases to avoid constantly rerasterizing the glyphs.
+ // - The transform has perspective or does not have a 2d inverse (Screen or local space).
+ // - The transform's scale will result in result in very large rasterized glyphs and
+ // we clamped the size. This will imply local raster space.
+ vec2 raster_glyph_offset = floor(glyph.offset * glyph_raster_scale + snap_bias) / res.scale;
+
+ // Compute the glyph rect in local space.
+ //
+ // The transform may be animated, so we don't want to do any snapping here for the
+ // text offset to avoid glyphs wiggling. The text offset should have been snapped
+ // already for axis aligned transforms excluding any animations during frame building.
+ vec2 glyph_origin = glyph_scale_inv * (res.offset + raster_glyph_offset) + text_offset;
+ RectWithEndpoint glyph_rect = RectWithEndpoint(
+ glyph_origin,
+ glyph_origin + glyph_scale_inv * (res.uv_rect.zw - res.uv_rect.xy)
+ );
+
+ // Select the corner of the glyph rect that we are processing.
+ vec2 local_pos = mix(glyph_rect.p0, glyph_rect.p1, aPosition.xy);
+#endif
+
+ VertexInfo vi = write_vertex(
+ local_pos,
+ ph.local_clip_rect,
+ ph.z,
+ transform,
+ task
+ );
+
+#ifdef WR_FEATURE_GLYPH_TRANSFORM
+ vec2 f = (glyph_transform * vi.local_pos - glyph_rect.p0) / rect_size(glyph_rect);
+ #ifdef SWGL_CLIP_DIST
+ gl_ClipDistance[0] = f.x;
+ gl_ClipDistance[1] = f.y;
+ gl_ClipDistance[2] = 1.0 - f.x;
+ gl_ClipDistance[3] = 1.0 - f.y;
+ #else
+ v_uv_clip = vec4(f, 1.0 - f);
+ #endif
+#else
+ vec2 f = (vi.local_pos - glyph_rect.p0) / rect_size(glyph_rect);
+#endif
+
+ write_clip(vi.world_pos, clip_area, task);
+
+ switch (color_mode) {
+ case COLOR_MODE_ALPHA:
+ v_mask_swizzle = vec3(0.0, 1.0, 1.0);
+ v_color = text.color;
+ break;
+ case COLOR_MODE_BITMAP_SHADOW:
+ #ifdef SWGL_BLEND
+ swgl_blendDropShadow(text.color);
+ v_mask_swizzle = vec3(1.0, 0.0, 0.0);
+ v_color = vec4(1.0);
+ #else
+ v_mask_swizzle = vec3(0.0, 1.0, 0.0);
+ v_color = text.color;
+ #endif
+ break;
+ case COLOR_MODE_SUBPX_BG_PASS2:
+ v_mask_swizzle = vec3(1.0, 0.0, 0.0);
+ v_color = text.color;
+ break;
+ case COLOR_MODE_SUBPX_BG_PASS0:
+ case COLOR_MODE_COLOR_BITMAP:
+ v_mask_swizzle = vec3(1.0, 0.0, 0.0);
+ v_color = vec4(text.color.a);
+ break;
+ case COLOR_MODE_SUBPX_BG_PASS1:
+ v_mask_swizzle = vec3(-1.0, 1.0, 0.0);
+ v_color = vec4(text.color.a) * text.bg_color;
+ break;
+ case COLOR_MODE_SUBPX_DUAL_SOURCE:
+ #ifdef SWGL_BLEND
+ swgl_blendSubpixelText(text.color);
+ v_mask_swizzle = vec3(1.0, 0.0, 0.0);
+ v_color = vec4(1.0);
+ #else
+ v_mask_swizzle = vec3(text.color.a, 0.0, 0.0);
+ v_color = text.color;
+ #endif
+ break;
+ default:
+ v_mask_swizzle = vec3(0.0, 0.0, 0.0);
+ v_color = vec4(1.0);
+ }
+
+ vec2 texture_size = vec2(TEX_SIZE(sColor0));
+ vec2 st0 = res.uv_rect.xy / texture_size;
+ vec2 st1 = res.uv_rect.zw / texture_size;
+
+ v_uv = mix(st0, st1, f);
+ v_uv_bounds = (res.uv_rect + vec4(0.5, 0.5, -0.5, -0.5)) / texture_size.xyxy;
+}
+
+#endif // WR_VERTEX_SHADER
+
+#ifdef WR_FRAGMENT_SHADER
+
+Fragment text_fs(void) {
+ Fragment frag;
+
+ vec2 tc = clamp(v_uv, v_uv_bounds.xy, v_uv_bounds.zw);
+ vec4 mask = texture(sColor0, tc);
+ // v_mask_swizzle.z != 0 means we are using an R8 texture as alpha,
+ // and therefore must swizzle from the r channel to all channels.
+ mask = mix(mask, mask.rrrr, bvec4(v_mask_swizzle.z != 0.0));
+ #ifndef WR_FEATURE_DUAL_SOURCE_BLENDING
+ mask.rgb = mask.rgb * v_mask_swizzle.x + mask.aaa * v_mask_swizzle.y;
+ #endif
+
+ #if defined(WR_FEATURE_GLYPH_TRANSFORM) && !defined(SWGL_CLIP_DIST)
+ mask *= float(all(greaterThanEqual(v_uv_clip, vec4(0.0))));
+ #endif
+
+ frag.color = v_color * mask;
+
+ #if defined(WR_FEATURE_DUAL_SOURCE_BLENDING) && !defined(SWGL_BLEND)
+ frag.blend = mask * v_mask_swizzle.x + mask.aaaa * v_mask_swizzle.y;
+ #endif
+
+ return frag;
+}
+
+
+void main() {
+ Fragment frag = text_fs();
+
+ float clip_mask = do_clip();
+ frag.color *= clip_mask;
+
+ #if defined(WR_FEATURE_DEBUG_OVERDRAW)
+ oFragColor = WR_DEBUG_OVERDRAW_COLOR;
+ #elif defined(WR_FEATURE_DUAL_SOURCE_BLENDING) && !defined(SWGL_BLEND)
+ oFragColor = frag.color;
+ oFragBlend = frag.blend * clip_mask;
+ #else
+ write_output(frag.color);
+ #endif
+}
+
+#if defined(SWGL_DRAW_SPAN) && defined(SWGL_BLEND) && defined(SWGL_CLIP_DIST)
+void swgl_drawSpanRGBA8() {
+ // Only support simple swizzles for now. More complex swizzles must either
+ // be handled by blend overrides or the slow path.
+ if (v_mask_swizzle.x != 0.0 && v_mask_swizzle.x != 1.0) {
+ return;
+ }
+
+ #ifdef WR_FEATURE_DUAL_SOURCE_BLENDING
+ swgl_commitTextureLinearRGBA8(sColor0, v_uv, v_uv_bounds);
+ #else
+ if (swgl_isTextureR8(sColor0)) {
+ swgl_commitTextureLinearColorR8ToRGBA8(sColor0, v_uv, v_uv_bounds, v_color);
+ } else {
+ swgl_commitTextureLinearColorRGBA8(sColor0, v_uv, v_uv_bounds, v_color);
+ }
+ #endif
+}
+#endif
+
+#endif // WR_FRAGMENT_SHADER