| /* | |
| * Copyright 2025 Rive | |
| */ | |
| // Provides GLSL-specific #defines and declarations that enable our shaders to | |
| // be compiled as C++ and unit tested. | |
| #include "rive/math/simd.hpp" | |
| #include "rive/math/math_types.hpp" | |
| #include <array> | |
| #include <cmath> | |
| #include <cstring> | |
| using namespace rive; | |
| #define INLINE inline | |
| #define OUT(T) T& | |
| #ifndef RIVE_GLSL_CROSS_WANT_CONSTRUCTORS | |
| #define make_float4(x, y, z, w) \ | |
| float4 { x, y, z, w } | |
| #define make_float2(x, y) \ | |
| float2 { x, y } | |
| #endif | |
| using math::PI; | |
| using simd::abs; | |
| using simd::any; | |
| using simd::clamp; | |
| using simd::dot; | |
| using simd::max; | |
| using simd::min; | |
| using simd::mix; | |
| using simd::sqrt; | |
| using std::sqrt; | |
| using float3 = vec<3>; | |
| using float2x2 = std::array<float2, 2>; | |
| using half4 = float4; | |
| using half3 = float3; | |
| using half2 = float2; | |
| using half = float; | |
| using uint = uint32_t; | |
| using ushort = uint16_t; | |
| inline half4 make_half4(half x, half y, half z, half w) { return {x, y, z, w}; } | |
| inline half4 make_half4(half3 xyz, half w) { return {xyz.x, xyz.y, xyz.z, w}; } | |
| inline half4 make_half4(half x) { return {x, x, x, x}; } | |
| inline half3 make_half3(half x, half y, half z) { return {x, y, z}; } | |
| inline half3 make_half3(half x) { return {x, x, x}; } | |
| inline half2 make_half2(half x, half y) { return {x, y}; } | |
| inline half2 make_half2(half x) { return {x, x}; } | |
| inline half make_half(float x) { return x; } | |
| // Vector booleans are masks of integer type; see simd.hpp. | |
| using bool2 = decltype(float2() == float2()); | |
| // GLSL builtins with no C++ equivalent. | |
| inline float fract(float x) { return x - std::floor(x); } | |
| inline float sign(float x) { return x > 0.f ? 1.f : (x < 0.f ? -1.f : 0.f); } | |
| inline float uintBitsToFloat(uint u) | |
| { | |
| float f; | |
| memcpy(&f, &u, sizeof(f)); | |
| return f; | |
| } | |
| inline uint floatBitsToUint(float f) | |
| { | |
| uint u; | |
| memcpy(&u, &f, sizeof(u)); | |
| return u; | |
| } | |
| // Declared, not defined: shaders that merely mention it still compile, and one | |
| // that actually calls it gets a link error instead of a wrong answer. | |
| half2 unpackHalf2x16(uint); | |
| using half2x3 = std::array<half3, 2>; | |
| inline half2x3 make_half2x3(half3 c0, half3 c1) { return {c0, c1}; } | |
| inline half3 MUL(half2x3 m, half2 v) | |
| { | |
| half3 ret; | |
| for (int i = 0; i < 3; ++i) | |
| { | |
| ret[i] = m[0][i] * v[0] + m[1][i] * v[1]; | |
| } | |
| return ret; | |
| } | |
| using half4x4 = std::array<half4, 4>; | |
| inline half4x4 make_half4x4(half4 c0, half4 c1, half4 c2, half4 c3) | |
| { | |
| return {c0, c1, c2, c3}; | |
| } | |
| using half3x3 = std::array<half3, 3>; | |
| inline half3x3 make_half3x3(half3 c0, half3 c1, half3 c2) | |
| { | |
| return {c0, c1, c2}; | |
| } | |
| inline half3 MUL(half3x3 m, half3 v) | |
| { | |
| half3 ret; | |
| for (int i = 0; i < 3; ++i) | |
| { | |
| ret[i] = m[0][i] * v[0] + m[1][i] * v[1] + m[2][i] * v[2]; | |
| } | |
| return ret; | |
| } | |
| inline float clamp(float x, float lo, float hi) | |
| { | |
| return std::clamp(x, lo, hi); | |
| } | |
| inline float min(float x, float y) { return std::min(x, y); } | |
| inline float max(float x, float y) { return std::max(x, y); } | |
| template <typename T> T inversesqrt(T x) { return 1 / sqrt(x); } | |
| template <int N> float length(vec<N> x) { return std::sqrt(dot(x, x)); } | |
| template <int N> vec<N> normalize(vec<N> x) { return x / length(x); } | |
| template <int N> vec<N> sign(vec<N> x) | |
| { | |
| return simd::if_then_else( | |
| x < 0, | |
| vec<N>(-1), | |
| simd::if_then_else(x > 0, vec<N>(1), vec<N>(0))); | |
| } | |
| template <int N> vec<N> mix(vec<N> a, vec<N> b, ivec<N> t) | |
| { | |
| return simd::if_then_else(t, b, a); | |
| } | |
| template <typename T, int N> | |
| ivec<N> equal(simd::gvec<T, N> x, simd::gvec<T, N> y) | |
| { | |
| return x == y; | |
| } | |
| template <typename T, int N> | |
| ivec<N> notEqual(simd::gvec<T, N> x, simd::gvec<T, N> y) | |
| { | |
| return x != y; | |
| } | |
| template <typename T, int N> | |
| ivec<N> lessThanEqual(simd::gvec<T, N> x, simd::gvec<T, N> y) | |
| { | |
| return x <= y; | |
| } | |
| template <typename T, int N> | |
| ivec<N> lessThan(simd::gvec<T, N> x, simd::gvec<T, N> y) | |
| { | |
| return x < y; | |
| } | |
| template <typename T, int N> | |
| ivec<N> greaterThanEqual(simd::gvec<T, N> x, simd::gvec<T, N> y) | |
| { | |
| return x >= y; | |
| } | |
| template <typename T, int N> | |
| ivec<N> greaterThan(simd::gvec<T, N> x, simd::gvec<T, N> y) | |
| { | |
| return x > y; | |
| } | |
| // Scaffolding for shaders that use GLSL constructor syntax -- float2(x, x) -- | |
| // or read the flush uniforms. common.glsl needs both. Opt in by defining | |
| // RIVE_GLSL_CROSS_WANT_CONSTRUCTORS before including this file; it is off by | |
| // default because bezier_utils.glsl defines its own make_float2 macro, and | |
| // bezier_utils_test.cpp relies on plain C++ splat casts like float2(2 / 3.f). | |
| #ifdef RIVE_GLSL_CROSS_WANT_CONSTRUCTORS | |
| // Only the fields the shaders' vertex helpers read. | |
| struct GlslCrossUniforms | |
| { | |
| float renderTargetInverseViewportX; | |
| float renderTargetInverseViewportY; | |
| }; | |
| // These macros are function-like on purpose: they expand where a shader calls | |
| // a constructor and leave declarations -- "INLINE float2 make_float2(...)" -- | |
| // alone, since no parenthesis follows the type name there. | |
| #define float2(a, b) make_float2(a, b) | |
| #define float4(a, b, c, d) make_float4(a, b, c, d) | |
| // cast_uint4_to_half4() and friends go through vec4() under GLSL, which the | |
| // tests define so they get those casts rather than the C-style vector casts of | |
| // the other backends. (MSVC's gvec polyfill is a struct, so it can't | |
| // C-style-cast between vector types the way clang's native vectors can.) | |
| #define vec4(a) make_float4_from(a) | |
| #define bool2(a, b) make_bool2(a, b) | |
| #define float2x2(a, b) make_float2x2(a, b) | |
| // Invoke this inside the namespace that includes the shader. It can't live at | |
| // file scope: common.glsl declares its own single-argument splat overloads of | |
| // make_float2() and friends, and these arities have to join that same overload | |
| // set rather than be hidden by it. | |
| #define RIVE_GLSL_CROSS_CONSTRUCTORS \ | |
| [[maybe_unused]] static GlslCrossUniforms uniforms; \ | |
| inline float2 make_float2(float x, float y) { return float2{x, y}; } \ | |
| inline float4 make_float4(float x, float y, float z, float w) \ | |
| { \ | |
| return float4{x, y, z, w}; \ | |
| } \ | |
| inline bool2 make_bool2(bool a, bool b) \ | |
| { \ | |
| return bool2{a ? -1 : 0, b ? -1 : 0}; \ | |
| } \ | |
| inline float2x2 make_float2x2(float2 a, float2 b) { return float2x2{a, b}; } \ | |
| template <typename T> inline float4 make_float4_from(T v) \ | |
| { \ | |
| return float4{ \ | |
| static_cast<float>(v.x), \ | |
| static_cast<float>(v.y), \ | |
| static_cast<float>(v.z), \ | |
| static_cast<float>(v.w)}; \ | |
| } | |
| #endif // RIVE_GLSL_CROSS_WANT_CONSTRUCTORS |