Copy as Markdown
Other Tools
#define SWGL 1
#define __VERSION__ 150
#define WR_MAX_VERTEX_TEXTURE_WIDTH 1024U
#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
// Composite a picture cache tile into the framebuffer.
// This shader must remain compatible with ESSL 1, at least for the
// WR_FEATURE_TEXTURE_EXTERNAL_ESSL1 feature, so that it can be used to render
// video on GLES devices without GL_OES_EGL_image_external_essl3 support.
// This means we cannot use textureSize(), int inputs/outputs, etc.
/* 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
#ifdef WR_FEATURE_TEXTURE_EXTERNAL
// Please check https://www.khronos.org/registry/OpenGL/extensions/OES/OES_EGL_image_external_essl3.txt
// 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
#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
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
// Keep these in sync with the corresponding constants in gpu_types.rs
// Specifies that the UV coordinates supplied to certain shaders are normalized.
#define UV_TYPE_NORMALIZED 0
// Specifies that the UV coordinates supplied to certain shaders are not normalized.
#define UV_TYPE_UNNORMALIZED 1
//======================================================================================
// 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
//======================================================================================
#ifdef WR_FEATURE_YUV
/* 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
#define YUV_FORMAT_NV12 0
#define YUV_FORMAT_P010 1
#define YUV_FORMAT_NV16 2
#define YUV_FORMAT_PLANAR 3
#define YUV_FORMAT_INTERLEAVED 4
//#define YUV_PRECISION mediump
#define YUV_PRECISION highp
#ifdef WR_VERTEX_SHADER
#ifdef WR_FEATURE_TEXTURE_RECT
#define TEX_SIZE_YUV(sampler) vec2(1.0)
#else
#define TEX_SIZE_YUV(sampler) vec2(TEX_SIZE(sampler).xy)
#endif
// `YuvRangedColorSpace`
#define YUV_COLOR_SPACE_REC601_NARROW 0
#define YUV_COLOR_SPACE_REC601_FULL 1
#define YUV_COLOR_SPACE_REC709_NARROW 2
#define YUV_COLOR_SPACE_REC709_FULL 3
#define YUV_COLOR_SPACE_REC2020_NARROW 4
#define YUV_COLOR_SPACE_REC2020_FULL 5
#define YUV_COLOR_SPACE_GBR_IDENTITY 6
// The constants added to the Y, U and V components are applied in the fragment shader.
// The matrix is stored in column-major.
const mat3 RgbFromYuv_Rec601 = mat3(
1.00000, 1.00000, 1.00000,
0.00000,-0.17207, 0.88600,
0.70100,-0.35707, 0.00000
);
const mat3 RgbFromYuv_Rec709 = mat3(
1.00000, 1.00000, 1.00000,
0.00000,-0.09366, 0.92780,
0.78740,-0.23406, 0.00000
);
const mat3 RgbFromYuv_Rec2020 = mat3(
1.00000, 1.00000, 1.00000,
0.00000,-0.08228, 0.94070,
0.73730,-0.28568, 0.00000
);
// The matrix is stored in column-major.
// Identity is stored as GBR
const mat3 RgbFromYuv_GbrIdentity = mat3(
0.0 , 1.0, 0.0,
0.0 , 0.0, 1.0,
1.0 , 0.0, 0.0
);
// -
struct YuvPrimitive {
int channel_bit_depth;
int color_space;
int yuv_format;
};
struct YuvColorSamplingInfo {
mat3 rgb_from_yuv;
vec4 packed_zero_one_vals;
};
struct YuvColorMatrixInfo {
vec3 ycbcr_bias;
mat3 rgb_from_debiased_ycbrc;
};
// -
vec4 yuv_channel_zero_one_identity(int bit_depth, float channel_max) {
float all_ones_normalized = float((1 << bit_depth) - 1) / channel_max;
return vec4(0.0, 0.0, all_ones_normalized, all_ones_normalized);
}
vec4 yuv_channel_zero_one_narrow_range(int bit_depth, float channel_max) {
// Note: 512/1023 != 128/255
ivec4 zero_one_ints = ivec4(16, 128, 235, 240) << (bit_depth - 8);
return vec4(zero_one_ints) / channel_max;
}
vec4 yuv_channel_zero_one_full_range(int bit_depth, float channel_max) {
vec4 narrow = yuv_channel_zero_one_narrow_range(bit_depth, channel_max);
vec4 identity = yuv_channel_zero_one_identity(bit_depth, channel_max);
return vec4(0.0, narrow.y, identity.z, identity.w);
}
YuvColorSamplingInfo get_yuv_color_info(YuvPrimitive prim) {
float channel_max = 255.0;
if (prim.channel_bit_depth > 8) {
if (prim.yuv_format == YUV_FORMAT_P010) {
// This is an MSB format.
channel_max = float((1 << prim.channel_bit_depth) - 1);
} else {
// For >8bpc, we get the low bits, not the high bits:
// 10bpc(1.0): 0b0000_0011_1111_1111
channel_max = 65535.0;
}
}
if (prim.color_space == YUV_COLOR_SPACE_REC601_NARROW) {
return YuvColorSamplingInfo(RgbFromYuv_Rec601,
yuv_channel_zero_one_narrow_range(prim.channel_bit_depth, channel_max));
} else if (prim.color_space == YUV_COLOR_SPACE_REC601_FULL) {
return YuvColorSamplingInfo(RgbFromYuv_Rec601,
yuv_channel_zero_one_full_range(prim.channel_bit_depth, channel_max));
} else if (prim.color_space == YUV_COLOR_SPACE_REC709_NARROW) {
return YuvColorSamplingInfo(RgbFromYuv_Rec709,
yuv_channel_zero_one_narrow_range(prim.channel_bit_depth, channel_max));
} else if (prim.color_space == YUV_COLOR_SPACE_REC709_FULL) {
return YuvColorSamplingInfo(RgbFromYuv_Rec709,
yuv_channel_zero_one_full_range(prim.channel_bit_depth, channel_max));
} else if (prim.color_space == YUV_COLOR_SPACE_REC2020_NARROW) {
return YuvColorSamplingInfo(RgbFromYuv_Rec2020,
yuv_channel_zero_one_narrow_range(prim.channel_bit_depth, channel_max));
} else if (prim.color_space == YUV_COLOR_SPACE_REC2020_FULL) {
return YuvColorSamplingInfo(RgbFromYuv_Rec2020,
yuv_channel_zero_one_full_range(prim.channel_bit_depth, channel_max));
} else {
// Identity
return YuvColorSamplingInfo(RgbFromYuv_GbrIdentity,
yuv_channel_zero_one_identity(prim.channel_bit_depth, channel_max));
}
}
YuvColorMatrixInfo get_rgb_from_ycbcr_info(YuvPrimitive prim) {
YuvColorSamplingInfo info = get_yuv_color_info(prim);
vec2 zero = info.packed_zero_one_vals.xy;
vec2 one = info.packed_zero_one_vals.zw;
// Such that yuv_value = (ycbcr_sample - zero) / (one - zero)
vec2 scale = 1.0 / (one - zero);
YuvColorMatrixInfo mat_info;
mat_info.ycbcr_bias = zero.xyy;
mat3 yuv_from_debiased_ycbcr = mat3(scale.x, 0.0, 0.0,
0.0, scale.y, 0.0,
0.0, 0.0, scale.y);
mat_info.rgb_from_debiased_ycbrc = info.rgb_from_yuv * yuv_from_debiased_ycbcr;
return mat_info;
}
void write_uv_rect(
vec2 uv0,
vec2 uv1,
vec2 f,
vec2 texture_size,
out vec2 uv,
out vec4 uv_bounds
) {
uv = mix(uv0, uv1, f);
uv_bounds = vec4(uv0 + vec2(0.5), uv1 - vec2(0.5));
#ifndef WR_FEATURE_TEXTURE_RECT
uv /= texture_size;
uv_bounds /= texture_size.xyxy;
#endif
}
#endif
#ifdef WR_FRAGMENT_SHADER
vec4 sample_yuv(
int format,
YUV_PRECISION vec3 ycbcr_bias,
YUV_PRECISION mat3 rgb_from_debiased_ycbrc,
vec2 in_uv_y,
vec2 in_uv_u,
vec2 in_uv_v,
vec4 uv_bounds_y,
vec4 uv_bounds_u,
vec4 uv_bounds_v
) {
YUV_PRECISION vec3 ycbcr_sample;
switch (format) {
case YUV_FORMAT_PLANAR:
{
// The yuv_planar format should have this third texture coordinate.
vec2 uv_y = clamp(in_uv_y, uv_bounds_y.xy, uv_bounds_y.zw);
vec2 uv_u = clamp(in_uv_u, uv_bounds_u.xy, uv_bounds_u.zw);
vec2 uv_v = clamp(in_uv_v, uv_bounds_v.xy, uv_bounds_v.zw);
ycbcr_sample.x = TEX_SAMPLE(sColor0, uv_y).r;
ycbcr_sample.y = TEX_SAMPLE(sColor1, uv_u).r;
ycbcr_sample.z = TEX_SAMPLE(sColor2, uv_v).r;
}
break;
case YUV_FORMAT_NV12:
case YUV_FORMAT_P010:
case YUV_FORMAT_NV16:
{
vec2 uv_y = clamp(in_uv_y, uv_bounds_y.xy, uv_bounds_y.zw);
vec2 uv_uv = clamp(in_uv_u, uv_bounds_u.xy, uv_bounds_u.zw);
ycbcr_sample.x = TEX_SAMPLE(sColor0, uv_y).r;
ycbcr_sample.yz = TEX_SAMPLE(sColor1, uv_uv).rg;
}
break;
case YUV_FORMAT_INTERLEAVED:
{
// "The Y, Cb and Cr color channels within the 422 data are mapped into
// the existing green, blue and red color channels."
vec2 uv_y = clamp(in_uv_y, uv_bounds_y.xy, uv_bounds_y.zw);
ycbcr_sample = TEX_SAMPLE(sColor0, uv_y).gbr;
}
break;
default:
ycbcr_sample = vec3(0.0);
break;
}
//if (true) return vec4(ycbcr_sample, 1.0);
// See the YuvColorMatrix definition for an explanation of where the constants come from.
YUV_PRECISION vec3 rgb = rgb_from_debiased_ycbrc * (ycbcr_sample - ycbcr_bias);
#if defined(WR_FEATURE_ALPHA_PASS) && defined(SWGL_CLIP_MASK)
// Avoid out-of-range RGB values that can mess with blending. These occur due to invalid
// YUV values outside the mappable space that never the less can be generated.
rgb = clamp(rgb, 0.0, 1.0);
#endif
return vec4(rgb, 1.0);
}
#endif
#endif
#ifdef WR_FEATURE_YUV
flat varying YUV_PRECISION vec3 vYcbcrBias;
flat varying YUV_PRECISION mat3 vRgbFromDebiasedYcbcr;
flat varying mediump ivec2 vYuvFormat;
#ifdef SWGL_DRAW_SPAN
flat varying mediump int vRescaleFactor;
#endif
varying highp vec2 vUV_y;
varying highp vec2 vUV_u;
varying highp vec2 vUV_v;
flat varying highp vec4 vUVBounds_y;
flat varying highp vec4 vUVBounds_u;
flat varying highp vec4 vUVBounds_v;
#else
varying highp vec2 vUv;
#ifndef WR_FEATURE_FAST_PATH
flat varying mediump vec4 vColor;
flat varying highp vec4 vUVBounds;
#endif
#ifdef WR_FEATURE_TEXTURE_EXTERNAL_ESSL1
uniform mediump vec2 uTextureSize;
#endif
#endif
#ifdef WR_VERTEX_SHADER
// CPU side data is in CompositeInstance (gpu_types.rs) and is
// converted to GPU data using desc::COMPOSITE (renderer.rs) by
// filling vaos.composite_vao with VertexArrayKind::Composite.
PER_INSTANCE attribute vec4 aDeviceRect;
PER_INSTANCE attribute vec4 aDeviceClipRect;
PER_INSTANCE attribute vec4 aColor;
PER_INSTANCE attribute vec4 aParams;
PER_INSTANCE attribute vec2 aFlip;
#ifdef WR_FEATURE_YUV
// YUV treats these as a UV clip rect (clamp)
PER_INSTANCE attribute vec4 aUvRect0;
PER_INSTANCE attribute vec4 aUvRect1;
PER_INSTANCE attribute vec4 aUvRect2;
#else
PER_INSTANCE attribute vec4 aUvRect0;
#endif
#ifdef WR_FEATURE_YUV
YuvPrimitive fetch_yuv_primitive() {
// From ExternalSurfaceDependency::Yuv:
int color_space = int(aParams.y);
int yuv_format = int(aParams.z);
int channel_bit_depth = int(aParams.w);
return YuvPrimitive(channel_bit_depth, color_space, yuv_format);
}
#endif
void main(void) {
// Flip device rect if required
vec4 device_rect = mix(aDeviceRect.xyzw, aDeviceRect.zwxy, aFlip.xyxy);
// Get world position
vec2 world_pos = mix(device_rect.xy, device_rect.zw, aPosition.xy);
// Clip the position to the world space clip rect
vec2 clipped_world_pos = clamp(world_pos, aDeviceClipRect.xy, aDeviceClipRect.zw);
// Derive the normalized UV from the clipped vertex position
vec2 uv = (clipped_world_pos - device_rect.xy) / (device_rect.zw - device_rect.xy);
#ifdef WR_FEATURE_YUV
YuvPrimitive prim = fetch_yuv_primitive();
#ifdef SWGL_DRAW_SPAN
// swgl_commitTextureLinearYUV needs to know the color space specifier and
// also needs to know how many bits of scaling are required to normalize
// HDR textures. Note that MSB HDR formats don't need renormalization.
vRescaleFactor = 0;
if (prim.channel_bit_depth > 8 && prim.yuv_format != YUV_FORMAT_P010) {
vRescaleFactor = 16 - prim.channel_bit_depth;
}
#endif
YuvColorMatrixInfo mat_info = get_rgb_from_ycbcr_info(prim);
vYcbcrBias = mat_info.ycbcr_bias;
vRgbFromDebiasedYcbcr = mat_info.rgb_from_debiased_ycbrc;
vYuvFormat.x = prim.yuv_format;
write_uv_rect(
aUvRect0.xy,
aUvRect0.zw,
uv,
TEX_SIZE_YUV(sColor0),
vUV_y,
vUVBounds_y
);
write_uv_rect(
aUvRect1.xy,
aUvRect1.zw,
uv,
TEX_SIZE_YUV(sColor1),
vUV_u,
vUVBounds_u
);
write_uv_rect(
aUvRect2.xy,
aUvRect2.zw,
uv,
TEX_SIZE_YUV(sColor2),
vUV_v,
vUVBounds_v
);
#else
uv = mix(aUvRect0.xy, aUvRect0.zw, uv);
// The uvs may be inverted, so use the min and max for the bounds
vec4 uvBounds = vec4(min(aUvRect0.xy, aUvRect0.zw), max(aUvRect0.xy, aUvRect0.zw));
if (int(aParams.y) == UV_TYPE_UNNORMALIZED) {
// using an atlas, so UVs are in pixels, and need to be
// normalized and clamped.
#if defined(WR_FEATURE_TEXTURE_RECT)
vec2 texture_size = vec2(1.0, 1.0);
#elif defined(WR_FEATURE_TEXTURE_EXTERNAL_ESSL1)
vec2 texture_size = uTextureSize;
#else
vec2 texture_size = vec2(TEX_SIZE(sColor0));
#endif
uvBounds += vec4(0.5, 0.5, -0.5, -0.5);
#ifndef WR_FEATURE_TEXTURE_RECT
uv /= texture_size;
uvBounds /= texture_size.xyxy;
#endif
}
vUv = uv;
#ifndef WR_FEATURE_FAST_PATH
vUVBounds = uvBounds;
// Pass through color
vColor = aColor;
#endif
#endif
gl_Position = uTransform * vec4(clipped_world_pos, 0.0, 1.0);
}
#endif
#ifdef WR_FRAGMENT_SHADER
void main(void) {
#ifdef WR_FEATURE_YUV
vec4 color = sample_yuv(
vYuvFormat.x,
vYcbcrBias,
vRgbFromDebiasedYcbcr,
vUV_y,
vUV_u,
vUV_v,
vUVBounds_y,
vUVBounds_u,
vUVBounds_v
);
#else
// The color is just the texture sample modulated by a supplied color.
// In the fast path we avoid clamping the UV coordinates and modulating by the color.
#ifdef WR_FEATURE_FAST_PATH
vec2 uv = vUv;
#else
vec2 uv = clamp(vUv, vUVBounds.xy, vUVBounds.zw);
#endif
vec4 texel = TEX_SAMPLE(sColor0, uv);
#ifdef WR_FEATURE_FAST_PATH
vec4 color = texel;
#else
vec4 color = vColor * texel;
#endif
#endif
write_output(color);
}
#ifdef SWGL_DRAW_SPAN
void swgl_drawSpanRGBA8() {
#ifdef WR_FEATURE_YUV
if (vYuvFormat.x == YUV_FORMAT_PLANAR) {
swgl_commitTextureLinearYUV(sColor0, vUV_y, vUVBounds_y,
sColor1, vUV_u, vUVBounds_u,
sColor2, vUV_v, vUVBounds_v,
vYcbcrBias,
vRgbFromDebiasedYcbcr,
vRescaleFactor);
} else if (vYuvFormat.x == YUV_FORMAT_NV12 || vYuvFormat.x == YUV_FORMAT_P010) {
swgl_commitTextureLinearYUV(sColor0, vUV_y, vUVBounds_y,
sColor1, vUV_u, vUVBounds_u,
vYcbcrBias,
vRgbFromDebiasedYcbcr,
vRescaleFactor);
} else if (vYuvFormat.x == YUV_FORMAT_INTERLEAVED) {
swgl_commitTextureLinearYUV(sColor0, vUV_y, vUVBounds_y,
vYcbcrBias,
vRgbFromDebiasedYcbcr,
vRescaleFactor);
}
#else
#ifdef WR_FEATURE_FAST_PATH
vec4 color = vec4(1.0);
#ifdef WR_FEATURE_TEXTURE_RECT
vec4 uvBounds = vec4(vec2(0.0), vec2(textureSize(sColor0)));
#else
vec4 uvBounds = vec4(0.0, 0.0, 1.0, 1.0);
#endif
#else
vec4 color = vColor;
vec4 uvBounds = vUVBounds;
#endif
if (color != vec4(1.0)) {
swgl_commitTextureColorRGBA8(sColor0, vUv, uvBounds, color);
} else {
swgl_commitTextureRGBA8(sColor0, vUv, uvBounds);
}
#endif
}
#endif
#endif