blob: 26dd4ee4eba13c1b6275d05cce6fe75655cb48f4 [file]
/*
* 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