blob: 9c81ca5b52ba1af65ae39fced4c9d028f77711ce [file] [log] [blame] [edit]
/*
* Copyright 2022 Rive
*/
// Common definitions and functions shared by multiple shaders.
#define PI 3.14159265359
#define _2PI 6.28318530718
#define PI_OVER_2 1.57079632679
#define ONE_OVER_SQRT_2 0.70710678118 // 1/sqrt(2)
#ifndef @RENDER_MODE_MSAA
#define AA_RADIUS float(.5)
#else
#define AA_RADIUS float(.0)
#endif
// Defined as a macro because 'uniforms' isn't always available at global scope.
#define RENDER_TARGET_COORD_TO_CLIP_COORD(COORD) \
pixel_coord_to_clip_coord(COORD, \
uniforms.renderTargetInverseViewportX, \
uniforms.renderTargetInverseViewportY)
#ifdef @TESS_TEXTURE_FLOATING_POINT
#define TEXTURE_TESSDATA4(SET, IDX, NAME) TEXTURE_RGBA32F(SET, IDX, NAME)
#define TESSDATA4 float4
#define FLOAT_AS_TESSDATA(X) X
#define TESSDATA_AS_FLOAT(X) X
#define UINT_AS_TESSDATA(X) uintBitsToFloat(X)
#define TESSDATA_AS_UINT(X) floatBitsToUint(X)
#else
#define TEXTURE_TESSDATA4(SET, IDX, NAME) TEXTURE_RGBA32UI(SET, IDX, NAME)
#define TESSDATA4 uint4
#define FLOAT_AS_TESSDATA(X) floatBitsToUint(X)
#define TESSDATA_AS_FLOAT(X) uintBitsToFloat(X)
#define UINT_AS_TESSDATA(X) X
#define TESSDATA_AS_UINT(X) X
#endif
// Gathers a 4xN matrix of texels, in the same order as the textureGather() API.
// clang-format off
#define TEXTURE_GATHER_MATRIX(NAME, COORD, COMPONENTS) \
TEXEL_FETCH(NAME, int2(COORD) + int2(-1, 0))COMPONENTS, \
TEXEL_FETCH(NAME, int2(COORD) + int2(0, 0))COMPONENTS, \
TEXEL_FETCH(NAME, int2(COORD) + int2(0, -1))COMPONENTS, \
TEXEL_FETCH(NAME, int2(COORD) + int2(-1, -1))COMPONENTS
// clang-format on
// This is a macro because we can't (at least for now) forward texture refs to a
// function in a way that works in all the languages we support.
// This is a macro because we can't (at least for now) forward texture refs to a
// function in a way that works in all the languages we support.
#define FEATHER(X) \
TEXTURE_SAMPLE_LOD_1D_ARRAY(@featherTexture, \
featherSampler, \
X, \
FEATHER_FUNCTION_ARRAY_INDEX, \
float(FEATHER_FUNCTION_ARRAY_INDEX), \
.0) \
.r
#define INVERSE_FEATHER(X) \
TEXTURE_SAMPLE_LOD_1D_ARRAY(@featherTexture, \
featherSampler, \
X, \
FEATHER_INVERSE_FUNCTION_ARRAY_INDEX, \
float(FEATHER_INVERSE_FUNCTION_ARRAY_INDEX), \
.0) \
.r
#ifdef GLSL
// GLSL has different semantics around precision. Normalize type conversions
// across languages with "cast_*_to_*()" methods.
INLINE half cast_float_to_half(float x) { return x; }
INLINE half cast_uint_to_half(uint x) { return float(x); }
INLINE half cast_ushort_to_half(ushort x) { return float(x); }
INLINE half cast_int_to_half(int x) { return float(x); }
INLINE half4 cast_float4_to_half4(float4 xyzw) { return xyzw; }
INLINE half2 cast_float2_to_half2(float2 xy) { return xy; }
INLINE half4 cast_uint4_to_half4(uint4 xyzw) { return vec4(xyzw); }
INLINE ushort cast_half_to_ushort(half x) { return uint(x); }
INLINE ushort cast_uint_to_ushort(uint x) { return x; }
#else
INLINE half cast_float_to_half(float x) { return (half)x; }
INLINE half cast_uint_to_half(uint x) { return (half)x; }
INLINE half cast_ushort_to_half(ushort x) { return (half)x; }
INLINE half cast_int_to_half(int x) { return (half)x; }
INLINE half4 cast_float4_to_half4(float4 xyzw) { return (half4)xyzw; }
INLINE half2 cast_float2_to_half2(float2 xy) { return (half2)xy; }
INLINE half4 cast_uint4_to_half4(uint4 xyzw) { return (half4)xyzw; }
INLINE ushort cast_half_to_ushort(half x) { return (ushort)x; }
INLINE ushort cast_uint_to_ushort(uint x) { return (ushort)x; }
#endif
INLINE half make_half(half x) { return x; }
INLINE half2 make_half2(half2 xy) { return xy; }
INLINE half2 make_half2(half x, half y)
{
half2 ret;
ret.x = x, ret.y = y;
return ret;
}
INLINE half2 make_half2(half x)
{
half2 ret;
ret.x = x, ret.y = x;
return ret;
}
INLINE float2 make_float2(float x) { return float2(x, x); }
INLINE half3 make_half3(half x, half y, half z)
{
half3 ret;
ret.x = x, ret.y = y, ret.z = z;
return ret;
}
INLINE half3 make_half3(half x)
{
half3 ret;
ret.x = x, ret.y = x, ret.z = x;
return ret;
}
INLINE half4 make_half4(half x, half y, half z, half w)
{
half4 ret;
ret.x = x, ret.y = y, ret.z = z, ret.w = w;
return ret;
}
INLINE half4 make_half4(half3 xyz, half w)
{
half4 ret;
ret.xyz = xyz;
ret.w = w;
return ret;
}
INLINE half4 make_half4(half x)
{
half4 ret;
ret.x = x, ret.y = x, ret.z = x, ret.w = x;
return ret;
}
INLINE half4 make_half4(half4 x) { return x; }
INLINE bool2 make_bool2(bool b) { return bool2(b, b); }
INLINE half3x3 make_half3x3(half3 a, half3 b, half3 c)
{
half3x3 ret;
ret[0] = a;
ret[1] = b;
ret[2] = c;
return ret;
}
INLINE half2x3 make_half2x3(half3 a, half3 b)
{
half2x3 ret;
ret[0] = a;
ret[1] = b;
return ret;
}
INLINE half4x4 make_half4x4(half4 a, half4 b, half4 c, half4 d)
{
half4x4 ret;
ret[0] = a;
ret[1] = b;
ret[2] = c;
ret[3] = d;
return ret;
}
INLINE float2x2 make_float2x2(float4 x) { return float2x2(x.xy, x.zw); }
INLINE uint make_uint(ushort x) { return x; }
INLINE float2 unchecked_mix(float2 a, float2 b, float t)
{
return (b - a) * t + a;
}
INLINE half id_bits_to_f16(uint idBits, uint pathIDGranularity)
{
return idBits == 0u
? .0
: unpackHalf2x16((idBits + MAX_DENORM_F16) * pathIDGranularity)
.r;
}
INLINE float atan2(float2 v)
{
v = normalize(v);
float theta = acos(clamp(v.x, -1., 1.));
return v.y >= .0 ? theta : -theta;
}
INLINE half4 premultiply(half4 color)
{
return make_half4(color.rgb * color.a, color.a);
}
INLINE half3 unmultiply_rgb(half4 premul)
{
// We *could* return preciesly 1 when premul.rgb == premul.a, but we can
// also be approximate here. The blend modes that depend on this exact level
// of precision (colordodge and colorburn) account for it with dstPremul.
return premul.rgb * (premul.a != .0 ? 1. / premul.a : .0);
}
INLINE half min_value(half4 min4)
{
half2 min2 = min(min4.xy, min4.zw);
half min1 = min(min2.x, min2.y);
return min1;
}
INLINE float manhattan_width(float2 x) { return abs(x.x) + abs(x.y); }
#ifndef $UNIFORM_DEFINITIONS_AUTO_GENERATED
UNIFORM_BLOCK_BEGIN(FLUSH_UNIFORM_BUFFER_IDX, @FlushUniforms)
float gradInverseViewportY;
float tessInverseViewportY;
float renderTargetInverseViewportX;
float renderTargetInverseViewportY;
uint renderTargetWidth;
uint renderTargetHeight;
uint colorClearValue; // Only used if clears are implemented as draws.
uint coverageClearValue; // Only used if clears are implemented as draws.
int4 renderTargetUpdateBounds; // drawBounds, or renderTargetBounds if there is
// a clear. (LTRB.)
float2 atlasTextureInverseSize; // 1 / [atlasWidth, atlasHeight]
float2 atlasContentInverseViewport; // 2 / atlasContentBounds
uint coverageBufferPrefix;
uint pathIDGranularity; // Spacing between adjacent path IDs (1 if IEEE
// compliant).
float vertexDiscardValue;
float mipMapLODBias;
// Debugging.
uint wireframeEnabled;
UNIFORM_BLOCK_END(uniforms)
#endif
#ifdef @VERTEX
INLINE float4 pixel_coord_to_clip_coord(float2 pixelCoord,
float inverseViewportX,
float inverseViewportY)
{
return float4(pixelCoord.x * inverseViewportX - 1.,
pixelCoord.y * inverseViewportY - sign(inverseViewportY),
0.,
1.);
}
#ifndef @RENDER_MODE_MSAA
// Calculates the Manhattan distance in pixels from the given pixelPosition, to
// the point at each edge of the clipRect where coverage = 0.
//
// clipRectInverseMatrix transforms from pixel coordinates to a space where the
// clipRect is the normalized rectangle: [-1, -1, 1, 1].
INLINE float4 find_clip_rect_coverage_distances(float2x2 clipRectInverseMatrix,
float2 clipRectInverseTranslate,
float2 pixelPosition)
{
float2 clipRectAAWidth =
abs(clipRectInverseMatrix[0]) + abs(clipRectInverseMatrix[1]);
if (clipRectAAWidth.x != .0 && clipRectAAWidth.y != .0)
{
float2 r = 1. / clipRectAAWidth;
float2 clipRectCoord = MUL(clipRectInverseMatrix, pixelPosition) +
clipRectInverseTranslate;
// When the center of a pixel falls exactly on an edge, coverage should
// be .5.
const float coverageWhenDistanceIsZero = .5;
return float4(clipRectCoord, -clipRectCoord) * r.xyxy + r.xyxy +
coverageWhenDistanceIsZero;
}
else
{
// The caller gave us a singular clipRectInverseMatrix. This is a
// special case where we are expected to use tx and ty as uniform
// coverage.
return clipRectInverseTranslate.xyxy;
}
}
#else // !@RENDER_MODE_MSAA => @RENDER_MODE_MSAA
INLINE float normalize_z_index(uint zIndex)
{
return 1. - float(zIndex) * (2. / 32768.);
}
#ifdef @ENABLE_CLIP_RECT
INLINE void set_clip_rect_plane_distances(float2x2 clipRectInverseMatrix,
float2 clipRectInverseTranslate,
float2 pixelPosition)
{
if (clipRectInverseMatrix != float2x2(0))
{
float2 clipRectCoord = MUL(clipRectInverseMatrix, pixelPosition) +
clipRectInverseTranslate.xy;
gl_ClipDistance[0] = clipRectCoord.x + 1.;
gl_ClipDistance[1] = clipRectCoord.y + 1.;
gl_ClipDistance[2] = 1. - clipRectCoord.x;
gl_ClipDistance[3] = 1. - clipRectCoord.y;
}
else
{
// "clipRectInverseMatrix == 0" is a special case:
// "clipRectInverseTranslate.x == 1" => all in.
// "clipRectInverseTranslate.x == 0" => all out.
gl_ClipDistance[0] = gl_ClipDistance[1] = gl_ClipDistance[2] =
gl_ClipDistance[3] = clipRectInverseTranslate.x - .5;
}
}
#endif // ENABLE_CLIP_RECT
#endif // @RENDER_MODE_MSAA
#endif // VERTEX
#ifdef @FRAGMENT
#ifdef @NEEDS_GAMMA_CORRECTION
half gamma_to_linear(half color)
{
return (color <= 0.04045) ? color / 12.92
: pow(abs((color + 0.055) / 1.055), 2.4);
}
half3 gamma_to_linear(half3 color)
{
return make_half3(gamma_to_linear(color.r),
gamma_to_linear(color.g),
gamma_to_linear(color.b));
}
half4 gamma_to_linear(half4 color)
{
return make_half4(gamma_to_linear(color.rgb), color.a);
}
#endif // NEEDS_GAMMA_CORRECTION
#endif // FRAGMENT
#ifdef @DRAW_IMAGE
#ifndef $UNIFORM_DEFINITIONS_AUTO_GENERATED
UNIFORM_BLOCK_BEGIN(IMAGE_DRAW_UNIFORM_BUFFER_IDX, @ImageDrawUniforms)
float4 viewMatrix;
float2 translate;
float opacity;
float padding;
// clipRectInverseMatrix transforms from pixel coordinates to a space where the
// clipRect is the normalized rectangle: [-1, -1, 1, 1].
float4 clipRectInverseMatrix;
float2 clipRectInverseTranslate;
uint clipID;
uint blendMode;
uint zIndex;
UNIFORM_BLOCK_END(imageDrawUniforms)
#endif
#endif