Skip to content

Commit

Permalink
Defer PLS writes to GPU resources until flush
Browse files Browse the repository at this point in the history
Now that we have high-level draw objects, stash them on the render context and don't write them out until flush time. Doing it this way allows us to wait to allocate resources until we know exactly how big they need to be, which gets rid of intermediate flushes except the ones based on texture size limits, and greatly simplifies our allocation logic.

Diffs=
124a8f4e2 Defer PLS writes to GPU resources until flush (#6405)

Co-authored-by: Chris Dalton <[email protected]>
  • Loading branch information
csmartdalton and csmartdalton committed Jan 4, 2024
1 parent c213529 commit bb903d5
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 36 deletions.
2 changes: 1 addition & 1 deletion .rive_head
Original file line number Diff line number Diff line change
@@ -1 +1 @@
d52d14a1f3f45cda402246dec6b95814cc05abe3
124a8f4e23baa003af40515d04686e2960cc5809
28 changes: 21 additions & 7 deletions include/rive/math/simd.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,18 @@ namespace simd
// (The GLSL spec uses "gvec" to denote a vector of unspecified type.)
template <typename T, int N>
using gvec = T __attribute__((ext_vector_type(N))) __attribute__((aligned(sizeof(T) * N)));

// Vector booleans are masks of integer type, where true is -1 and false is 0. Vector booleans masks
// are generated using the builtin boolean operators: ==, !=, <=, >=, <, >
template <typename T> struct extract_element_type;
template <typename T, int N> struct extract_element_type<gvec<T, N>>
{
using type = T;
};
template <typename T> struct boolean_mask_type
{
using type = typename extract_element_type<decltype(gvec<T, 4>() == gvec<T, 4>())>::type;
};
} // namespace simd
} // namespace rive

Expand All @@ -65,12 +77,12 @@ namespace simd
{
////// Boolean logic //////
//
// Vector booleans are of type int32_t, where true is ~0 and false is 0. Vector booleans can be
// generated using the builtin boolean operators: ==, !=, <=, >=, <, >
// Vector booleans are masks of integer type, where true is -1 and false is 0. Vector booleans masks
// can be generated using the builtin boolean operators: ==, !=, <=, >=, <, >
//

// Returns true if all elements in x are equal to 0.
template <int N> RIVE_ALWAYS_INLINE bool any(gvec<int32_t, N> x)
template <typename T, int N> RIVE_ALWAYS_INLINE bool any(gvec<T, N> x)
{
#if __has_builtin(__builtin_reduce_or)
return __builtin_reduce_or(x);
Expand All @@ -89,7 +101,7 @@ template <int N> RIVE_ALWAYS_INLINE bool any(gvec<int32_t, N> x)
}

// Returns true if all elements in x are equal to ~0.
template <int N> RIVE_ALWAYS_INLINE bool all(gvec<int32_t, N> x)
template <typename T, int N> RIVE_ALWAYS_INLINE bool all(gvec<T, N> x)
{
#if __has_builtin(__builtin_reduce_and)
return __builtin_reduce_and(x);
Expand All @@ -105,13 +117,13 @@ template <int N> RIVE_ALWAYS_INLINE bool all(gvec<int32_t, N> x)
template <typename T,
int N,
typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
RIVE_ALWAYS_INLINE gvec<int32_t, N> isnan(gvec<T, N> x)
RIVE_ALWAYS_INLINE gvec<typename boolean_mask_type<T>::type, N> isnan(gvec<T, N> x)
{
return ~(x == x);
}

template <typename T, int N, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
constexpr gvec<int32_t, N> isnan(gvec<T, N>)
constexpr gvec<typename boolean_mask_type<T>::type, N> isnan(gvec<T, N>)
{
return {}; // Integer types are never NaN.
}
Expand All @@ -120,7 +132,9 @@ constexpr gvec<int32_t, N> isnan(gvec<T, N>)

// Elementwise ternary expression: "_if ? _then : _else" for each component.
template <typename T, int N>
RIVE_ALWAYS_INLINE gvec<T, N> if_then_else(gvec<int32_t, N> _if, gvec<T, N> _then, gvec<T, N> _else)
RIVE_ALWAYS_INLINE gvec<T, N> if_then_else(gvec<typename boolean_mask_type<T>::type, N> _if,
gvec<T, N> _then,
gvec<T, N> _else)
{
#if defined(__clang_major__) && __clang_major__ >= 13
// The '?:' operator supports a vector condition beginning in clang 13.
Expand Down
44 changes: 36 additions & 8 deletions include/rive/math/simd_gvec_polyfill.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,30 @@ static_assert(sizeof(gvec<float, 1>) == 4, "gvec<1> is expected to be tightly pa
static_assert(sizeof(gvec<float, 2>) == 8, "gvec<2> is expected to be tightly packed");
static_assert(sizeof(gvec<float, 4>) == 16, "gvec<4> is expected to be tightly packed");

// Vector booleans are masks of integer type, where true is -1 and false is 0. Vector booleans masks
// are generated using the builtin boolean operators: ==, !=, <=, >=, <, >
template <size_t> struct boolean_mask_type_by_size;
template <> struct boolean_mask_type_by_size<1>
{
using type = int8_t;
};
template <> struct boolean_mask_type_by_size<2>
{
using type = int16_t;
};
template <> struct boolean_mask_type_by_size<4>
{
using type = int32_t;
};
template <> struct boolean_mask_type_by_size<8>
{
using type = int64_t;
};
template <typename T> struct boolean_mask_type
{
using type = typename boolean_mask_type_by_size<sizeof(T)>::type;
};

#define DECL_UNARY_OP(_OP_) \
template <typename T, int N, Swizzle Z> gvec<T, N> operator _OP_(gvec<T, N, Z> x) \
{ \
Expand Down Expand Up @@ -218,23 +242,25 @@ DECL_ARITHMETIC_OP(>>);

#define DECL_BOOLEAN_OP(_OP_) \
template <typename T, int N, Swizzle Z0, Swizzle Z1> \
gvec<int32_t, N> operator _OP_(gvec<T, N, Z0> a, gvec<T, N, Z1> b) \
gvec<typename boolean_mask_type<T>::type, N> operator _OP_(gvec<T, N, Z0> a, gvec<T, N, Z1> b) \
{ \
gvec<int32_t, N> ret; \
gvec<typename boolean_mask_type<T>::type, N> ret; \
for (int i = 0; i < N; ++i) \
ret[i] = a[i] _OP_ b[i] ? ~0 : 0; \
return ret; \
} \
template <typename T, int N, Swizzle Z> gvec<int32_t, N> operator _OP_(gvec<T, N, Z> a, T b) \
template <typename T, int N, Swizzle Z> \
gvec<typename boolean_mask_type<T>::type, N> operator _OP_(gvec<T, N, Z> a, T b) \
{ \
gvec<int32_t, N> ret; \
gvec<typename boolean_mask_type<T>::type, N> ret; \
for (int i = 0; i < N; ++i) \
ret[i] = a[i] _OP_ b ? ~0 : 0; \
return ret; \
} \
template <typename T, int N, Swizzle Z> gvec<int32_t, N> operator _OP_(T a, gvec<T, N, Z> b) \
template <typename T, int N, Swizzle Z> \
gvec<typename boolean_mask_type<T>::type, N> operator _OP_(T a, gvec<T, N, Z> b) \
{ \
gvec<int32_t, N> ret; \
gvec<typename boolean_mask_type<T>::type, N> ret; \
for (int i = 0; i < N; ++i) \
ret[i] = a _OP_ b[i] ? ~0 : 0; \
return ret; \
Expand Down Expand Up @@ -264,7 +290,7 @@ DECL_BOOLEAN_OP(||)
return F((gvec<float, N>)x); \
}
#define ENABLE_SWIZZLE1B(F) \
template <int N, Swizzle Z0> bool F(gvec<int32_t, N, Z0> x) { return F((gvec<int32_t, N>)x); }
template <typename T, int N, Swizzle Z0> bool F(gvec<T, N, Z0> x) { return F((gvec<T, N>)x); }
#define ENABLE_SWIZZLEUT(F) \
template <typename T, typename U, int N, Swizzle Z0> gvec<U, N> F(gvec<T, N, Z0> x) \
{ \
Expand All @@ -290,7 +316,9 @@ DECL_BOOLEAN_OP(||)
}
#define ENABLE_SWIZZLE3IT(F) \
template <typename T, int N, Swizzle Z0, Swizzle Z1, Swizzle Z2> \
gvec<T, N> F(gvec<int32_t, N, Z0> a, gvec<T, N, Z1> b, gvec<T, N, Z2> c) \
gvec<T, N> F(gvec<typename boolean_mask_type<T>::type, N, Z0> a, \
gvec<T, N, Z1> b, \
gvec<T, N, Z2> c) \
{ \
return F((gvec<int32_t, N>)a, (gvec<T, N>)b, (gvec<T, N>)c); \
}
Expand Down
88 changes: 68 additions & 20 deletions test/simd_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,24 +186,28 @@ TEST_CASE("swizzles", "[simd]")
}

// Verify the simd float types are IEEE 754 compliant for infinity and NaN.
TEST_CASE("ieee-compliance", "[simd]")
template <typename T> void check_ieee_compliance()
{
float4 test = float4{1, -kInf, 1, 4} / float4{0, 2, kInf, 4};
CHECK_ALL((test == float4{kInf, -kInf, 0, 1}));
using vec4 = simd::gvec<T, 4>;
using vec2 = simd::gvec<T, 2>;
constexpr T kTInf = std::numeric_limits<T>::infinity();

vec4 test = vec4{1, -kTInf, 1, 4} / vec4{0, 2, kTInf, 4};
CHECK_ALL((test == vec4{kTInf, -kTInf, 0, 1}));

// Inf * Inf == Inf
test = float4{kInf, -kInf, kInf, -kInf} * float4{kInf, kInf, -kInf, -kInf};
CHECK_ALL((test == float4{kInf, -kInf, -kInf, kInf}));
test = vec4{kTInf, -kTInf, kTInf, -kTInf} * vec4{kTInf, kTInf, -kTInf, -kTInf};
CHECK_ALL((test == vec4{kTInf, -kTInf, -kTInf, kTInf}));

// Inf/0 == Inf, 0/Inf == 0
test = float4{kInf, -kInf, 0, 0} / float4{0, 0, kInf, -kInf};
CHECK_ALL((test == float4{kInf, -kInf, 0, 0}));
test = vec4{kTInf, -kTInf, 0, 0} / vec4{0, 0, kTInf, -kTInf};
CHECK_ALL((test == vec4{kTInf, -kTInf, 0, 0}));

// Inf/Inf, 0/0, 0 * Inf, Inf - Inf == NaN
test = {kInf, 0, 0, kInf};
test.xy /= float2{kInf, 0};
test.z *= kInf;
test.w -= kInf;
test = {kTInf, 0, 0, kTInf};
test.xy /= vec2{kTInf, 0};
test.z *= kTInf;
test.w -= kTInf;
for (int i = 0; i < 4; ++i)
{
CHECK(std::isnan(test[i]));
Expand All @@ -217,25 +221,51 @@ TEST_CASE("ieee-compliance", "[simd]")
CHECK(!simd::any(test > test));

// Inf + Inf == Inf, Inf + -Inf == NaN
test = float4{kInf, -kInf, kInf, -kInf} + float4{kInf, -kInf, -kInf, kInf};
CHECK_ALL((test.xy == float2{kInf, -kInf}));
test = vec4{kTInf, -kTInf, kTInf, -kTInf} + vec4{kTInf, -kTInf, -kTInf, kTInf};
CHECK_ALL((test.xy == vec2{kTInf, -kTInf}));
CHECK(!simd::any(test.zw == test.zw)); // NaN
}

TEST_CASE("ieee-compliance", "[simd]")
{
check_ieee_compliance<float>();
check_ieee_compliance<double>();
}

// Check simd::if_then_else.
TEST_CASE("ternary-operator", "[simd]")
template <typename T> void check_if_then_else()
{
using vec4 = simd::gvec<T, 4>;
using vec2 = simd::gvec<T, 2>;

// Vector condition.
float4 f4 = simd::if_then_else(int4{1, 2, 3, 4} < int4{4, 3, 2, 1}, float4(-1), float4(1));
CHECK_ALL((f4 == float4{-1, -1, 1, 1}));
vec4 f4 = simd::if_then_else(vec4{1, 2, 3, 4} < vec4{4, 3, 2, 1}, vec4(1), vec4(2));
CHECK_ALL((f4 == vec4{1, 1, 2, 2}));

// In vector, -1 is true, 0 is false.
uint2 u2 = simd::if_then_else(int2{0, -1}, uint2{1, 2}, uint2{3, 4});
CHECK_ALL((u2 == uint2{3, 2}));
vec2 u2 = simd::if_then_else(simd::gvec<typename simd::boolean_mask_type<T>::type, 2>{0, -1},
vec2{1, 2},
vec2{3, 4});
CHECK_ALL((u2 == vec2{3, 2}));

// Scalar condition.
f4 = u2.x == u2.y ? float4{1, 2, 3, 4} : float4{5, 6, 7, 8};
CHECK_ALL((f4 == float4{5, 6, 7, 8}));
f4 = u2.x == u2.y ? vec4{1, 2, 3, 4} : vec4{5, 6, 7, 8};
CHECK_ALL((f4 == vec4{5, 6, 7, 8}));
}

TEST_CASE("ternary-operator", "[simd]")
{
check_if_then_else<int8_t>();
check_if_then_else<uint8_t>();
check_if_then_else<int16_t>();
check_if_then_else<uint16_t>();
check_if_then_else<float>();
check_if_then_else<int32_t>();
check_if_then_else<uint32_t>();
check_if_then_else<size_t>();
check_if_then_else<double>();
check_if_then_else<int64_t>();
check_if_then_else<uint64_t>();
}

// Check simd::min/max compliance.
Expand Down Expand Up @@ -270,10 +300,28 @@ TEST_CASE("min-max", "[simd]")
CHECK(std::isnan(std::min<float>(kNaN, 1)));
CHECK(simd::max<float, 1>(kNaN, 1).x == 1);
CHECK(std::isnan(std::max<float>(kNaN, 1)));
CHECK(simd::min<double, 1>(kNaN, 1).x == 1);
CHECK(std::isnan(std::min<double>(kNaN, 1)));
CHECK(simd::max<double, 1>(kNaN, 1).x == 1);
CHECK(std::isnan(std::max<double>(kNaN, 1)));

// simd::min/max is equivalent std::min/max when the second argument is NaN.
CHECK(simd::min<float, 1>(1, kNaN).x == std::min<float>(1, kNaN));
CHECK(simd::max<float, 1>(1, kNaN).x == std::max<float>(1, kNaN));
CHECK(simd::min<double, 1>(1, kNaN).x == std::min<double>(1, kNaN));
CHECK(simd::max<double, 1>(1, kNaN).x == std::max<double>(1, kNaN));

// check non-32-bit types.
CHECK_ALL((simd::max(simd::gvec<double, 2>{3, 4}, simd::gvec<double, 2>{4, 3}) ==
simd::gvec<double, 2>{4, 4}));
CHECK_ALL((simd::min(simd::gvec<uint64_t, 2>{3, 4}, simd::gvec<uint64_t, 2>{4, 3}) ==
simd::gvec<uint64_t, 2>{3, 3}));
CHECK_ALL((simd::max(simd::gvec<size_t, 2>{3, 4}, simd::gvec<size_t, 2>{4, 3}) ==
simd::gvec<size_t, 2>{4, 4}));
CHECK_ALL(
(simd::max(simd::gvec<uint8_t, 16>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
simd::gvec<uint8_t, 16>{15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}) ==
simd::gvec<uint8_t, 16>{15, 14, 13, 12, 11, 10, 9, 8, 8, 9, 10, 11, 12, 13, 14, 15}));
}

// Check simd::clamp.
Expand Down

0 comments on commit bb903d5

Please sign in to comment.