Math types
This commit is contained in:
parent
26f4deca12
commit
f6f4ee884a
|
@ -1,182 +1,54 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <Orange/Core/Types.h>
|
||||||
#include <Orange/Core/Traits.h>
|
#include <Orange/Core/Traits.h>
|
||||||
#include <Orange/Core/AlignedStorage.h>
|
|
||||||
#include <Orange/Core/Span.h>
|
|
||||||
|
|
||||||
namespace orange
|
namespace orange
|
||||||
{
|
{
|
||||||
template <typename T, size_t N>
|
template <typename T, size_t Size>
|
||||||
class SmallArray
|
struct Array
|
||||||
{
|
{
|
||||||
public:
|
constexpr Array()
|
||||||
|
: m_data{ } {}
|
||||||
|
|
||||||
SmallArray() {}
|
constexpr Array(const T components[Size])
|
||||||
SmallArray(size_t size) { Resize(size); }
|
|
||||||
SmallArray(Span<T> span)
|
|
||||||
{
|
{
|
||||||
Reserve(span.size);
|
Copy(&components[0], &components[Size], m_data.begin());
|
||||||
for (const auto& val : span)
|
|
||||||
PushBack(val);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SmallArray (const SmallArray&) = delete;
|
template <typename... Args>
|
||||||
SmallArray& operator = (const SmallArray&) = delete;
|
constexpr Array(const Args&... args)
|
||||||
|
: m_data {{ args... }}
|
||||||
~SmallArray()
|
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < m_size; i++)
|
static_assert(sizeof...(Args) == Size);
|
||||||
Ptr(i)->~T();
|
|
||||||
|
|
||||||
if (m_capacity > N)
|
|
||||||
delete[] u.m_ptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename J>
|
constexpr Array(const Array& other) = default;
|
||||||
bool operator == (const Span<J>& x)
|
|
||||||
|
constexpr T& operator[](size_t index) { return m_data[index]; }
|
||||||
|
constexpr const T& operator[](size_t index) const { return m_data[index]; }
|
||||||
|
|
||||||
|
|
||||||
|
constexpr const T* begin() const { return &m_data[0]; }
|
||||||
|
constexpr T* begin() { return &m_data[0]; }
|
||||||
|
constexpr const T* end() const { return &m_data[Size]; }
|
||||||
|
constexpr T* end() { return &m_data[Size]; }
|
||||||
|
|
||||||
|
|
||||||
|
constexpr bool operator==(const Array& other) const
|
||||||
{
|
{
|
||||||
return Span<T>(*this) == x;
|
return Equal(begin(), end(), other.begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator == (const char *x)
|
constexpr bool operator!=(const Array& other) const
|
||||||
{
|
{
|
||||||
return Span<T>(*this) == StringView(x);
|
return !operator==(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reserve(size_t n)
|
|
||||||
{
|
|
||||||
n = PickCapacity(n);
|
|
||||||
|
|
||||||
if (n <= m_capacity)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Storage* data = new Storage[n];
|
|
||||||
|
|
||||||
for (size_t i = 0; i < m_size; i++)
|
|
||||||
{
|
|
||||||
new (&data[i]) T(Move(*Ptr(i)));
|
|
||||||
Ptr(i)->~T();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_capacity > N)
|
|
||||||
delete[] u.m_ptr;
|
|
||||||
|
|
||||||
m_capacity = n;
|
|
||||||
u.m_ptr = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Size() const { return m_size; }
|
|
||||||
|
|
||||||
const T* Data() const { return Ptr(0); }
|
|
||||||
T* Data() { return Ptr(0); }
|
|
||||||
|
|
||||||
void Resize(size_t n)
|
|
||||||
{
|
|
||||||
Reserve(n);
|
|
||||||
|
|
||||||
for (size_t i = n; i < m_size; i++)
|
|
||||||
Ptr(i)->~T();
|
|
||||||
|
|
||||||
for (size_t i = m_size; i < n; i++)
|
|
||||||
new (Ptr(i)) T();
|
|
||||||
|
|
||||||
m_size = n;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PushBack(const T& object)
|
|
||||||
{
|
|
||||||
Reserve(m_size + 1);
|
|
||||||
new (Ptr(m_size++)) T(object);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PushBack(T&& object)
|
|
||||||
{
|
|
||||||
Reserve(m_size + 1);
|
|
||||||
new (Ptr(m_size++)) T(Move(object));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
void EmplaceBack(Args... args)
|
|
||||||
{
|
|
||||||
Reserve(m_size + 1);
|
|
||||||
new (Ptr(m_size++)) T(Forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Erase(size_t idx)
|
|
||||||
{
|
|
||||||
Ptr(idx)->~T();
|
|
||||||
|
|
||||||
for (size_t i = idx; i < m_size - 1; i++)
|
|
||||||
{
|
|
||||||
new (Ptr(i)) T(Move(*Ptr(i + 1)));
|
|
||||||
Ptr(i + 1)->~T();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PopBack()
|
|
||||||
{
|
|
||||||
Ptr(--m_size)->~T();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Empty() const
|
|
||||||
{
|
|
||||||
return Size() == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
T& operator [] (size_t idx) { return *Ptr(idx); }
|
|
||||||
const T& operator [] (size_t idx) const { return *Ptr(idx); }
|
|
||||||
|
|
||||||
T* begin() { return Ptr(0); }
|
|
||||||
const T* begin() const { return Ptr(0); }
|
|
||||||
|
|
||||||
T* end() { return Ptr(m_size); }
|
|
||||||
const T* end() const { return Ptr(m_size); }
|
|
||||||
|
|
||||||
T& front() { return *Ptr(0); }
|
|
||||||
const T& front() const { return *Ptr(0); }
|
|
||||||
|
|
||||||
T& back() { return *Ptr(m_size - 1); }
|
|
||||||
const T& back() const { return *Ptr(m_size - 1); }
|
|
||||||
|
|
||||||
operator Span<T>() { return Span<T>(*this); }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using Storage = AlignedStorage<sizeof(T), alignof(T)>;
|
|
||||||
|
|
||||||
size_t m_capacity = N;
|
T m_data[Size];
|
||||||
size_t m_size = 0;
|
|
||||||
|
|
||||||
union
|
|
||||||
{
|
|
||||||
Storage* m_ptr;
|
|
||||||
Storage m_data[sizeof(T) * N];
|
|
||||||
} u;
|
|
||||||
|
|
||||||
size_t PickCapacity(size_t n) {
|
|
||||||
size_t capacity = m_capacity;
|
|
||||||
|
|
||||||
while (capacity < n)
|
|
||||||
capacity = (capacity * 2) + 2;
|
|
||||||
|
|
||||||
return capacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
T* Ptr(size_t idx) {
|
|
||||||
return m_capacity == N
|
|
||||||
? reinterpret_cast<T*>(&u.m_data[idx])
|
|
||||||
: reinterpret_cast<T*>(&u.m_ptr[idx]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const T* Ptr(size_t idx) const
|
|
||||||
{
|
|
||||||
return m_capacity == N
|
|
||||||
? reinterpret_cast<const T*>(&u.m_data[idx])
|
|
||||||
: reinterpret_cast<const T*>(&u.m_ptr[idx]);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
using Array = SmallArray<T, sizeof(T) / sizeof(void*)>;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Orange/Core/Array.h"
|
#include "Orange/Core/Vector.h"
|
||||||
#include <Orange/Core/Result.h>
|
#include <Orange/Core/Result.h>
|
||||||
#include <Orange/Core/Span.h>
|
#include <Orange/Core/Span.h>
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ namespace orange::stream
|
||||||
return Consume(first, end, " \t");
|
return Consume(first, end, " \t");
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename OutArray = SmallArray<char, 1>>
|
template <typename OutArray = SmallVector<char, 1>>
|
||||||
size_t ReadOrAdvance(const char*& first, const char* end, StringView delims, size_t advancement = 0, OutArray* array = nullptr)
|
size_t ReadOrAdvance(const char*& first, const char* end, StringView delims, size_t advancement = 0, OutArray* array = nullptr)
|
||||||
{
|
{
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
|
@ -88,7 +88,7 @@ namespace orange::stream
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename OutArray = SmallArray<char, 1>>
|
template <typename OutArray = SmallVector<char, 1>>
|
||||||
size_t ReadString(const char*& first, const char* end, StringView delims, OutArray& array)
|
size_t ReadString(const char*& first, const char* end, StringView delims, OutArray& array)
|
||||||
{
|
{
|
||||||
return ReadOrAdvance(first, end, delims, 0, &array);
|
return ReadOrAdvance(first, end, delims, 0, &array);
|
||||||
|
|
|
@ -60,4 +60,120 @@ namespace orange
|
||||||
{
|
{
|
||||||
return Min( Max( val, minVal ), maxVal );
|
return Min( Max( val, minVal ), maxVal );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Transformations
|
||||||
|
|
||||||
|
template <typename InputIt, typename OutputIt, typename UnaryOperation>
|
||||||
|
OutputIt Transform(InputIt inFirst, InputIt inLast,
|
||||||
|
OutputIt outFirst, UnaryOperation unaryOp)
|
||||||
|
{
|
||||||
|
while (inFirst != inLast)
|
||||||
|
*outFirst++ = unaryOp(*inFirst++);
|
||||||
|
return outFirst;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename InputIt1, typename InputIt2, typename OutputIt, typename BinaryOperation>
|
||||||
|
OutputIt Transform(InputIt1 first1, InputIt1 last1, InputIt2 first2,
|
||||||
|
OutputIt outFirst, BinaryOperation binOp)
|
||||||
|
{
|
||||||
|
while (first1 != last1)
|
||||||
|
*outFirst++ = binOp(*first1++, *first2++);
|
||||||
|
return outFirst;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename InputIt, typename UnaryOperation>
|
||||||
|
T TransformResult(InputIt first, InputIt last, UnaryOperation op)
|
||||||
|
{
|
||||||
|
T result;
|
||||||
|
Transform(first, last, result.begin(), op);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename InputIt1, typename InputIt2, typename BinaryOperation>
|
||||||
|
T TransformResult(InputIt1 first1, InputIt1 last1, InputIt2 first2, BinaryOperation op)
|
||||||
|
{
|
||||||
|
T result;
|
||||||
|
Transform(first1, last1, first2, result.begin(), op);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InputIt, typename T>
|
||||||
|
constexpr T Accumulate(InputIt first, InputIt last, T init)
|
||||||
|
{
|
||||||
|
for (; first != last; ++first)
|
||||||
|
init = Move(init) + *first;
|
||||||
|
return init;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InputIt, typename T, typename BinaryOperation>
|
||||||
|
constexpr T Accumulate(InputIt first, InputIt last, T init, BinaryOperation op)
|
||||||
|
{
|
||||||
|
for (; first != last; ++first)
|
||||||
|
init = op(Move(init), *first);
|
||||||
|
return init;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InputIt1, typename InputIt2>
|
||||||
|
bool Equal(InputIt1 first1, InputIt1 last1, InputIt2 first2)
|
||||||
|
{
|
||||||
|
for (; first1 != last1; ++first1, ++first2)
|
||||||
|
{
|
||||||
|
if (!(*first1 == *first2))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InputIt1, typename InputIt2, typename BinaryPredicate>
|
||||||
|
bool Equal(InputIt1 first1, InputIt1 last1, InputIt2 first2, BinaryPredicate p)
|
||||||
|
{
|
||||||
|
for (; first1 != last1; ++first1, ++first2)
|
||||||
|
{
|
||||||
|
if (!p(*first1, *first2))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InputIt, typename OutputIt>
|
||||||
|
OutputIt Copy(InputIt first, InputIt last, OutputIt d_first)
|
||||||
|
{
|
||||||
|
for (; first != last; (void)++first, (void)++d_first)
|
||||||
|
*d_first = *first;
|
||||||
|
return d_first;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InputIt, typename OutputIt, typename UnaryPredicate>
|
||||||
|
OutputIt CopyIf(InputIt first, InputIt last,
|
||||||
|
OutputIt d_first, UnaryPredicate pred)
|
||||||
|
{
|
||||||
|
for (; first != last; ++first)
|
||||||
|
{
|
||||||
|
if (pred(*first))
|
||||||
|
{
|
||||||
|
*d_first = *first;
|
||||||
|
++d_first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return d_first;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ForwardIt, typename T>
|
||||||
|
void Fill(ForwardIt first, ForwardIt last, const T& value)
|
||||||
|
{
|
||||||
|
for (; first != last; ++first)
|
||||||
|
*first = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Math ops
|
||||||
|
namespace math
|
||||||
|
{
|
||||||
|
template <typename T> T Negate(const T& x) { return -x; }
|
||||||
|
|
||||||
|
template <typename T> T Add (const T& x, const T& y) { return x + y; }
|
||||||
|
template <typename T> T Subtract (const T& x, const T& y) { return x - y; }
|
||||||
|
template <typename T> T Multiply (const T& x, const T& y) { return x * y; }
|
||||||
|
template <typename T> T Divide (const T& x, const T& y) { return x / y; }
|
||||||
|
template <typename T> T Modulo (const T& x, const T& y) { return x % y; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,182 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Orange/Core/Traits.h>
|
||||||
|
#include <Orange/Core/AlignedStorage.h>
|
||||||
|
#include <Orange/Core/Span.h>
|
||||||
|
|
||||||
|
namespace orange
|
||||||
|
{
|
||||||
|
template <typename T, size_t N>
|
||||||
|
class SmallVector
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
SmallVector() {}
|
||||||
|
SmallVector(size_t size) { Resize(size); }
|
||||||
|
SmallVector(Span<T> span)
|
||||||
|
{
|
||||||
|
Reserve(span.size);
|
||||||
|
for (const auto& val : span)
|
||||||
|
PushBack(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
SmallVector (const SmallVector&) = delete;
|
||||||
|
SmallVector& operator = (const SmallVector&) = delete;
|
||||||
|
|
||||||
|
~SmallVector()
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < m_size; i++)
|
||||||
|
Ptr(i)->~T();
|
||||||
|
|
||||||
|
if (m_capacity > N)
|
||||||
|
delete[] u.m_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename J>
|
||||||
|
bool operator == (const Span<J>& x)
|
||||||
|
{
|
||||||
|
return Span<T>(*this) == x;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator == (const char *x)
|
||||||
|
{
|
||||||
|
return Span<T>(*this) == StringView(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reserve(size_t n)
|
||||||
|
{
|
||||||
|
n = PickCapacity(n);
|
||||||
|
|
||||||
|
if (n <= m_capacity)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Storage* data = new Storage[n];
|
||||||
|
|
||||||
|
for (size_t i = 0; i < m_size; i++)
|
||||||
|
{
|
||||||
|
new (&data[i]) T(Move(*Ptr(i)));
|
||||||
|
Ptr(i)->~T();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_capacity > N)
|
||||||
|
delete[] u.m_ptr;
|
||||||
|
|
||||||
|
m_capacity = n;
|
||||||
|
u.m_ptr = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Size() const { return m_size; }
|
||||||
|
|
||||||
|
const T* Data() const { return Ptr(0); }
|
||||||
|
T* Data() { return Ptr(0); }
|
||||||
|
|
||||||
|
void Resize(size_t n)
|
||||||
|
{
|
||||||
|
Reserve(n);
|
||||||
|
|
||||||
|
for (size_t i = n; i < m_size; i++)
|
||||||
|
Ptr(i)->~T();
|
||||||
|
|
||||||
|
for (size_t i = m_size; i < n; i++)
|
||||||
|
new (Ptr(i)) T();
|
||||||
|
|
||||||
|
m_size = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PushBack(const T& object)
|
||||||
|
{
|
||||||
|
Reserve(m_size + 1);
|
||||||
|
new (Ptr(m_size++)) T(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PushBack(T&& object)
|
||||||
|
{
|
||||||
|
Reserve(m_size + 1);
|
||||||
|
new (Ptr(m_size++)) T(Move(object));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
void EmplaceBack(Args... args)
|
||||||
|
{
|
||||||
|
Reserve(m_size + 1);
|
||||||
|
new (Ptr(m_size++)) T(Forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Erase(size_t idx)
|
||||||
|
{
|
||||||
|
Ptr(idx)->~T();
|
||||||
|
|
||||||
|
for (size_t i = idx; i < m_size - 1; i++)
|
||||||
|
{
|
||||||
|
new (Ptr(i)) T(Move(*Ptr(i + 1)));
|
||||||
|
Ptr(i + 1)->~T();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PopBack()
|
||||||
|
{
|
||||||
|
Ptr(--m_size)->~T();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Empty() const
|
||||||
|
{
|
||||||
|
return Size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
T& operator [] (size_t idx) { return *Ptr(idx); }
|
||||||
|
const T& operator [] (size_t idx) const { return *Ptr(idx); }
|
||||||
|
|
||||||
|
T* begin() { return Ptr(0); }
|
||||||
|
const T* begin() const { return Ptr(0); }
|
||||||
|
|
||||||
|
T* end() { return Ptr(m_size); }
|
||||||
|
const T* end() const { return Ptr(m_size); }
|
||||||
|
|
||||||
|
T& front() { return *Ptr(0); }
|
||||||
|
const T& front() const { return *Ptr(0); }
|
||||||
|
|
||||||
|
T& back() { return *Ptr(m_size - 1); }
|
||||||
|
const T& back() const { return *Ptr(m_size - 1); }
|
||||||
|
|
||||||
|
operator Span<T>() { return Span<T>(*this); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
using Storage = AlignedStorage<sizeof(T), alignof(T)>;
|
||||||
|
|
||||||
|
size_t m_capacity = N;
|
||||||
|
size_t m_size = 0;
|
||||||
|
|
||||||
|
union
|
||||||
|
{
|
||||||
|
Storage* m_ptr;
|
||||||
|
Storage m_data[sizeof(T) * N];
|
||||||
|
} u;
|
||||||
|
|
||||||
|
size_t PickCapacity(size_t n) {
|
||||||
|
size_t capacity = m_capacity;
|
||||||
|
|
||||||
|
while (capacity < n)
|
||||||
|
capacity = (capacity * 2) + 2;
|
||||||
|
|
||||||
|
return capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
T* Ptr(size_t idx) {
|
||||||
|
return m_capacity == N
|
||||||
|
? reinterpret_cast<T*>(&u.m_data[idx])
|
||||||
|
: reinterpret_cast<T*>(&u.m_ptr[idx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const T* Ptr(size_t idx) const
|
||||||
|
{
|
||||||
|
return m_capacity == N
|
||||||
|
? reinterpret_cast<const T*>(&u.m_data[idx])
|
||||||
|
: reinterpret_cast<const T*>(&u.m_ptr[idx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using Vector = SmallVector<T, sizeof(T) / sizeof(void*)>;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
namespace orange
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
struct DefaultLengthTypeResolver
|
||||||
|
{
|
||||||
|
using LengthType = float;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct DefaultLengthTypeResolver<double>
|
||||||
|
{
|
||||||
|
using LengthType = double;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct DefaultLengthTypeResolver<long double>
|
||||||
|
{
|
||||||
|
using LengthType = long double;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename J = DefaultLengthTypeResolver<T>::LengthType>
|
||||||
|
constexpr J rcp(const T& a)
|
||||||
|
{
|
||||||
|
return static_cast<J>(1) / static_cast<J>(a);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,275 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Orange/Core/Types.h>
|
||||||
|
#include <Orange/Core/Traits.h>
|
||||||
|
#include <Orange/Core/Array.h>
|
||||||
|
|
||||||
|
#include <Orange/Math/Basic.h>
|
||||||
|
|
||||||
|
namespace orange
|
||||||
|
{
|
||||||
|
template <typename T, size_t Size>
|
||||||
|
struct Vec
|
||||||
|
{
|
||||||
|
using DefaultLengthType = DefaultLengthTypeResolver<T>::LengthType;
|
||||||
|
using DefaultLengthVec = Vec<DefaultLengthType, Size>;
|
||||||
|
|
||||||
|
constexpr Vec()
|
||||||
|
: data{ } {}
|
||||||
|
|
||||||
|
constexpr explicit Vec(T splat)
|
||||||
|
{
|
||||||
|
Fill(data.begin(), data.end(), splat);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Vec(const T components[Size])
|
||||||
|
{
|
||||||
|
Copy(&components[0], &components[Size], data.begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
constexpr Vec(const Args&... args)
|
||||||
|
: data {{ args... }}
|
||||||
|
{
|
||||||
|
static_assert(sizeof...(Args) == Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Vec(const Vec& other) = default;
|
||||||
|
|
||||||
|
|
||||||
|
constexpr T& operator[](size_t index) { return data[index]; }
|
||||||
|
constexpr const T& operator[](size_t index) const { return data[index]; }
|
||||||
|
|
||||||
|
|
||||||
|
constexpr const T* begin() const { return data.begin(); }
|
||||||
|
constexpr T* begin() { return data.begin(); }
|
||||||
|
constexpr const T* end() const { return data.end(); }
|
||||||
|
constexpr T* end() { return data.end(); }
|
||||||
|
|
||||||
|
|
||||||
|
constexpr bool operator==(const Vec& other) const
|
||||||
|
{
|
||||||
|
return Equal(begin(), end(), other.begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool operator!=(const Vec& other) const
|
||||||
|
{
|
||||||
|
return !operator==(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename UnaryOperation>
|
||||||
|
constexpr Vec TransformResult(UnaryOperation op) const
|
||||||
|
{
|
||||||
|
return TransformResult<Vec>(begin(), end(), op);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename BinaryOperation>
|
||||||
|
constexpr Vec TransformResult(const T *other, BinaryOperation op) const
|
||||||
|
{
|
||||||
|
return TransformResult<Vec>(begin(), end(), other, op);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename BinaryOperation>
|
||||||
|
constexpr Vec TransformResult(const Vec& other, BinaryOperation op) const
|
||||||
|
{
|
||||||
|
return TransformResult(other.begin(), op);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename UnaryOperation>
|
||||||
|
constexpr Vec& TransformInPlace(UnaryOperation op)
|
||||||
|
{
|
||||||
|
Transform(begin(), end(), begin(), op);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename BinaryOperation>
|
||||||
|
constexpr Vec& TransformInPlace(const T *other, BinaryOperation op)
|
||||||
|
{
|
||||||
|
Transform(begin(), end(), other, begin(), op);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename BinaryOperation>
|
||||||
|
constexpr Vec& TransformInPlace(const Vec& other, BinaryOperation op)
|
||||||
|
{
|
||||||
|
return TransformInPlace(other.begin(), op);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
constexpr Vec operator-() const
|
||||||
|
{
|
||||||
|
return TransformResult(math::Negate);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
constexpr Vec operator+(const Vec& other) const
|
||||||
|
{
|
||||||
|
return TransformResult(other, math::Add);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Vec operator-(const Vec& other) const
|
||||||
|
{
|
||||||
|
return TransformResult(other, math::Subtract);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Vec operator*(const Vec& other) const
|
||||||
|
{
|
||||||
|
return TransformResult(other, math::Multiply);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Vec operator*(const T& scalar) const
|
||||||
|
{
|
||||||
|
return TransformResult([scalar](T value) { return value * scalar; });
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Vec operator/(const Vec& other) const
|
||||||
|
{
|
||||||
|
return TransformResult(other, math::Divide);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Vec operator/(const T& scalar) const
|
||||||
|
{
|
||||||
|
return TransformResult([scalar](T value) { return value / scalar; });
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Vec operator%(const Vec& other) const
|
||||||
|
{
|
||||||
|
return TransformResult(other, math::Modulo);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Vec operator%(const T& scalar) const
|
||||||
|
{
|
||||||
|
return TransformResult([scalar](T value) { return value % scalar; });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
constexpr Vec& operator+=(const Vec& other)
|
||||||
|
{
|
||||||
|
return TransformInPlace(math::Add);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Vec& operator-=(const Vec& other)
|
||||||
|
{
|
||||||
|
return TransformInPlace(math::Subtract);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Vec& operator*=(const Vec& other)
|
||||||
|
{
|
||||||
|
return TransformInPlace(math::Multiply);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Vec& operator*=(const T& scalar)
|
||||||
|
{
|
||||||
|
return TransformInPlace([scalar](T value) { return value * scalar; });
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Vec& operator/=(const Vec& other)
|
||||||
|
{
|
||||||
|
return TransformInPlace(math::Divide);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Vec& operator/=(const T& scalar)
|
||||||
|
{
|
||||||
|
return TransformInPlace([scalar](T value) { return value / scalar; });
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Vec& operator%=(const Vec& other)
|
||||||
|
{
|
||||||
|
return TransformInPlace(math::Modulo);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Vec& operator%=(const T& scalar)
|
||||||
|
{
|
||||||
|
return TransformInPlace([scalar](T value) { return value % scalar; });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static constexpr Vec Zero = Vec{ T{ 0 } };
|
||||||
|
static constexpr Vec Identity = Vec{ T{ 1 } };
|
||||||
|
|
||||||
|
|
||||||
|
// Align to 16 if the size is a multiple of 4 and T is 4 bytes in size.
|
||||||
|
// to take advantage of aligned load/stores.
|
||||||
|
static constexpr size_t Alignment =
|
||||||
|
(Size % 4zu == 0 && sizeof(T) == 4zu)
|
||||||
|
? Max<size_t>(16zu, alignof(T))
|
||||||
|
: alignof(T);
|
||||||
|
|
||||||
|
alignas(Alignment) Array<T, Size> data;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, size_t Size>
|
||||||
|
constexpr Vec<T, Size> operator*(T scalar, const Vec<T, Size>& Vec)
|
||||||
|
{
|
||||||
|
return Vec.TransformResult([scalar](T value) { return scalar * value; });
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, size_t Size>
|
||||||
|
constexpr Vec<T, Size> operator/(T scalar, const Vec<T, Size>& Vec)
|
||||||
|
{
|
||||||
|
return Vec.TransformResult([scalar](T value) { return scalar / value; });
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, size_t Size>
|
||||||
|
constexpr Vec<T, Size> operator%(T scalar, const Vec<T, Size>& Vec)
|
||||||
|
{
|
||||||
|
return Vec.TransformResult([scalar](T value) { return scalar % value; });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, size_t Size>
|
||||||
|
constexpr T accumulate(const Vec<T, Size>& a, T init = T{})
|
||||||
|
{
|
||||||
|
return Accumulate(a.begin(), a.end(), init);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, size_t Size>
|
||||||
|
constexpr T dot(const Vec<T, Size>& a, const Vec<T, Size>& b)
|
||||||
|
{
|
||||||
|
return accumulate(a * b);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, size_t Size>
|
||||||
|
constexpr T lengthSqr(const Vec<T, Size>& a)
|
||||||
|
{
|
||||||
|
return dot(a, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, size_t Size, typename J = Vec<T, Size>::DefaultLengthType>
|
||||||
|
constexpr J length(const Vec<T, Size>& a)
|
||||||
|
{
|
||||||
|
sqrtf(J{ lengthSqr(a) });
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, size_t Size, typename J = Vec<T, Size>::DefaultLengthType>
|
||||||
|
constexpr Vec<J, Size> normalize(const Vec<T, Size>& a)
|
||||||
|
{
|
||||||
|
return a * J{ rcp( length<T, Size>(a) ) };
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, size_t Size, typename J = Vec<T, Size>::DefaultLengthType>
|
||||||
|
constexpr Vec<J, Size> rcp(const Vec<T, Size>& a)
|
||||||
|
{
|
||||||
|
return TransformResult<Vec<J, Size>>(a.begin(), a.end(), [](T value){ return rcp<T, J>(value); });
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, size_t Size>
|
||||||
|
constexpr bool empty(const Vec<T, Size>& a)
|
||||||
|
{
|
||||||
|
return a == Vec<T, Size>::Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, size_t Size>
|
||||||
|
constexpr bool identity(const Vec<T, Size>& a)
|
||||||
|
{
|
||||||
|
return a == Vec<T, Size>::Identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
using Vec1 = Vec<float, 1>;
|
||||||
|
using Vec2 = Vec<float, 2>;
|
||||||
|
using Vec3 = Vec<float, 3>;
|
||||||
|
using Vec4 = Vec<float, 4>;
|
||||||
|
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
#include <Orange/Core/Span.h>
|
#include <Orange/Core/Span.h>
|
||||||
#include <Orange/Core/Result.h>
|
#include <Orange/Core/Result.h>
|
||||||
#include <Orange/Core/Array.h>
|
#include <Orange/Core/Vector.h>
|
||||||
|
|
||||||
#include <Orange/Render/RenderContext.h>
|
#include <Orange/Render/RenderContext.h>
|
||||||
#include <vulkan/vulkan_core.h>
|
#include <vulkan/vulkan_core.h>
|
||||||
|
@ -51,13 +51,13 @@ namespace orange
|
||||||
VkExtent2D m_extent;
|
VkExtent2D m_extent;
|
||||||
VkSwapchainKHR m_swapchain;
|
VkSwapchainKHR m_swapchain;
|
||||||
|
|
||||||
SmallArray<VkImage, 6> m_images;
|
SmallVector<VkImage, 6> m_images;
|
||||||
SmallArray<VkImageView, 6> m_imageViews;
|
SmallVector<VkImageView, 6> m_imageViews;
|
||||||
|
|
||||||
SmallArray<VkCommandBuffer, MaxFramesInFlight> m_commandBuffers;
|
SmallVector<VkCommandBuffer, MaxFramesInFlight> m_commandBuffers;
|
||||||
SmallArray<VkSemaphore, MaxFramesInFlight> m_imageAvailableSemaphores;
|
SmallVector<VkSemaphore, MaxFramesInFlight> m_imageAvailableSemaphores;
|
||||||
SmallArray<VkSemaphore, MaxFramesInFlight> m_renderFinishedSemaphores;
|
SmallVector<VkSemaphore, MaxFramesInFlight> m_renderFinishedSemaphores;
|
||||||
SmallArray<VkFence, MaxFramesInFlight> m_inFlightFences;
|
SmallVector<VkFence, MaxFramesInFlight> m_inFlightFences;
|
||||||
|
|
||||||
uint32_t m_currentFrame = 0;
|
uint32_t m_currentFrame = 0;
|
||||||
uint32_t m_currentImage = 0;
|
uint32_t m_currentImage = 0;
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
#include "Orange/Core/Array.h"
|
#include <Orange/Core/Array.h>
|
||||||
|
#include <Orange/Core/Vector.h>
|
||||||
#include "Orange/Core/Span.h"
|
#include "Orange/Core/Span.h"
|
||||||
#include <Orange/Core/Result.h>
|
#include <Orange/Core/Result.h>
|
||||||
#include <Orange/Core/FileSystem.h>
|
#include <Orange/Core/FileSystem.h>
|
||||||
|
#include <Orange/Math/Vector.h>
|
||||||
#include <Orange/Core/Parse.h>
|
#include <Orange/Core/Parse.h>
|
||||||
|
|
||||||
#include <Orange/Render/Window.h>
|
#include <Orange/Render/Window.h>
|
||||||
|
@ -16,7 +18,7 @@ Result<VoidResult> ParseOBJ(StringView buffer)
|
||||||
const char* end = buffer.data + buffer.size;
|
const char* end = buffer.data + buffer.size;
|
||||||
while (obj != end)
|
while (obj != end)
|
||||||
{
|
{
|
||||||
SmallArray<char, 8> element;
|
SmallVector<char, 8> element;
|
||||||
stream::ReadString(obj, end, " #\n", element);
|
stream::ReadString(obj, end, " #\n", element);
|
||||||
|
|
||||||
if (element == "v" || element == "vn" || element == "vt")
|
if (element == "v" || element == "vn" || element == "vt")
|
||||||
|
@ -37,7 +39,7 @@ Result<VoidResult> ParseOBJ(StringView buffer)
|
||||||
else if (element == "g" || element == "o")
|
else if (element == "g" || element == "o")
|
||||||
{
|
{
|
||||||
stream::ConsumeSpace(obj, end);
|
stream::ConsumeSpace(obj, end);
|
||||||
SmallArray<char, 32> name;
|
SmallVector<char, 32> name;
|
||||||
stream::ReadString(obj, end, " #\n", name);
|
stream::ReadString(obj, end, " #\n", name);
|
||||||
|
|
||||||
name.PushBack('\0');
|
name.PushBack('\0');
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#include <Orange/Core/Log.h>
|
#include <Orange/Core/Log.h>
|
||||||
#include <Orange/Core/Array.h>
|
#include <Orange/Core/Vector.h>
|
||||||
|
|
||||||
#include <Orange/Render/VulkanHelpers.h>
|
#include <Orange/Render/VulkanHelpers.h>
|
||||||
#include <Orange/Render/RenderContext.h>
|
#include <Orange/Render/RenderContext.h>
|
||||||
|
@ -9,7 +9,7 @@ namespace orange
|
||||||
{
|
{
|
||||||
static VulkanResult<VkInstance> CreateVkInstance(const char* appName)
|
static VulkanResult<VkInstance> CreateVkInstance(const char* appName)
|
||||||
{
|
{
|
||||||
SmallArray<const char *, 32> instanceExtensions;
|
SmallVector<const char *, 32> instanceExtensions;
|
||||||
{
|
{
|
||||||
auto r_tempWindow = Window::Create();
|
auto r_tempWindow = Window::Create();
|
||||||
if (!r_tempWindow)
|
if (!r_tempWindow)
|
||||||
|
@ -49,7 +49,7 @@ namespace orange
|
||||||
|
|
||||||
static Result<uint32_t> PickQueueFamilyIndex(VkPhysicalDevice physicalDevice)
|
static Result<uint32_t> PickQueueFamilyIndex(VkPhysicalDevice physicalDevice)
|
||||||
{
|
{
|
||||||
SmallArray<VkQueueFamilyProperties, 32> queueFamilyProperties;
|
SmallVector<VkQueueFamilyProperties, 32> queueFamilyProperties;
|
||||||
VkEnumerate(vkGetPhysicalDeviceQueueFamilyProperties, queueFamilyProperties, physicalDevice);
|
VkEnumerate(vkGetPhysicalDeviceQueueFamilyProperties, queueFamilyProperties, physicalDevice);
|
||||||
|
|
||||||
for (uint32_t i = 0; i < queueFamilyProperties.Size(); i++)
|
for (uint32_t i = 0; i < queueFamilyProperties.Size(); i++)
|
||||||
|
@ -80,7 +80,7 @@ namespace orange
|
||||||
|
|
||||||
static Result<VkPhysicalDevice> PickPhysicalDevice(VkInstance instance)
|
static Result<VkPhysicalDevice> PickPhysicalDevice(VkInstance instance)
|
||||||
{
|
{
|
||||||
SmallArray<VkPhysicalDevice, 32> physicalDevices;
|
SmallVector<VkPhysicalDevice, 32> physicalDevices;
|
||||||
VkEnumerate(vkEnumeratePhysicalDevices, physicalDevices, instance);
|
VkEnumerate(vkEnumeratePhysicalDevices, physicalDevices, instance);
|
||||||
|
|
||||||
VkPhysicalDevice bestPhysicalDevice = VK_NULL_HANDLE;
|
VkPhysicalDevice bestPhysicalDevice = VK_NULL_HANDLE;
|
||||||
|
|
|
@ -11,7 +11,7 @@ namespace orange
|
||||||
|
|
||||||
static Result<VkSurfaceFormatKHR> ChooseSwapChainFormat(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface)
|
static Result<VkSurfaceFormatKHR> ChooseSwapChainFormat(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface)
|
||||||
{
|
{
|
||||||
SmallArray<VkSurfaceFormatKHR, 32> surfaceFormats;
|
SmallVector<VkSurfaceFormatKHR, 32> surfaceFormats;
|
||||||
if (!VkEnumerate(vkGetPhysicalDeviceSurfaceFormatsKHR, surfaceFormats, physicalDevice, surface))
|
if (!VkEnumerate(vkGetPhysicalDeviceSurfaceFormatsKHR, surfaceFormats, physicalDevice, surface))
|
||||||
{
|
{
|
||||||
log::err("Surface supports no formats");
|
log::err("Surface supports no formats");
|
||||||
|
@ -29,7 +29,7 @@ namespace orange
|
||||||
|
|
||||||
static VkPresentModeKHR ChoosePresentMode(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface)
|
static VkPresentModeKHR ChoosePresentMode(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface)
|
||||||
{
|
{
|
||||||
SmallArray<VkPresentModeKHR, 32> presentModes;
|
SmallVector<VkPresentModeKHR, 32> presentModes;
|
||||||
VkEnumerate(vkGetPhysicalDeviceSurfacePresentModesKHR, presentModes, physicalDevice, surface);
|
VkEnumerate(vkGetPhysicalDeviceSurfacePresentModesKHR, presentModes, physicalDevice, surface);
|
||||||
|
|
||||||
for (auto& presentMode : presentModes) { if (presentMode == VK_PRESENT_MODE_MAILBOX_KHR) return presentMode; }
|
for (auto& presentMode : presentModes) { if (presentMode == VK_PRESENT_MODE_MAILBOX_KHR) return presentMode; }
|
||||||
|
@ -99,11 +99,11 @@ namespace orange
|
||||||
if (vkCreateSwapchainKHR(context.Device(), &swapchainInfo, nullptr, &swapchain) != VK_SUCCESS)
|
if (vkCreateSwapchainKHR(context.Device(), &swapchainInfo, nullptr, &swapchain) != VK_SUCCESS)
|
||||||
return Result<Swapchain>::PrintError("Failed to create swapchain");
|
return Result<Swapchain>::PrintError("Failed to create swapchain");
|
||||||
|
|
||||||
SmallArray<VkImage, 6> swapchainImages;
|
SmallVector<VkImage, 6> swapchainImages;
|
||||||
if (!VkEnumerate(vkGetSwapchainImagesKHR, swapchainImages, context.Device(), swapchain))
|
if (!VkEnumerate(vkGetSwapchainImagesKHR, swapchainImages, context.Device(), swapchain))
|
||||||
return Result<Swapchain>::PrintError("Failed to get swapchain images");
|
return Result<Swapchain>::PrintError("Failed to get swapchain images");
|
||||||
|
|
||||||
SmallArray<VkImageView, 6> swapchainImageViews{ swapchainImages.Size() };
|
SmallVector<VkImageView, 6> swapchainImageViews{ swapchainImages.Size() };
|
||||||
for (size_t i = 0; i < swapchainImages.Size(); i++)
|
for (size_t i = 0; i < swapchainImages.Size(); i++)
|
||||||
{
|
{
|
||||||
VkImageViewCreateInfo imageViewInfo =
|
VkImageViewCreateInfo imageViewInfo =
|
||||||
|
@ -119,7 +119,7 @@ namespace orange
|
||||||
return Result<Swapchain>::PrintError("Failed to get swapchain image view");
|
return Result<Swapchain>::PrintError("Failed to get swapchain image view");
|
||||||
}
|
}
|
||||||
|
|
||||||
SmallArray<VkCommandBuffer, 6> commandBuffers{ DefaultFramesInFlight };
|
SmallVector<VkCommandBuffer, 6> commandBuffers{ DefaultFramesInFlight };
|
||||||
{
|
{
|
||||||
VkCommandBufferAllocateInfo commandBufferInfo =
|
VkCommandBufferAllocateInfo commandBufferInfo =
|
||||||
{
|
{
|
||||||
|
@ -133,9 +133,9 @@ namespace orange
|
||||||
return Result<Swapchain>::PrintError("Failed to get swapchain image view");
|
return Result<Swapchain>::PrintError("Failed to get swapchain image view");
|
||||||
}
|
}
|
||||||
|
|
||||||
SmallArray<VkSemaphore, MaxFramesInFlight> imageAvailableSemaphores{ DefaultFramesInFlight };
|
SmallVector<VkSemaphore, MaxFramesInFlight> imageAvailableSemaphores{ DefaultFramesInFlight };
|
||||||
SmallArray<VkSemaphore, MaxFramesInFlight> renderFinishedSemaphores{ DefaultFramesInFlight };
|
SmallVector<VkSemaphore, MaxFramesInFlight> renderFinishedSemaphores{ DefaultFramesInFlight };
|
||||||
SmallArray<VkFence, MaxFramesInFlight> inFlightFences { DefaultFramesInFlight };
|
SmallVector<VkFence, MaxFramesInFlight> inFlightFences { DefaultFramesInFlight };
|
||||||
for (uint32_t i = 0; i < DefaultFramesInFlight; i++)
|
for (uint32_t i = 0; i < DefaultFramesInFlight; i++)
|
||||||
{
|
{
|
||||||
auto r_fence = context.CreateFence(true);
|
auto r_fence = context.CreateFence(true);
|
||||||
|
|
Loading…
Reference in New Issue