fix ResourceBuffer going out of scope before texture callbacks
This commit is contained in:
840
windows/include/filament/utils/Allocator.h
Normal file
840
windows/include/filament/utils/Allocator.h
Normal file
@@ -0,0 +1,840 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_UTILS_ALLOCATOR_H
|
||||
#define TNT_UTILS_ALLOCATOR_H
|
||||
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/memalign.h>
|
||||
#include <utils/Mutex.h>
|
||||
#include <utils/SpinLock.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <mutex>
|
||||
#include <type_traits>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace utils {
|
||||
|
||||
namespace pointermath {
|
||||
|
||||
template <typename P, typename T>
|
||||
static inline P* add(P* a, T b) noexcept {
|
||||
return (P*)(uintptr_t(a) + uintptr_t(b));
|
||||
}
|
||||
|
||||
template <typename P>
|
||||
static inline P* align(P* p, size_t alignment) noexcept {
|
||||
// alignment must be a power-of-two
|
||||
assert(alignment && !(alignment & alignment-1));
|
||||
return (P*)((uintptr_t(p) + alignment - 1) & ~(alignment - 1));
|
||||
}
|
||||
|
||||
template <typename P>
|
||||
static inline P* align(P* p, size_t alignment, size_t offset) noexcept {
|
||||
P* const r = align(add(p, offset), alignment);
|
||||
assert(r >= add(p, offset));
|
||||
return r;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* LinearAllocator
|
||||
*
|
||||
* + Allocates blocks linearly
|
||||
* + Cannot free individual blocks
|
||||
* + Can free top of memory back up to a specified point
|
||||
* + Doesn't call destructors
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
class LinearAllocator {
|
||||
public:
|
||||
// use memory area provided
|
||||
LinearAllocator(void* begin, void* end) noexcept;
|
||||
|
||||
template <typename AREA>
|
||||
explicit LinearAllocator(const AREA& area) : LinearAllocator(area.begin(), area.end()) { }
|
||||
|
||||
// Allocators can't be copied
|
||||
LinearAllocator(const LinearAllocator& rhs) = delete;
|
||||
LinearAllocator& operator=(const LinearAllocator& rhs) = delete;
|
||||
|
||||
// Allocators can be moved
|
||||
LinearAllocator(LinearAllocator&& rhs) noexcept;
|
||||
LinearAllocator& operator=(LinearAllocator&& rhs) noexcept;
|
||||
|
||||
~LinearAllocator() noexcept = default;
|
||||
|
||||
// our allocator concept
|
||||
void* alloc(size_t size, size_t alignment = alignof(std::max_align_t), size_t extra = 0) UTILS_RESTRICT {
|
||||
// branch-less allocation
|
||||
void* const p = pointermath::align(current(), alignment, extra);
|
||||
void* const c = pointermath::add(p, size);
|
||||
bool success = c <= end();
|
||||
set_current(success ? c : current());
|
||||
return success ? p : nullptr;
|
||||
}
|
||||
|
||||
// API specific to this allocator
|
||||
|
||||
void *getCurrent() UTILS_RESTRICT noexcept {
|
||||
return current();
|
||||
}
|
||||
|
||||
// free memory back to the specified point
|
||||
void rewind(void* p) UTILS_RESTRICT noexcept {
|
||||
assert(p>=mBegin && p<end());
|
||||
set_current(p);
|
||||
}
|
||||
|
||||
// frees all allocated blocks
|
||||
void reset() UTILS_RESTRICT noexcept {
|
||||
rewind(mBegin);
|
||||
}
|
||||
|
||||
size_t allocated() const UTILS_RESTRICT noexcept {
|
||||
return mSize;
|
||||
}
|
||||
|
||||
size_t available() const UTILS_RESTRICT noexcept {
|
||||
return mSize - mCur;
|
||||
}
|
||||
|
||||
void swap(LinearAllocator& rhs) noexcept;
|
||||
|
||||
void *base() noexcept { return mBegin; }
|
||||
|
||||
void free(void*, size_t) UTILS_RESTRICT noexcept { }
|
||||
|
||||
private:
|
||||
void* end() UTILS_RESTRICT noexcept { return pointermath::add(mBegin, mSize); }
|
||||
void* current() UTILS_RESTRICT noexcept { return pointermath::add(mBegin, mCur); }
|
||||
void set_current(void* p) UTILS_RESTRICT noexcept {
|
||||
mCur = uint32_t(uintptr_t(p) - uintptr_t(mBegin));
|
||||
}
|
||||
|
||||
void* mBegin = nullptr;
|
||||
uint32_t mSize = 0;
|
||||
uint32_t mCur = 0;
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* HeapAllocator
|
||||
*
|
||||
* + uses malloc() for all allocations
|
||||
* + frees blocks with free()
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
class HeapAllocator {
|
||||
public:
|
||||
HeapAllocator() noexcept = default;
|
||||
|
||||
template <typename AREA>
|
||||
explicit HeapAllocator(const AREA&) { }
|
||||
|
||||
// our allocator concept
|
||||
void* alloc(size_t size, size_t alignment = alignof(std::max_align_t), size_t extra = 0) {
|
||||
// this allocator doesn't support 'extra'
|
||||
assert(extra == 0);
|
||||
return aligned_alloc(size, alignment);
|
||||
}
|
||||
|
||||
void free(void* p) noexcept {
|
||||
aligned_free(p);
|
||||
}
|
||||
|
||||
void free(void* p, size_t) noexcept {
|
||||
this->free(p);
|
||||
}
|
||||
|
||||
~HeapAllocator() noexcept = default;
|
||||
|
||||
void swap(HeapAllocator&) noexcept { }
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
class FreeList {
|
||||
public:
|
||||
FreeList() noexcept = default;
|
||||
FreeList(void* begin, void* end, size_t elementSize, size_t alignment, size_t extra) noexcept;
|
||||
FreeList(const FreeList& rhs) = delete;
|
||||
FreeList& operator=(const FreeList& rhs) = delete;
|
||||
FreeList(FreeList&& rhs) noexcept = default;
|
||||
FreeList& operator=(FreeList&& rhs) noexcept = default;
|
||||
|
||||
void* pop() noexcept {
|
||||
Node* const head = mHead;
|
||||
mHead = head ? head->next : nullptr;
|
||||
// this could indicate a use after free
|
||||
assert(!mHead || mHead >= mBegin && mHead < mEnd);
|
||||
return head;
|
||||
}
|
||||
|
||||
void push(void* p) noexcept {
|
||||
assert(p);
|
||||
assert(p >= mBegin && p < mEnd);
|
||||
// TODO: assert this is one of our pointer (i.e.: it's address match one of ours)
|
||||
Node* const head = static_cast<Node*>(p);
|
||||
head->next = mHead;
|
||||
mHead = head;
|
||||
}
|
||||
|
||||
void *getFirst() noexcept {
|
||||
return mHead;
|
||||
}
|
||||
|
||||
private:
|
||||
struct Node {
|
||||
Node* next;
|
||||
};
|
||||
|
||||
static Node* init(void* begin, void* end,
|
||||
size_t elementSize, size_t alignment, size_t extra) noexcept;
|
||||
|
||||
Node* mHead = nullptr;
|
||||
|
||||
#ifndef NDEBUG
|
||||
// These are needed only for debugging...
|
||||
void* mBegin = nullptr;
|
||||
void* mEnd = nullptr;
|
||||
#endif
|
||||
};
|
||||
|
||||
class AtomicFreeList {
|
||||
public:
|
||||
AtomicFreeList() noexcept = default;
|
||||
AtomicFreeList(void* begin, void* end,
|
||||
size_t elementSize, size_t alignment, size_t extra) noexcept;
|
||||
AtomicFreeList(const FreeList& rhs) = delete;
|
||||
AtomicFreeList& operator=(const FreeList& rhs) = delete;
|
||||
|
||||
void* pop() noexcept {
|
||||
Node* const storage = mStorage;
|
||||
|
||||
HeadPtr currentHead = mHead.load();
|
||||
while (currentHead.offset >= 0) {
|
||||
// The value of "next" we load here might already contain application data if another
|
||||
// thread raced ahead of us. But in that case, the computed "newHead" will be discarded
|
||||
// since compare_exchange_weak fails. Then this thread will loop with the updated
|
||||
// value of currentHead, and try again.
|
||||
Node* const next = storage[currentHead.offset].next.load(std::memory_order_relaxed);
|
||||
const HeadPtr newHead{ next ? int32_t(next - storage) : -1, currentHead.tag + 1 };
|
||||
// In the rare case that the other thread that raced ahead of us already returned the
|
||||
// same mHead we just loaded, but it now has a different "next" value, the tag field will not
|
||||
// match, and compare_exchange_weak will fail and prevent that particular race condition.
|
||||
if (mHead.compare_exchange_weak(currentHead, newHead)) {
|
||||
// This assert needs to occur after we have validated that there was no race condition
|
||||
// Otherwise, next might already contain application data, if another thread
|
||||
// raced ahead of us after we loaded mHead, but before we loaded mHead->next.
|
||||
assert(!next || next >= storage);
|
||||
break;
|
||||
}
|
||||
}
|
||||
void* p = (currentHead.offset >= 0) ? (storage + currentHead.offset) : nullptr;
|
||||
assert(!p || p >= storage);
|
||||
return p;
|
||||
}
|
||||
|
||||
void push(void* p) noexcept {
|
||||
Node* const storage = mStorage;
|
||||
assert(p && p >= storage);
|
||||
Node* const node = static_cast<Node*>(p);
|
||||
HeadPtr currentHead = mHead.load();
|
||||
HeadPtr newHead = { int32_t(node - storage), currentHead.tag + 1 };
|
||||
do {
|
||||
newHead.tag = currentHead.tag + 1;
|
||||
Node* const n = (currentHead.offset >= 0) ? (storage + currentHead.offset) : nullptr;
|
||||
node->next.store(n, std::memory_order_relaxed);
|
||||
} while(!mHead.compare_exchange_weak(currentHead, newHead));
|
||||
}
|
||||
|
||||
void* getFirst() noexcept {
|
||||
return mStorage + mHead.load(std::memory_order_relaxed).offset;
|
||||
}
|
||||
|
||||
private:
|
||||
struct Node {
|
||||
// This should be a regular (non-atomic) pointer, but this causes TSAN to complain
|
||||
// about a data-race that exists but is benin. We always use this atomic<> in
|
||||
// relaxed mode.
|
||||
// The data race TSAN complains about is when a pop() is interrupted by a
|
||||
// pop() + push() just after mHead->next is read -- it appears as though it is written
|
||||
// without synchronization (by the push), however in that case, the pop's CAS will fail
|
||||
// and things will auto-correct.
|
||||
//
|
||||
// Pop() |
|
||||
// | |
|
||||
// read head->next |
|
||||
// | pop()
|
||||
// | |
|
||||
// | read head->next
|
||||
// | CAS, tag++
|
||||
// | |
|
||||
// | push()
|
||||
// | |
|
||||
// [TSAN: data-race here] write head->next
|
||||
// | CAS, tag++
|
||||
// CAS fails
|
||||
// |
|
||||
// read head->next
|
||||
// |
|
||||
// CAS, tag++
|
||||
//
|
||||
std::atomic<Node*> next;
|
||||
};
|
||||
|
||||
// This struct is using a 32-bit offset into the arena rather than
|
||||
// a direct pointer, because together with the 32-bit tag, it needs to
|
||||
// fit into 8 bytes. If it was any larger, it would not be possible to
|
||||
// access it atomically.
|
||||
struct alignas(8) HeadPtr {
|
||||
int32_t offset;
|
||||
uint32_t tag;
|
||||
};
|
||||
|
||||
std::atomic<HeadPtr> mHead{};
|
||||
|
||||
Node* mStorage = nullptr;
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
template <
|
||||
size_t ELEMENT_SIZE,
|
||||
size_t ALIGNMENT = alignof(std::max_align_t),
|
||||
size_t OFFSET = 0,
|
||||
typename FREELIST = FreeList>
|
||||
class PoolAllocator {
|
||||
static_assert(ELEMENT_SIZE >= sizeof(void*), "ELEMENT_SIZE must accommodate at least a pointer");
|
||||
public:
|
||||
// our allocator concept
|
||||
void* alloc(size_t size = ELEMENT_SIZE,
|
||||
size_t alignment = ALIGNMENT, size_t offset = OFFSET) noexcept {
|
||||
assert(size <= ELEMENT_SIZE);
|
||||
assert(alignment <= ALIGNMENT);
|
||||
assert(offset == OFFSET);
|
||||
return mFreeList.pop();
|
||||
}
|
||||
|
||||
void free(void* p, size_t = ELEMENT_SIZE) noexcept {
|
||||
mFreeList.push(p);
|
||||
}
|
||||
|
||||
constexpr size_t getSize() const noexcept { return ELEMENT_SIZE; }
|
||||
|
||||
PoolAllocator(void* begin, void* end) noexcept
|
||||
: mFreeList(begin, end, ELEMENT_SIZE, ALIGNMENT, OFFSET) {
|
||||
}
|
||||
|
||||
template <typename AREA>
|
||||
explicit PoolAllocator(const AREA& area) noexcept
|
||||
: PoolAllocator(area.begin(), area.end()) {
|
||||
}
|
||||
|
||||
// Allocators can't be copied
|
||||
PoolAllocator(const PoolAllocator& rhs) = delete;
|
||||
PoolAllocator& operator=(const PoolAllocator& rhs) = delete;
|
||||
|
||||
// Allocators can be moved
|
||||
PoolAllocator(PoolAllocator&& rhs) = default;
|
||||
PoolAllocator& operator=(PoolAllocator&& rhs) = default;
|
||||
|
||||
PoolAllocator() noexcept = default;
|
||||
~PoolAllocator() noexcept = default;
|
||||
|
||||
// API specific to this allocator
|
||||
|
||||
void *getCurrent() noexcept {
|
||||
return mFreeList.getFirst();
|
||||
}
|
||||
|
||||
private:
|
||||
FREELIST mFreeList;
|
||||
};
|
||||
|
||||
#define UTILS_MAX(a,b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
template <typename T, size_t OFFSET = 0>
|
||||
using ObjectPoolAllocator = PoolAllocator<sizeof(T),
|
||||
UTILS_MAX(alignof(FreeList), alignof(T)), OFFSET>;
|
||||
|
||||
template <typename T, size_t OFFSET = 0>
|
||||
using ThreadSafeObjectPoolAllocator = PoolAllocator<sizeof(T),
|
||||
UTILS_MAX(alignof(FreeList), alignof(T)), OFFSET, AtomicFreeList>;
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Areas
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
namespace AreaPolicy {
|
||||
|
||||
class StaticArea {
|
||||
public:
|
||||
StaticArea() noexcept = default;
|
||||
|
||||
StaticArea(void* b, void* e) noexcept
|
||||
: mBegin(b), mEnd(e) {
|
||||
}
|
||||
|
||||
~StaticArea() noexcept = default;
|
||||
|
||||
StaticArea(const StaticArea& rhs) = default;
|
||||
StaticArea& operator=(const StaticArea& rhs) = default;
|
||||
StaticArea(StaticArea&& rhs) noexcept = default;
|
||||
StaticArea& operator=(StaticArea&& rhs) noexcept = default;
|
||||
|
||||
void* data() const noexcept { return mBegin; }
|
||||
void* begin() const noexcept { return mBegin; }
|
||||
void* end() const noexcept { return mEnd; }
|
||||
size_t size() const noexcept { return uintptr_t(mEnd) - uintptr_t(mBegin); }
|
||||
|
||||
friend void swap(StaticArea& lhs, StaticArea& rhs) noexcept {
|
||||
using std::swap;
|
||||
swap(lhs.mBegin, rhs.mBegin);
|
||||
swap(lhs.mEnd, rhs.mEnd);
|
||||
}
|
||||
|
||||
private:
|
||||
void* mBegin = nullptr;
|
||||
void* mEnd = nullptr;
|
||||
};
|
||||
|
||||
class HeapArea {
|
||||
public:
|
||||
HeapArea() noexcept = default;
|
||||
|
||||
explicit HeapArea(size_t size) {
|
||||
if (size) {
|
||||
// TODO: policy committing memory
|
||||
mBegin = malloc(size);
|
||||
mEnd = pointermath::add(mBegin, size);
|
||||
}
|
||||
}
|
||||
|
||||
~HeapArea() noexcept {
|
||||
// TODO: policy for returning memory to system
|
||||
free(mBegin);
|
||||
}
|
||||
|
||||
HeapArea(const HeapArea& rhs) = delete;
|
||||
HeapArea& operator=(const HeapArea& rhs) = delete;
|
||||
HeapArea(HeapArea&& rhs) noexcept = delete;
|
||||
HeapArea& operator=(HeapArea&& rhs) noexcept = delete;
|
||||
|
||||
void* data() const noexcept { return mBegin; }
|
||||
void* begin() const noexcept { return mBegin; }
|
||||
void* end() const noexcept { return mEnd; }
|
||||
size_t size() const noexcept { return uintptr_t(mEnd) - uintptr_t(mBegin); }
|
||||
|
||||
friend void swap(HeapArea& lhs, HeapArea& rhs) noexcept {
|
||||
using std::swap;
|
||||
swap(lhs.mBegin, rhs.mBegin);
|
||||
swap(lhs.mEnd, rhs.mEnd);
|
||||
}
|
||||
|
||||
private:
|
||||
void* mBegin = nullptr;
|
||||
void* mEnd = nullptr;
|
||||
};
|
||||
|
||||
class NullArea {
|
||||
public:
|
||||
void* data() const noexcept { return nullptr; }
|
||||
size_t size() const noexcept { return 0; }
|
||||
};
|
||||
|
||||
} // namespace AreaPolicy
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Policies
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
namespace LockingPolicy {
|
||||
|
||||
struct NoLock {
|
||||
void lock() noexcept { }
|
||||
void unlock() noexcept { }
|
||||
};
|
||||
|
||||
using SpinLock = utils::SpinLock;
|
||||
using Mutex = utils::Mutex;
|
||||
|
||||
} // namespace LockingPolicy
|
||||
|
||||
|
||||
namespace TrackingPolicy {
|
||||
|
||||
// default no-op tracker
|
||||
struct Untracked {
|
||||
Untracked() noexcept = default;
|
||||
Untracked(const char* name, void* base, size_t size) noexcept {
|
||||
(void)name, void(base), (void)size;
|
||||
}
|
||||
void onAlloc(void* p, size_t size, size_t alignment, size_t extra) noexcept {
|
||||
(void)p, (void)size, (void)alignment, (void)extra;
|
||||
}
|
||||
void onFree(void* p, size_t = 0) noexcept { (void)p; }
|
||||
void onReset() noexcept { }
|
||||
void onRewind(void* addr) noexcept { (void)addr; }
|
||||
};
|
||||
|
||||
// This just track the max memory usage and logs it in the destructor
|
||||
struct HighWatermark {
|
||||
HighWatermark() noexcept = default;
|
||||
HighWatermark(const char* name, void* base, size_t size) noexcept
|
||||
: mName(name), mBase(base), mSize(uint32_t(size)) { }
|
||||
~HighWatermark() noexcept;
|
||||
void onAlloc(void* p, size_t size, size_t alignment, size_t extra) noexcept;
|
||||
void onFree(void* p, size_t size) noexcept;
|
||||
void onReset() noexcept;
|
||||
void onRewind(void const* addr) noexcept;
|
||||
uint32_t getHighWatermark() const noexcept { return mHighWaterMark; }
|
||||
protected:
|
||||
const char* mName = nullptr;
|
||||
void* mBase = nullptr;
|
||||
uint32_t mSize = 0;
|
||||
uint32_t mCurrent = 0;
|
||||
uint32_t mHighWaterMark = 0;
|
||||
};
|
||||
|
||||
// This just fills buffers with known values to help catch uninitialized access and use after free.
|
||||
struct Debug {
|
||||
Debug() noexcept = default;
|
||||
Debug(const char* name, void* base, size_t size) noexcept
|
||||
: mName(name), mBase(base), mSize(uint32_t(size)) { }
|
||||
void onAlloc(void* p, size_t size, size_t alignment, size_t extra) noexcept;
|
||||
void onFree(void* p, size_t size) noexcept;
|
||||
void onReset() noexcept;
|
||||
void onRewind(void* addr) noexcept;
|
||||
protected:
|
||||
const char* mName = nullptr;
|
||||
void* mBase = nullptr;
|
||||
uint32_t mSize = 0;
|
||||
};
|
||||
|
||||
struct DebugAndHighWatermark : protected HighWatermark, protected Debug {
|
||||
DebugAndHighWatermark() noexcept = default;
|
||||
DebugAndHighWatermark(const char* name, void* base, size_t size) noexcept
|
||||
: HighWatermark(name, base, size), Debug(name, base, size) { }
|
||||
void onAlloc(void* p, size_t size, size_t alignment, size_t extra) noexcept {
|
||||
HighWatermark::onAlloc(p, size, alignment, extra);
|
||||
Debug::onAlloc(p, size, alignment, extra);
|
||||
}
|
||||
void onFree(void* p, size_t size) noexcept {
|
||||
HighWatermark::onFree(p, size);
|
||||
Debug::onFree(p, size);
|
||||
}
|
||||
void onReset() noexcept {
|
||||
HighWatermark::onReset();
|
||||
Debug::onReset();
|
||||
}
|
||||
void onRewind(void* addr) noexcept {
|
||||
HighWatermark::onRewind(addr);
|
||||
Debug::onRewind(addr);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace TrackingPolicy
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Arenas
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
template<typename AllocatorPolicy, typename LockingPolicy,
|
||||
typename TrackingPolicy = TrackingPolicy::Untracked,
|
||||
typename AreaPolicy = AreaPolicy::HeapArea>
|
||||
class Arena {
|
||||
public:
|
||||
|
||||
Arena() = default;
|
||||
|
||||
// construct an arena with a name and forward argument to its allocator
|
||||
template<typename ... ARGS>
|
||||
Arena(const char* name, size_t size, ARGS&& ... args)
|
||||
: mArenaName(name),
|
||||
mArea(size),
|
||||
mAllocator(mArea, std::forward<ARGS>(args) ... ),
|
||||
mListener(name, mArea.data(), mArea.size()) {
|
||||
}
|
||||
|
||||
template<typename ... ARGS>
|
||||
Arena(const char* name, AreaPolicy&& area, ARGS&& ... args)
|
||||
: mArenaName(name),
|
||||
mArea(std::forward<AreaPolicy>(area)),
|
||||
mAllocator(mArea, std::forward<ARGS>(args) ... ),
|
||||
mListener(name, mArea.data(), mArea.size()) {
|
||||
}
|
||||
|
||||
// allocate memory from arena with given size and alignment
|
||||
// (acceptable size/alignment may depend on the allocator provided)
|
||||
void* alloc(size_t size, size_t alignment = alignof(std::max_align_t), size_t extra = 0) noexcept {
|
||||
std::lock_guard<LockingPolicy> guard(mLock);
|
||||
void* p = mAllocator.alloc(size, alignment, extra);
|
||||
mListener.onAlloc(p, size, alignment, extra);
|
||||
return p;
|
||||
}
|
||||
|
||||
// Allocate an array of trivially destructible objects
|
||||
// for safety, we disable the object-based alloc method if the object type is not
|
||||
// trivially destructible, since free() won't call the destructor and this is allocating
|
||||
// an array.
|
||||
template <typename T,
|
||||
typename = typename std::enable_if<std::is_trivially_destructible<T>::value>::type>
|
||||
T* alloc(size_t count, size_t alignment = alignof(T), size_t extra = 0) noexcept {
|
||||
return (T*)alloc(count * sizeof(T), alignment, extra);
|
||||
}
|
||||
|
||||
// return memory pointed by p to the arena
|
||||
// (actual behaviour may depend on allocator provided)
|
||||
void free(void* p) noexcept {
|
||||
if (p) {
|
||||
std::lock_guard<LockingPolicy> guard(mLock);
|
||||
mListener.onFree(p);
|
||||
mAllocator.free(p);
|
||||
}
|
||||
}
|
||||
|
||||
// some allocators require the size of the allocation for free
|
||||
void free(void* p, size_t size) noexcept {
|
||||
if (p) {
|
||||
std::lock_guard<LockingPolicy> guard(mLock);
|
||||
mListener.onFree(p, size);
|
||||
mAllocator.free(p, size);
|
||||
}
|
||||
}
|
||||
|
||||
// some allocators don't have a free() call, but a single reset() or rewind() instead
|
||||
void reset() noexcept {
|
||||
std::lock_guard<LockingPolicy> guard(mLock);
|
||||
mListener.onReset();
|
||||
mAllocator.reset();
|
||||
}
|
||||
|
||||
void* getCurrent() noexcept { return mAllocator.getCurrent(); }
|
||||
|
||||
void rewind(void *addr) noexcept {
|
||||
std::lock_guard<LockingPolicy> guard(mLock);
|
||||
mListener.onRewind(addr);
|
||||
mAllocator.rewind(addr);
|
||||
}
|
||||
|
||||
// Allocate and construct an object
|
||||
template<typename T, size_t ALIGN = alignof(T), typename... ARGS>
|
||||
T* make(ARGS&& ... args) noexcept {
|
||||
void* const p = this->alloc(sizeof(T), ALIGN);
|
||||
return p ? new(p) T(std::forward<ARGS>(args)...) : nullptr;
|
||||
}
|
||||
|
||||
// destroys an object created with make<T>() above, and frees associated memory
|
||||
template<typename T>
|
||||
void destroy(T* p) noexcept {
|
||||
if (p) {
|
||||
p->~T();
|
||||
this->free((void*)p, sizeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
char const* getName() const noexcept { return mArenaName; }
|
||||
|
||||
AllocatorPolicy& getAllocator() noexcept { return mAllocator; }
|
||||
AllocatorPolicy const& getAllocator() const noexcept { return mAllocator; }
|
||||
|
||||
TrackingPolicy& getListener() noexcept { return mListener; }
|
||||
TrackingPolicy const& getListener() const noexcept { return mListener; }
|
||||
|
||||
AreaPolicy& getArea() noexcept { return mArea; }
|
||||
AreaPolicy const& getArea() const noexcept { return mArea; }
|
||||
|
||||
void setListener(TrackingPolicy listener) noexcept {
|
||||
std::swap(mListener, listener);
|
||||
}
|
||||
|
||||
template <typename ... ARGS>
|
||||
void emplaceListener(ARGS&& ... args) noexcept {
|
||||
mListener.~TrackingPolicy();
|
||||
new (&mListener) TrackingPolicy(std::forward<ARGS>(args)...);
|
||||
}
|
||||
|
||||
// An arena can't be copied
|
||||
Arena(Arena const& rhs) noexcept = delete;
|
||||
Arena& operator=(Arena const& rhs) noexcept = delete;
|
||||
|
||||
friend void swap(Arena& lhs, Arena& rhs) noexcept {
|
||||
using std::swap;
|
||||
swap(lhs.mArea, rhs.mArea);
|
||||
swap(lhs.mAllocator, rhs.mAllocator);
|
||||
swap(lhs.mLock, rhs.mLock);
|
||||
swap(lhs.mListener, rhs.mListener);
|
||||
swap(lhs.mArenaName, rhs.mArenaName);
|
||||
}
|
||||
|
||||
private:
|
||||
char const* mArenaName = nullptr;
|
||||
AreaPolicy mArea;
|
||||
// note: we should use something like compressed_pair for the members below
|
||||
AllocatorPolicy mAllocator;
|
||||
LockingPolicy mLock;
|
||||
TrackingPolicy mListener;
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
template<typename TrackingPolicy = TrackingPolicy::Untracked>
|
||||
using HeapArena = Arena<HeapAllocator, LockingPolicy::NoLock, TrackingPolicy>;
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
// This doesn't implement our allocator concept, because it's too risky to use this as an allocator
|
||||
// in particular, doing ArenaScope<ArenaScope>.
|
||||
template<typename ARENA>
|
||||
class ArenaScope {
|
||||
|
||||
struct Finalizer {
|
||||
void (*finalizer)(void* p) = nullptr;
|
||||
Finalizer* next = nullptr;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
static void destruct(void* p) noexcept {
|
||||
static_cast<T*>(p)->~T();
|
||||
}
|
||||
|
||||
public:
|
||||
explicit ArenaScope(ARENA& allocator)
|
||||
: mArena(allocator), mRewind(allocator.getCurrent()) {
|
||||
}
|
||||
|
||||
ArenaScope& operator=(const ArenaScope& rhs) = delete;
|
||||
ArenaScope(ArenaScope&& rhs) noexcept = delete;
|
||||
ArenaScope& operator=(ArenaScope&& rhs) noexcept = delete;
|
||||
|
||||
~ArenaScope() {
|
||||
// run the finalizer chain
|
||||
Finalizer* head = mFinalizerHead;
|
||||
while (head) {
|
||||
void* p = pointermath::add(head, sizeof(Finalizer));
|
||||
head->finalizer(p);
|
||||
head = head->next;
|
||||
}
|
||||
// ArenaScope works only with Arena that implements rewind()
|
||||
mArena.rewind(mRewind);
|
||||
}
|
||||
|
||||
template<typename T, size_t ALIGN = alignof(T), typename... ARGS>
|
||||
T* make(ARGS&& ... args) noexcept {
|
||||
T* o = nullptr;
|
||||
if (std::is_trivially_destructible<T>::value) {
|
||||
o = mArena.template make<T, ALIGN>(std::forward<ARGS>(args)...);
|
||||
} else {
|
||||
void* const p = (Finalizer*)mArena.alloc(sizeof(T), ALIGN, sizeof(Finalizer));
|
||||
if (p != nullptr) {
|
||||
Finalizer* const f = static_cast<Finalizer*>(p) - 1;
|
||||
// constructor must be called before adding the dtor to the list
|
||||
// so that the ctor can allocate objects in a nested scope and have the
|
||||
// finalizers called in reverse order.
|
||||
o = new(p) T(std::forward<ARGS>(args)...);
|
||||
f->finalizer = &destruct<T>;
|
||||
f->next = mFinalizerHead;
|
||||
mFinalizerHead = f;
|
||||
}
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
void* allocate(size_t size, size_t alignment = 1) noexcept {
|
||||
return mArena.template alloc<uint8_t>(size, alignment, 0);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* allocate(size_t size, size_t alignment = alignof(T), size_t extra = 0) noexcept {
|
||||
return mArena.template alloc<T>(size, alignment, extra);
|
||||
}
|
||||
|
||||
// use with caution
|
||||
ARENA& getAllocator() noexcept { return mArena; }
|
||||
|
||||
private:
|
||||
ARENA& mArena;
|
||||
void* mRewind = nullptr;
|
||||
Finalizer* mFinalizerHead = nullptr;
|
||||
};
|
||||
|
||||
|
||||
template <typename TYPE, typename ARENA>
|
||||
class STLAllocator {
|
||||
public:
|
||||
using value_type = TYPE;
|
||||
using pointer = TYPE*;
|
||||
using const_pointer = const TYPE*;
|
||||
using reference = TYPE&;
|
||||
using const_reference = const TYPE&;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using propagate_on_container_move_assignment = std::true_type;
|
||||
using is_always_equal = std::true_type;
|
||||
|
||||
template<typename OTHER>
|
||||
struct rebind { using other = STLAllocator<OTHER, ARENA>; };
|
||||
|
||||
public:
|
||||
// we don't make this explicit, so that we can initialize a vector using a STLAllocator
|
||||
// from an Arena, avoiding to have to repeat the vector type.
|
||||
STLAllocator(ARENA& arena) : mArena(arena) { } // NOLINT(google-explicit-constructor)
|
||||
|
||||
template<typename U>
|
||||
explicit STLAllocator(STLAllocator<U, ARENA> const& rhs) : mArena(rhs.mArena) { }
|
||||
|
||||
TYPE* allocate(std::size_t n) {
|
||||
auto p = static_cast<TYPE *>(mArena.alloc(n * sizeof(TYPE), alignof(TYPE)));
|
||||
assert_invariant(p);
|
||||
return p;
|
||||
}
|
||||
|
||||
void deallocate(TYPE* p, std::size_t n) {
|
||||
mArena.free(p, n * sizeof(TYPE));
|
||||
}
|
||||
|
||||
// these should be out-of-class friends, but this doesn't seem to work with some compilers
|
||||
// which complain about multiple definition each time a STLAllocator<> is instantiated.
|
||||
template <typename U, typename A>
|
||||
bool operator==(const STLAllocator<U, A>& rhs) const noexcept {
|
||||
return std::addressof(mArena) == std::addressof(rhs.mArena);
|
||||
}
|
||||
|
||||
template <typename U, typename A>
|
||||
bool operator!=(const STLAllocator<U, A>& rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename U, typename A>
|
||||
friend class STLAllocator;
|
||||
|
||||
ARENA& mArena;
|
||||
};
|
||||
|
||||
} // namespace utils
|
||||
|
||||
#endif // TNT_UTILS_ALLOCATOR_H
|
||||
145
windows/include/filament/utils/BitmaskEnum.h
Normal file
145
windows/include/filament/utils/BitmaskEnum.h
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_UTILS_BITMASKENUM_H
|
||||
#define TNT_UTILS_BITMASKENUM_H
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <type_traits> // for std::false_type
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace utils {
|
||||
|
||||
template<typename Enum>
|
||||
struct EnableBitMaskOperators : public std::false_type { };
|
||||
|
||||
template<typename Enum>
|
||||
struct EnableIntegerOperators : public std::false_type { };
|
||||
|
||||
namespace Enum {
|
||||
|
||||
template<typename Enum>
|
||||
size_t count();
|
||||
|
||||
} // namespace enum
|
||||
} // namespace utils
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
template<typename Enum, typename std::enable_if_t<
|
||||
std::is_enum<Enum>::value && utils::EnableIntegerOperators<Enum>::value, int> = 0>
|
||||
inline constexpr int operator+(Enum value) noexcept {
|
||||
return int(value);
|
||||
}
|
||||
|
||||
template<typename Enum, typename std::enable_if_t<
|
||||
std::is_enum<Enum>::value && utils::EnableIntegerOperators<Enum>::value, int> = 0>
|
||||
inline constexpr bool operator==(Enum lhs, size_t rhs) noexcept {
|
||||
using underlying_t = std::underlying_type_t<Enum>;
|
||||
return underlying_t(lhs) == rhs;
|
||||
}
|
||||
|
||||
template<typename Enum, typename std::enable_if_t<
|
||||
std::is_enum<Enum>::value && utils::EnableIntegerOperators<Enum>::value, int> = 0>
|
||||
inline constexpr bool operator==(size_t lhs, Enum rhs) noexcept {
|
||||
return rhs == lhs;
|
||||
}
|
||||
|
||||
template<typename Enum, typename std::enable_if_t<
|
||||
std::is_enum<Enum>::value && utils::EnableIntegerOperators<Enum>::value, int> = 0>
|
||||
inline constexpr bool operator!=(Enum lhs, size_t rhs) noexcept {
|
||||
return !(rhs == lhs);
|
||||
}
|
||||
|
||||
template<typename Enum, typename std::enable_if_t<
|
||||
std::is_enum<Enum>::value && utils::EnableIntegerOperators<Enum>::value, int> = 0>
|
||||
inline constexpr bool operator!=(size_t lhs, Enum rhs) noexcept {
|
||||
return rhs != lhs;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
template<typename Enum, typename std::enable_if_t<
|
||||
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
|
||||
inline constexpr bool operator!(Enum rhs) noexcept {
|
||||
using underlying = std::underlying_type_t<Enum>;
|
||||
return underlying(rhs) == 0;
|
||||
}
|
||||
|
||||
template<typename Enum, typename std::enable_if_t<
|
||||
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
|
||||
inline constexpr Enum operator~(Enum rhs) noexcept {
|
||||
using underlying = std::underlying_type_t<Enum>;
|
||||
return Enum(~underlying(rhs));
|
||||
}
|
||||
|
||||
template<typename Enum, typename std::enable_if_t<
|
||||
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
|
||||
inline constexpr Enum operator|(Enum lhs, Enum rhs) noexcept {
|
||||
using underlying = std::underlying_type_t<Enum>;
|
||||
return Enum(underlying(lhs) | underlying(rhs));
|
||||
}
|
||||
|
||||
template<typename Enum, typename std::enable_if_t<
|
||||
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
|
||||
inline constexpr Enum operator&(Enum lhs, Enum rhs) noexcept {
|
||||
using underlying = std::underlying_type_t<Enum>;
|
||||
return Enum(underlying(lhs) & underlying(rhs));
|
||||
}
|
||||
|
||||
template<typename Enum, typename std::enable_if_t<
|
||||
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
|
||||
inline constexpr Enum operator^(Enum lhs, Enum rhs) noexcept {
|
||||
using underlying = std::underlying_type_t<Enum>;
|
||||
return Enum(underlying(lhs) ^ underlying(rhs));
|
||||
}
|
||||
|
||||
template<typename Enum, typename std::enable_if_t<
|
||||
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
|
||||
inline constexpr Enum operator|=(Enum& lhs, Enum rhs) noexcept {
|
||||
return lhs = lhs | rhs;
|
||||
}
|
||||
|
||||
template<typename Enum, typename std::enable_if_t<
|
||||
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
|
||||
inline constexpr Enum operator&=(Enum& lhs, Enum rhs) noexcept {
|
||||
return lhs = lhs & rhs;
|
||||
}
|
||||
|
||||
template<typename Enum, typename std::enable_if_t<
|
||||
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
|
||||
inline constexpr Enum operator^=(Enum& lhs, Enum rhs) noexcept {
|
||||
return lhs = lhs ^ rhs;
|
||||
}
|
||||
|
||||
template<typename Enum, typename std::enable_if_t<
|
||||
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
|
||||
inline constexpr bool none(Enum lhs) noexcept {
|
||||
return !lhs;
|
||||
}
|
||||
|
||||
template<typename Enum, typename std::enable_if_t<
|
||||
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
|
||||
inline constexpr bool any(Enum lhs) noexcept {
|
||||
return !none(lhs);
|
||||
}
|
||||
|
||||
|
||||
#endif // TNT_UTILS_BITMASKENUM_H
|
||||
230
windows/include/filament/utils/CString.h
Normal file
230
windows/include/filament/utils/CString.h
Normal file
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_UTILS_CSTRING_H
|
||||
#define TNT_UTILS_CSTRING_H
|
||||
|
||||
// NOTE: this header should not include STL headers
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace utils {
|
||||
|
||||
//! \privatesection
|
||||
struct hashCStrings {
|
||||
typedef const char* argument_type;
|
||||
typedef size_t result_type;
|
||||
result_type operator()(argument_type cstr) const noexcept {
|
||||
size_t hash = 5381;
|
||||
while (int c = *cstr++) {
|
||||
hash = (hash * 33u) ^ size_t(c);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
template <size_t N>
|
||||
using StringLiteral = const char[N];
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
class UTILS_PUBLIC CString {
|
||||
public:
|
||||
using value_type = char;
|
||||
using size_type = uint32_t;
|
||||
using difference_type = int32_t;
|
||||
using reference = value_type&;
|
||||
using const_reference = const value_type&;
|
||||
using pointer = value_type*;
|
||||
using const_pointer = const value_type*;
|
||||
using iterator = value_type*;
|
||||
using const_iterator = const value_type*;
|
||||
|
||||
CString() noexcept {} // NOLINT(modernize-use-equals-default), Ubuntu compiler bug
|
||||
|
||||
// Allocates memory and appends a null. This constructor can be used to hold arbitrary data
|
||||
// inside the string (i.e. it can contain nulls or non-ASCII encodings).
|
||||
CString(const char* cstr, size_t length);
|
||||
|
||||
// Allocates memory for a string of size length plus space for the null terminating character.
|
||||
// Also initializes the memory to 0. This constructor can be used to hold arbitrary data
|
||||
// inside the string.
|
||||
explicit CString(size_t length);
|
||||
|
||||
// Allocates memory and copies traditional C string content. Unlike the above constructor, this
|
||||
// does not allow embedded nulls. This is explicit because this operation is costly.
|
||||
explicit CString(const char* cstr);
|
||||
|
||||
template<size_t N>
|
||||
CString(StringLiteral<N> const& other) noexcept // NOLINT(google-explicit-constructor)
|
||||
: CString(other, N - 1) {
|
||||
}
|
||||
|
||||
CString(const CString& rhs);
|
||||
|
||||
CString(CString&& rhs) noexcept {
|
||||
this->swap(rhs);
|
||||
}
|
||||
|
||||
|
||||
CString& operator=(const CString& rhs);
|
||||
|
||||
CString& operator=(CString&& rhs) noexcept {
|
||||
this->swap(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
~CString() noexcept {
|
||||
if (mData) {
|
||||
free(mData - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void swap(CString& other) noexcept {
|
||||
// don't use std::swap(), we don't want an STL dependency in this file
|
||||
auto *temp = mCStr;
|
||||
mCStr = other.mCStr;
|
||||
other.mCStr = temp;
|
||||
}
|
||||
|
||||
const_pointer c_str() const noexcept { return mCStr; }
|
||||
pointer c_str() noexcept { return mCStr; }
|
||||
const_pointer c_str_safe() const noexcept { return mData ? c_str() : ""; }
|
||||
const_pointer data() const noexcept { return c_str(); }
|
||||
pointer data() noexcept { return c_str(); }
|
||||
size_type size() const noexcept { return mData ? mData[-1].length : 0; }
|
||||
size_type length() const noexcept { return size(); }
|
||||
bool empty() const noexcept { return size() == 0; }
|
||||
|
||||
iterator begin() noexcept { return mCStr; }
|
||||
iterator end() noexcept { return begin() + length(); }
|
||||
const_iterator begin() const noexcept { return data(); }
|
||||
const_iterator end() const noexcept { return begin() + length(); }
|
||||
const_iterator cbegin() const noexcept { return begin(); }
|
||||
const_iterator cend() const noexcept { return end(); }
|
||||
|
||||
CString& replace(size_type pos, size_type len, const CString& str) noexcept;
|
||||
CString& insert(size_type pos, const CString& str) noexcept { return replace(pos, 0, str); }
|
||||
|
||||
const_reference operator[](size_type pos) const noexcept {
|
||||
assert(pos < size());
|
||||
return begin()[pos];
|
||||
}
|
||||
|
||||
reference operator[](size_type pos) noexcept {
|
||||
assert(pos < size());
|
||||
return begin()[pos];
|
||||
}
|
||||
|
||||
const_reference at(size_type pos) const noexcept {
|
||||
assert(pos < size());
|
||||
return begin()[pos];
|
||||
}
|
||||
|
||||
reference at(size_type pos) noexcept {
|
||||
assert(pos < size());
|
||||
return begin()[pos];
|
||||
}
|
||||
|
||||
reference front() noexcept {
|
||||
assert(size());
|
||||
return begin()[0];
|
||||
}
|
||||
|
||||
const_reference front() const noexcept {
|
||||
assert(size());
|
||||
return begin()[0];
|
||||
}
|
||||
|
||||
reference back() noexcept {
|
||||
assert(size());
|
||||
return begin()[size() - 1];
|
||||
}
|
||||
|
||||
const_reference back() const noexcept {
|
||||
assert(size());
|
||||
return begin()[size() - 1];
|
||||
}
|
||||
|
||||
// placement new declared as "throw" to avoid the compiler's null-check
|
||||
inline void* operator new(size_t, void* ptr) {
|
||||
assert(ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
struct Hasher : private hashCStrings {
|
||||
typedef CString argument_type;
|
||||
typedef size_t result_type;
|
||||
result_type operator()(const argument_type& s) const noexcept {
|
||||
return hashCStrings::operator()(s.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
struct Data {
|
||||
size_type length;
|
||||
};
|
||||
|
||||
// mCStr points to the C-string or nullptr. if non-null, mCStr is preceded by the string's size
|
||||
union {
|
||||
value_type *mCStr = nullptr;
|
||||
Data* mData; // Data is stored at mData[-1]
|
||||
};
|
||||
|
||||
int compare(const CString& rhs) const noexcept {
|
||||
size_type lhs_size = size();
|
||||
size_type rhs_size = rhs.size();
|
||||
if (lhs_size < rhs_size) return -1;
|
||||
if (lhs_size > rhs_size) return 1;
|
||||
return strncmp(data(), rhs.data(), size());
|
||||
}
|
||||
|
||||
friend bool operator==(CString const& lhs, CString const& rhs) noexcept {
|
||||
return (lhs.data() == rhs.data()) ||
|
||||
((lhs.size() == rhs.size()) && !strncmp(lhs.data(), rhs.data(), lhs.size()));
|
||||
}
|
||||
friend bool operator!=(CString const& lhs, CString const& rhs) noexcept {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
friend bool operator<(CString const& lhs, CString const& rhs) noexcept {
|
||||
return lhs.compare(rhs) < 0;
|
||||
}
|
||||
friend bool operator>(CString const& lhs, CString const& rhs) noexcept {
|
||||
return lhs.compare(rhs) > 0;
|
||||
}
|
||||
friend bool operator>=(CString const& lhs, CString const& rhs) noexcept {
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
friend bool operator<=(CString const& lhs, CString const& rhs) noexcept {
|
||||
return !(lhs > rhs);
|
||||
}
|
||||
};
|
||||
|
||||
// implement this for your type for automatic conversion to CString. Failing to do so leads
|
||||
// to a compile-time failure.
|
||||
template<typename T>
|
||||
CString to_string(T value) noexcept;
|
||||
|
||||
} // namespace utils
|
||||
|
||||
#endif // TNT_UTILS_CSTRING_H
|
||||
127
windows/include/filament/utils/CallStack.h
Normal file
127
windows/include/filament/utils/CallStack.h
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef UTILS_CALLSTACK_H
|
||||
#define UTILS_CALLSTACK_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <typeinfo>
|
||||
|
||||
#include <utils/CString.h>
|
||||
#include <utils/Log.h>
|
||||
|
||||
namespace utils {
|
||||
|
||||
/**
|
||||
* CallStack captures the current's thread call stack.
|
||||
*/
|
||||
class CallStack {
|
||||
public:
|
||||
/**
|
||||
* Creates an empty call stack
|
||||
* @see CallStack::capture()
|
||||
*/
|
||||
CallStack() = default;
|
||||
CallStack(const CallStack&) = default;
|
||||
~CallStack() = default;
|
||||
|
||||
/**
|
||||
* A convenience method to create and capture the stack trace in one go.
|
||||
* @param ignore number frames to ignore at the top of the stack.
|
||||
* @return A CallStack object
|
||||
*/
|
||||
static CallStack unwind(size_t ignore = 0) noexcept;
|
||||
|
||||
/**
|
||||
* Capture the current thread's stack and replaces the existing one if any.
|
||||
* @param ignore number frames to ignore at the top of the stack.
|
||||
*/
|
||||
void update(size_t ignore = 0) noexcept;
|
||||
|
||||
/**
|
||||
* Get the number of stack frames this object has recorded.
|
||||
* @return How many stack frames are accessible through operator[]
|
||||
*/
|
||||
size_t getFrameCount() const noexcept;
|
||||
|
||||
/**
|
||||
* Return the program-counter of each stack frame captured
|
||||
* @param index of the frame between 0 and getFrameCount()-1
|
||||
* @return the program-counter of the stack-frame recorded at index \p index
|
||||
* @throw std::out_of_range if the index is out of range
|
||||
*/
|
||||
intptr_t operator [](size_t index) const;
|
||||
|
||||
/** Demangles a C++ type name */
|
||||
static utils::CString demangleTypeName(const char* mangled);
|
||||
|
||||
template<typename T>
|
||||
static utils::CString typeName() {
|
||||
#if UTILS_HAS_RTTI
|
||||
return demangleTypeName(typeid(T).name());
|
||||
#else
|
||||
return CString("<no-rtti>");
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs a CallStack into a stream.
|
||||
* This will print, when possible, the demangled names of functions corresponding to the
|
||||
* program-counter recorded.
|
||||
*/
|
||||
friend utils::io::ostream& operator <<(utils::io::ostream& stream, const CallStack& callstack);
|
||||
|
||||
bool operator <(const CallStack& rhs) const;
|
||||
|
||||
inline bool operator >(const CallStack& rhs) const {
|
||||
return rhs < *this;
|
||||
}
|
||||
|
||||
inline bool operator !=(const CallStack& rhs) const {
|
||||
return *this < rhs || rhs < *this;
|
||||
}
|
||||
|
||||
inline bool operator >=(const CallStack& rhs) const {
|
||||
return !operator <(rhs);
|
||||
}
|
||||
|
||||
inline bool operator <=(const CallStack& rhs) const {
|
||||
return !operator >(rhs);
|
||||
}
|
||||
|
||||
inline bool operator ==(const CallStack& rhs) const {
|
||||
return !operator !=(rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
void update_gcc(size_t ignore) noexcept;
|
||||
|
||||
static utils::CString demangle(const char* mangled);
|
||||
|
||||
static constexpr size_t NUM_FRAMES = 20;
|
||||
|
||||
struct StackFrameInfo {
|
||||
intptr_t pc;
|
||||
};
|
||||
|
||||
size_t m_frame_count = 0;
|
||||
StackFrameInfo m_stack[NUM_FRAMES];
|
||||
};
|
||||
|
||||
} // namespace utils
|
||||
|
||||
#endif // UTILS_CALLSTACK_H
|
||||
88
windows/include/filament/utils/Entity.h
Normal file
88
windows/include/filament/utils/Entity.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_UTILS_ENTITY_H
|
||||
#define TNT_UTILS_ENTITY_H
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace utils {
|
||||
|
||||
class UTILS_PUBLIC Entity {
|
||||
public:
|
||||
// this can be used to create an array of to-be-filled entities (see create())
|
||||
Entity() noexcept { } // NOLINT(modernize-use-equals-default), Ubuntu compiler bug
|
||||
|
||||
// Entities can be copied
|
||||
Entity(const Entity& e) noexcept = default;
|
||||
Entity(Entity&& e) noexcept = default;
|
||||
Entity& operator=(const Entity& e) noexcept = default;
|
||||
Entity& operator=(Entity&& e) noexcept = default;
|
||||
|
||||
// Entities can be compared
|
||||
bool operator==(Entity e) const { return e.mIdentity == mIdentity; }
|
||||
bool operator!=(Entity e) const { return e.mIdentity != mIdentity; }
|
||||
|
||||
// Entities can be sorted
|
||||
bool operator<(Entity e) const { return e.mIdentity < mIdentity; }
|
||||
|
||||
bool isNull() const noexcept {
|
||||
return mIdentity == 0;
|
||||
}
|
||||
|
||||
// an id that can be used for debugging/printing
|
||||
uint32_t getId() const noexcept {
|
||||
return mIdentity;
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept { return !isNull(); }
|
||||
|
||||
void clear() noexcept { mIdentity = 0; }
|
||||
|
||||
// Exports an entity to an int32_t which can be used "as is" in the Java programing language.
|
||||
static int32_t smuggle(Entity entity) noexcept {
|
||||
return int32_t(entity.getId());
|
||||
}
|
||||
|
||||
// Imports an entity from an int32_t generated by smuggle() above.
|
||||
static Entity import(int32_t identity) noexcept {
|
||||
return Entity{ Type(identity) };
|
||||
}
|
||||
|
||||
struct Hasher {
|
||||
typedef Entity argument_type;
|
||||
typedef size_t result_type;
|
||||
result_type operator()(argument_type const& e) const {
|
||||
return e.getId();
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
friend class EntityManager;
|
||||
friend class EntityManagerImpl;
|
||||
using Type = uint32_t;
|
||||
|
||||
explicit Entity(Type identity) noexcept : mIdentity(identity) { }
|
||||
|
||||
Type mIdentity = 0;
|
||||
};
|
||||
|
||||
} // namespace utils
|
||||
|
||||
#endif // TNT_UTILS_ENTITY_H
|
||||
89
windows/include/filament/utils/EntityInstance.h
Normal file
89
windows/include/filament/utils/EntityInstance.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_UTILS_ENTITYINSTANCE_H
|
||||
#define TNT_UTILS_ENTITYINSTANCE_H
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
namespace utils {
|
||||
|
||||
class UTILS_PUBLIC EntityInstanceBase {
|
||||
public:
|
||||
using Type = uint32_t;
|
||||
protected:
|
||||
Type mInstance = 0;
|
||||
};
|
||||
|
||||
template <typename T, bool EDIT = false>
|
||||
class UTILS_PUBLIC EntityInstance : public EntityInstanceBase {
|
||||
public:
|
||||
// default Instance is invalid
|
||||
constexpr EntityInstance() noexcept = default;
|
||||
|
||||
// check if this Instance is valid
|
||||
constexpr bool isValid() const noexcept { return mInstance != 0; }
|
||||
|
||||
// Instances of same type can be copied/assigned
|
||||
constexpr EntityInstance(EntityInstance const& other) noexcept = default;
|
||||
constexpr EntityInstance& operator=(EntityInstance const& other) noexcept = default;
|
||||
|
||||
// EDIT instances can be converted to "read" Instances of same type
|
||||
template <typename = std::enable_if<!EDIT>>
|
||||
constexpr explicit EntityInstance(EntityInstance<T, true> const& other) noexcept {
|
||||
mInstance = other.asValue();
|
||||
}
|
||||
template <typename = std::enable_if<!EDIT>>
|
||||
EntityInstance& operator=(EntityInstance<T, true> const& other) noexcept {
|
||||
mInstance = other.asValue();
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Instances can be compared
|
||||
constexpr bool operator!=(EntityInstance e) const { return mInstance != e.mInstance; }
|
||||
constexpr bool operator==(EntityInstance e) const { return mInstance == e.mInstance; }
|
||||
|
||||
// Instances can be sorted
|
||||
constexpr bool operator<(EntityInstance e) const { return mInstance < e.mInstance; }
|
||||
constexpr bool operator<=(EntityInstance e) const { return mInstance <= e.mInstance; }
|
||||
constexpr bool operator>(EntityInstance e) const { return mInstance > e.mInstance; }
|
||||
constexpr bool operator>=(EntityInstance e) const { return mInstance >= e.mInstance; }
|
||||
|
||||
// and we can iterate
|
||||
constexpr EntityInstance& operator++() noexcept { ++mInstance; return *this; }
|
||||
constexpr EntityInstance& operator--() noexcept { --mInstance; return *this; }
|
||||
constexpr const EntityInstance operator++(int) const noexcept { return EntityInstance{ mInstance + 1 }; }
|
||||
constexpr const EntityInstance operator--(int) const noexcept { return EntityInstance{ mInstance - 1 }; }
|
||||
|
||||
|
||||
// return a value for this Instance (mostly needed for debugging
|
||||
constexpr uint32_t asValue() const noexcept { return mInstance; }
|
||||
|
||||
// auto convert to Type so it can be used as an index
|
||||
constexpr operator Type() const noexcept { return mInstance; } // NOLINT(google-explicit-constructor)
|
||||
|
||||
// conversion from Type so we can initialize from an index
|
||||
constexpr EntityInstance(Type value) noexcept { mInstance = value; } // NOLINT(google-explicit-constructor)
|
||||
};
|
||||
|
||||
} // namespace utils
|
||||
|
||||
#endif // TNT_UTILS_ENTITYINSTANCE_H
|
||||
133
windows/include/filament/utils/EntityManager.h
Normal file
133
windows/include/filament/utils/EntityManager.h
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_UTILS_ENTITYMANAGER_H
|
||||
#define TNT_UTILS_ENTITYMANAGER_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <utils/Entity.h>
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#ifndef FILAMENT_UTILS_TRACK_ENTITIES
|
||||
#define FILAMENT_UTILS_TRACK_ENTITIES false
|
||||
#endif
|
||||
|
||||
#if FILAMENT_UTILS_TRACK_ENTITIES
|
||||
#include <utils/ostream.h>
|
||||
#include <vector>
|
||||
#endif
|
||||
|
||||
namespace utils {
|
||||
|
||||
class UTILS_PUBLIC EntityManager {
|
||||
public:
|
||||
// Get the global EntityManager. It is recommended to cache this value.
|
||||
// Thread Safe.
|
||||
static EntityManager& get() noexcept;
|
||||
|
||||
class Listener {
|
||||
public:
|
||||
virtual void onEntitiesDestroyed(size_t n, Entity const* entities) noexcept = 0;
|
||||
protected:
|
||||
~Listener() noexcept;
|
||||
};
|
||||
|
||||
|
||||
// maximum number of entities that can exist at the same time
|
||||
static size_t getMaxEntityCount() noexcept {
|
||||
// because index 0 is reserved, we only have 2^GENERATION_SHIFT - 1 valid indices
|
||||
return RAW_INDEX_COUNT - 1;
|
||||
}
|
||||
|
||||
// create n entities. Thread safe.
|
||||
void create(size_t n, Entity* entities);
|
||||
|
||||
// destroys n entities. Thread safe.
|
||||
void destroy(size_t n, Entity* entities) noexcept;
|
||||
|
||||
// create a new Entity. Thread safe.
|
||||
// Return Entity.isNull() if the entity cannot be allocated.
|
||||
Entity create() {
|
||||
Entity e;
|
||||
create(1, &e);
|
||||
return e;
|
||||
}
|
||||
|
||||
// destroys an Entity. Thread safe.
|
||||
void destroy(Entity e) noexcept {
|
||||
destroy(1, &e);
|
||||
}
|
||||
|
||||
// return whether the given Entity has been destroyed (false) or not (true).
|
||||
// Thread safe.
|
||||
bool isAlive(Entity e) const noexcept {
|
||||
assert(getIndex(e) < RAW_INDEX_COUNT);
|
||||
return (!e.isNull()) && (getGeneration(e) == mGens[getIndex(e)]);
|
||||
}
|
||||
|
||||
// registers a listener to be called when an entity is destroyed. thread safe.
|
||||
// if the listener is already register, this method has no effect.
|
||||
void registerListener(Listener* l) noexcept;
|
||||
|
||||
// unregisters a listener.
|
||||
void unregisterListener(Listener* l) noexcept;
|
||||
|
||||
|
||||
/* no user serviceable parts below */
|
||||
|
||||
// current generation of the given index. Use for debugging and testing.
|
||||
uint8_t getGenerationForIndex(size_t index) const noexcept {
|
||||
return mGens[index];
|
||||
}
|
||||
// singleton, can't be copied
|
||||
EntityManager(const EntityManager& rhs) = delete;
|
||||
EntityManager& operator=(const EntityManager& rhs) = delete;
|
||||
|
||||
#if FILAMENT_UTILS_TRACK_ENTITIES
|
||||
std::vector<Entity> getActiveEntities() const;
|
||||
void dumpActiveEntities(utils::io::ostream& out) const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
friend class EntityManagerImpl;
|
||||
EntityManager();
|
||||
~EntityManager();
|
||||
|
||||
// GENERATION_SHIFT determines how many simultaneous Entities are available, the
|
||||
// minimum memory requirement is 2^GENERATION_SHIFT bytes.
|
||||
static constexpr const int GENERATION_SHIFT = 17;
|
||||
static constexpr const size_t RAW_INDEX_COUNT = (1 << GENERATION_SHIFT);
|
||||
static constexpr const Entity::Type INDEX_MASK = (1 << GENERATION_SHIFT) - 1u;
|
||||
|
||||
static inline Entity::Type getGeneration(Entity e) noexcept {
|
||||
return e.getId() >> GENERATION_SHIFT;
|
||||
}
|
||||
static inline Entity::Type getIndex(Entity e) noexcept {
|
||||
return e.getId() & INDEX_MASK;
|
||||
}
|
||||
static inline Entity::Type makeIdentity(Entity::Type g, Entity::Type i) noexcept {
|
||||
return (g << GENERATION_SHIFT) | (i & INDEX_MASK);
|
||||
}
|
||||
|
||||
// stores the generation of each index.
|
||||
uint8_t * const mGens;
|
||||
};
|
||||
|
||||
} // namespace utils
|
||||
|
||||
#endif // TNT_UTILS_ENTITYMANAGER_H
|
||||
420
windows/include/filament/utils/FixedCapacityVector.h
Normal file
420
windows/include/filament/utils/FixedCapacityVector.h
Normal file
@@ -0,0 +1,420 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_UTILS_FIXEDCAPACITYVECTOR_H
|
||||
#define TNT_UTILS_FIXEDCAPACITYVECTOR_H
|
||||
|
||||
#include <utils/compressed_pair.h>
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <initializer_list>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector> // TODO: is this necessary?
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define FILAMENT_FORCE_CAPACITY_CHECK true
|
||||
#else
|
||||
#define FILAMENT_FORCE_CAPACITY_CHECK false
|
||||
#endif
|
||||
|
||||
namespace utils {
|
||||
|
||||
/**
|
||||
* FixedCapacityVector is (almost) a drop-in replacement for std::vector<> except it has a
|
||||
* fixed capacity decided at runtime. The vector storage is never reallocated unless reserve()
|
||||
* is called. Operations that add elements to the vector can fail if there is not enough
|
||||
* capacity.
|
||||
*
|
||||
* An empty vector with a given capacity is created with
|
||||
* FixedCapacityVector<T>::with_capacity( capacity );
|
||||
*
|
||||
* NOTE: When passing an initial size into the FixedCapacityVector constructor, default construction
|
||||
* of the elements is skipped when their construction is trivial. This behavior is different from
|
||||
* std::vector. e.g., std::vector<int>(4) constructs 4 zeros while FixedCapacityVector<int>(4)
|
||||
* allocates 4 uninitialized values. Note that zero initialization is easily achieved by passing in
|
||||
* the optional value argument, e.g. FixedCapacityVector<int>(4, 0) or foo.resize(4, 0).
|
||||
*/
|
||||
template<typename T, typename A = std::allocator<T>, bool CapacityCheck = true>
|
||||
class UTILS_PUBLIC FixedCapacityVector {
|
||||
public:
|
||||
using allocator_type = A;
|
||||
using value_type = T;
|
||||
using reference = T&;
|
||||
using const_reference = T const&;
|
||||
using size_type = uint32_t;
|
||||
using difference_type = int32_t;
|
||||
using pointer = T*;
|
||||
using const_pointer = T const*;
|
||||
using iterator = pointer;
|
||||
using const_iterator = const_pointer;
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
|
||||
private:
|
||||
using storage_traits = std::allocator_traits<allocator_type>;
|
||||
|
||||
public:
|
||||
/** returns an empty vector with the specified capacity */
|
||||
static FixedCapacityVector with_capacity(
|
||||
size_type capacity, const allocator_type& allocator = allocator_type()) {
|
||||
return FixedCapacityVector(construct_with_capacity, capacity, allocator);
|
||||
}
|
||||
|
||||
FixedCapacityVector() = default;
|
||||
|
||||
explicit FixedCapacityVector(const allocator_type& allocator) noexcept
|
||||
: mCapacityAllocator({}, allocator) {
|
||||
}
|
||||
|
||||
explicit FixedCapacityVector(size_type size, const allocator_type& allocator = allocator_type())
|
||||
: mSize(size),
|
||||
mCapacityAllocator(size, allocator) {
|
||||
mData = this->allocator().allocate(this->capacity());
|
||||
construct(begin(), end());
|
||||
}
|
||||
|
||||
FixedCapacityVector(std::initializer_list<T> list,
|
||||
const allocator_type& alloc = allocator_type())
|
||||
: mSize(list.size()),
|
||||
mCapacityAllocator(list.size(), alloc) {
|
||||
mData = this->allocator().allocate(this->capacity());
|
||||
std::uninitialized_copy(list.begin(), list.end(), begin());
|
||||
}
|
||||
|
||||
FixedCapacityVector(size_type size, const_reference value,
|
||||
const allocator_type& alloc = allocator_type())
|
||||
: mSize(size),
|
||||
mCapacityAllocator(size, alloc) {
|
||||
mData = this->allocator().allocate(this->capacity());
|
||||
construct(begin(), end(), value);
|
||||
}
|
||||
|
||||
FixedCapacityVector(FixedCapacityVector const& rhs)
|
||||
: mSize(rhs.mSize),
|
||||
mCapacityAllocator(rhs.capacity(),
|
||||
storage_traits::select_on_container_copy_construction(rhs.allocator())) {
|
||||
mData = allocator().allocate(capacity());
|
||||
std::uninitialized_copy(rhs.begin(), rhs.end(), begin());
|
||||
}
|
||||
|
||||
FixedCapacityVector(FixedCapacityVector&& rhs) noexcept {
|
||||
this->swap(rhs);
|
||||
}
|
||||
|
||||
~FixedCapacityVector() noexcept {
|
||||
destroy(begin(), end());
|
||||
allocator().deallocate(data(), capacity());
|
||||
}
|
||||
|
||||
FixedCapacityVector& operator=(FixedCapacityVector const& rhs) {
|
||||
if (this != &rhs) {
|
||||
FixedCapacityVector t(rhs);
|
||||
this->swap(t);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
FixedCapacityVector& operator=(FixedCapacityVector&& rhs) noexcept {
|
||||
this->swap(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
allocator_type get_allocator() const noexcept {
|
||||
return mCapacityAllocator.second();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
iterator begin() noexcept { return data(); }
|
||||
iterator end() noexcept { return data() + size(); }
|
||||
const_iterator begin() const noexcept { return data(); }
|
||||
const_iterator end() const noexcept { return data() + size(); }
|
||||
reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
|
||||
const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); }
|
||||
reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
|
||||
const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); }
|
||||
const_iterator cbegin() const noexcept { return begin(); }
|
||||
const_iterator cend() const noexcept { return end(); }
|
||||
const_reverse_iterator crbegin() const noexcept { return rbegin(); }
|
||||
const_reverse_iterator crend() const noexcept { return rend(); }
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
size_type size() const noexcept { return mSize; }
|
||||
size_type capacity() const noexcept { return mCapacityAllocator.first(); }
|
||||
bool empty() const noexcept { return size() == 0; }
|
||||
size_type max_size() const noexcept {
|
||||
return std::min(storage_traits::max_size(allocator()),
|
||||
std::numeric_limits<size_type>::max());
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
reference operator[](size_type n) noexcept {
|
||||
assert(n < size());
|
||||
return *(begin() + n);
|
||||
}
|
||||
|
||||
const_reference operator[](size_type n) const noexcept {
|
||||
assert(n < size());
|
||||
return *(begin() + n);
|
||||
}
|
||||
|
||||
reference front() noexcept { return *begin(); }
|
||||
const_reference front() const noexcept { return *begin(); }
|
||||
reference back() noexcept { return *(end() - 1); }
|
||||
const_reference back() const noexcept { return *(end() - 1); }
|
||||
value_type* data() noexcept { return mData; }
|
||||
const value_type* data() const noexcept { return mData; }
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
void push_back(const_reference v) {
|
||||
auto pos = assertCapacityForSize(size() + 1);
|
||||
++mSize;
|
||||
storage_traits::construct(allocator(), pos, v);
|
||||
}
|
||||
|
||||
void push_back(value_type&& v) {
|
||||
auto pos = assertCapacityForSize(size() + 1);
|
||||
++mSize;
|
||||
storage_traits::construct(allocator(), pos, std::move(v));
|
||||
}
|
||||
|
||||
template<typename ... ARGS>
|
||||
reference emplace_back(ARGS&& ... args) {
|
||||
auto pos = assertCapacityForSize(size() + 1);
|
||||
++mSize;
|
||||
storage_traits::construct(allocator(), pos, std::forward<ARGS>(args)...);
|
||||
return *pos;
|
||||
}
|
||||
|
||||
void pop_back() {
|
||||
assert(!empty());
|
||||
--mSize;
|
||||
destroy(end(), end() + 1);
|
||||
}
|
||||
|
||||
iterator insert(const_iterator position, const_reference v) {
|
||||
if (position == end()) {
|
||||
push_back(v);
|
||||
} else {
|
||||
assertCapacityForSize(size() + 1);
|
||||
pointer p = const_cast<pointer>(position);
|
||||
move_range(p, end(), p + 1);
|
||||
++mSize;
|
||||
// here we handle inserting an element of this vector!
|
||||
const_pointer pv = std::addressof(v);
|
||||
if (p <= pv && pv < end()) {
|
||||
*p = *(pv + 1);
|
||||
} else {
|
||||
*p = v;
|
||||
}
|
||||
}
|
||||
return const_cast<iterator>(position);
|
||||
}
|
||||
|
||||
iterator insert(const_iterator position, value_type&& v) {
|
||||
if (position == end()) {
|
||||
push_back(std::move(v));
|
||||
} else {
|
||||
assertCapacityForSize(size() + 1);
|
||||
pointer p = const_cast<pointer>(position);
|
||||
move_range(p, end(), p + 1);
|
||||
++mSize;
|
||||
*p = std::move(v);
|
||||
}
|
||||
return const_cast<iterator>(position);
|
||||
}
|
||||
|
||||
iterator erase(const_iterator pos) {
|
||||
assert(pos != end());
|
||||
return erase(pos, pos + 1);
|
||||
}
|
||||
|
||||
iterator erase(const_iterator first, const_iterator last) {
|
||||
assert(first <= last);
|
||||
auto e = std::move(const_cast<iterator>(last), end(), const_cast<iterator>(first));
|
||||
destroy(e, end());
|
||||
mSize -= std::distance(first, last);
|
||||
return const_cast<iterator>(first);
|
||||
}
|
||||
|
||||
void clear() noexcept {
|
||||
destroy(begin(), end());
|
||||
mSize = 0;
|
||||
}
|
||||
|
||||
void resize(size_type count) {
|
||||
assertCapacityForSize(count);
|
||||
if constexpr(std::is_trivially_constructible_v<value_type> &&
|
||||
std::is_trivially_destructible_v<value_type>) {
|
||||
// we check for triviality here so that the implementation could be non-inline
|
||||
mSize = count;
|
||||
} else {
|
||||
resize_non_trivial(count);
|
||||
}
|
||||
}
|
||||
|
||||
void resize(size_type count, const_reference v) {
|
||||
assertCapacityForSize(count);
|
||||
resize_non_trivial(count, v);
|
||||
}
|
||||
|
||||
void swap(FixedCapacityVector& other) {
|
||||
using std::swap;
|
||||
swap(mData, other.mData);
|
||||
swap(mSize, other.mSize);
|
||||
mCapacityAllocator.swap(other.mCapacityAllocator);
|
||||
}
|
||||
|
||||
UTILS_NOINLINE
|
||||
void reserve(size_type c) {
|
||||
if (c > capacity()) {
|
||||
FixedCapacityVector t(construct_with_capacity, c, allocator());
|
||||
t.mSize = size();
|
||||
std::uninitialized_move(begin(), end(), t.begin());
|
||||
this->swap(t);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
enum construct_with_capacity_tag{ construct_with_capacity };
|
||||
|
||||
FixedCapacityVector(construct_with_capacity_tag,
|
||||
size_type capacity, const allocator_type& allocator = allocator_type())
|
||||
: mCapacityAllocator(capacity, allocator) {
|
||||
mData = this->allocator().allocate(this->capacity());
|
||||
}
|
||||
|
||||
allocator_type& allocator() noexcept {
|
||||
return mCapacityAllocator.second();
|
||||
}
|
||||
|
||||
allocator_type const& allocator() const noexcept {
|
||||
return mCapacityAllocator.second();
|
||||
}
|
||||
|
||||
iterator assertCapacityForSize(size_type s) {
|
||||
if constexpr(CapacityCheck || FILAMENT_FORCE_CAPACITY_CHECK) {
|
||||
ASSERT_PRECONDITION(capacity() >= s,
|
||||
"capacity exceeded: requested size %lu, available capacity %lu.",
|
||||
(unsigned long)s, (unsigned long)capacity());
|
||||
}
|
||||
return end();
|
||||
}
|
||||
|
||||
inline void construct(iterator first, iterator last) noexcept {
|
||||
// we check for triviality here so that the implementation could be non-inline
|
||||
if constexpr(!std::is_trivially_constructible_v<value_type>) {
|
||||
construct_non_trivial(first, last);
|
||||
}
|
||||
}
|
||||
|
||||
void construct(iterator first, iterator last, const_reference proto) noexcept {
|
||||
UTILS_NOUNROLL
|
||||
while (first != last) {
|
||||
storage_traits::construct(allocator(), first++, proto);
|
||||
}
|
||||
}
|
||||
|
||||
// should this be NOINLINE?
|
||||
void construct_non_trivial(iterator first, iterator last) noexcept {
|
||||
UTILS_NOUNROLL
|
||||
while (first != last) {
|
||||
storage_traits::construct(allocator(), first++);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline void destroy(iterator first, iterator last) noexcept {
|
||||
// we check for triviality here so that the implementation could be non-inline
|
||||
if constexpr(!std::is_trivially_destructible_v<value_type>) {
|
||||
destroy_non_trivial(first, last);
|
||||
}
|
||||
}
|
||||
|
||||
// should this be NOINLINE?
|
||||
void destroy_non_trivial(iterator first, iterator last) noexcept {
|
||||
UTILS_NOUNROLL
|
||||
while (first != last) {
|
||||
storage_traits::destroy(allocator(), --last);
|
||||
}
|
||||
}
|
||||
|
||||
// should this be NOINLINE?
|
||||
void resize_non_trivial(size_type count) {
|
||||
if (count > size()) {
|
||||
construct(end(), begin() + count);
|
||||
} else if (count < size()) {
|
||||
destroy(begin() + count, end());
|
||||
}
|
||||
mSize = count;
|
||||
}
|
||||
|
||||
// should this be NOINLINE?
|
||||
void resize_non_trivial(size_type count, const_reference v) {
|
||||
if (count > size()) {
|
||||
construct(end(), begin() + count, v);
|
||||
} else if (count < size()) {
|
||||
destroy(begin() + count, end());
|
||||
}
|
||||
mSize = count;
|
||||
}
|
||||
|
||||
// should this be NOINLINE?
|
||||
void move_range(pointer s, pointer e, pointer to) {
|
||||
if constexpr(std::is_trivially_copy_assignable_v<value_type>
|
||||
&& std::is_trivially_destructible_v<value_type>) {
|
||||
// this generates memmove -- which doesn't happen otherwise
|
||||
std::move_backward(s, e, to + std::distance(s, e));
|
||||
} else {
|
||||
pointer our_end = end();
|
||||
difference_type n = our_end - to; // nb of elements to move by operator=
|
||||
pointer i = s + n; // 1st element to move by move ctor
|
||||
for (pointer d = our_end ; i < our_end ; ++i, ++d) {
|
||||
storage_traits::construct(allocator(), d, std::move(*i));
|
||||
}
|
||||
std::move_backward(s, s + n, our_end);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TYPE>
|
||||
class SizeTypeWrapper {
|
||||
TYPE value{};
|
||||
public:
|
||||
SizeTypeWrapper() noexcept = default;
|
||||
SizeTypeWrapper(SizeTypeWrapper const& rhs) noexcept = default;
|
||||
explicit SizeTypeWrapper(TYPE value) noexcept : value(value) { }
|
||||
SizeTypeWrapper& operator=(TYPE rhs) noexcept { value = rhs; return *this; }
|
||||
SizeTypeWrapper& operator=(SizeTypeWrapper& rhs) noexcept = delete;
|
||||
operator TYPE() const noexcept { return value; }
|
||||
};
|
||||
|
||||
pointer mData{};
|
||||
size_type mSize{};
|
||||
compressed_pair<SizeTypeWrapper<size_type>, allocator_type> mCapacityAllocator{};
|
||||
};
|
||||
|
||||
} // namespace utils
|
||||
|
||||
#endif // TNT_UTILS_FIXEDCAPACITYVECTOR_H
|
||||
152
windows/include/filament/utils/Invocable.h
Normal file
152
windows/include/filament/utils/Invocable.h
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_UTILS_INVOKABLE_H
|
||||
#define TNT_UTILS_INVOKABLE_H
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
namespace utils {
|
||||
|
||||
/*
|
||||
* Invocable is a move-only general purpose function wrapper. Instances can
|
||||
* store and invoke lambda expressions and other function objects.
|
||||
*
|
||||
* It is similar to std::function, with the following differences:
|
||||
* - Invocable is move only.
|
||||
* - Invocable can capture move only types.
|
||||
* - No conversion between 'compatible' functions.
|
||||
* - No small buffer optimization
|
||||
*/
|
||||
|
||||
// Helper for enabling methods if Fn matches the function signature
|
||||
// requirements of the Invocable type.
|
||||
#if __cplusplus >= 201703L
|
||||
// only available on C++17 and up
|
||||
template<typename Fn, typename R, typename... Args>
|
||||
using EnableIfFnMatchesInvocable =
|
||||
std::enable_if_t<std::is_invocable_v<Fn, Args...> &&
|
||||
std::is_same_v<R, std::invoke_result_t<Fn, Args...>>, int>;
|
||||
#else
|
||||
template<typename Fn, typename R, typename... Args>
|
||||
using EnableIfFnMatchesInvocable = std::enable_if_t<true, int>;
|
||||
#endif
|
||||
|
||||
template<typename Signature>
|
||||
class Invocable;
|
||||
|
||||
template<typename R, typename... Args>
|
||||
class Invocable<R(Args...)> {
|
||||
public:
|
||||
// Creates an Invocable that does not contain a functor.
|
||||
// Will evaluate to false.
|
||||
Invocable() = default;
|
||||
|
||||
~Invocable() noexcept;
|
||||
|
||||
// Creates an Invocable from the functor passed in.
|
||||
template<typename Fn, EnableIfFnMatchesInvocable<Fn, R, Args...> = 0>
|
||||
Invocable(Fn&& fn) noexcept; // NOLINT(google-explicit-constructor)
|
||||
|
||||
Invocable(const Invocable&) = delete;
|
||||
Invocable(Invocable&& rhs) noexcept;
|
||||
|
||||
Invocable& operator=(const Invocable&) = delete;
|
||||
Invocable& operator=(Invocable&& rhs) noexcept;
|
||||
|
||||
// Invokes the invocable with the args passed in.
|
||||
// If the Invocable is empty, this will assert.
|
||||
template<typename... OperatorArgs>
|
||||
R operator()(OperatorArgs&& ... args);
|
||||
template<typename... OperatorArgs>
|
||||
R operator()(OperatorArgs&& ... args) const;
|
||||
|
||||
// Evaluates to true if Invocable contains a functor, false otherwise.
|
||||
explicit operator bool() const noexcept;
|
||||
|
||||
private:
|
||||
void* mInvocable = nullptr;
|
||||
void (*mDeleter)(void*) = nullptr;
|
||||
R (* mInvoker)(void*, Args...) = nullptr;
|
||||
};
|
||||
|
||||
template<typename R, typename... Args>
|
||||
template<typename Fn, EnableIfFnMatchesInvocable<Fn, R, Args...>>
|
||||
Invocable<R(Args...)>::Invocable(Fn&& fn) noexcept
|
||||
: mInvocable(new Fn(std::forward<std::decay_t<Fn>>(fn))),
|
||||
mDeleter(+[](void* erased_invocable) {
|
||||
auto typed_invocable = static_cast<Fn*>(erased_invocable);
|
||||
delete typed_invocable;
|
||||
}),
|
||||
mInvoker(+[](void* erased_invocable, Args... args) -> R {
|
||||
auto typed_invocable = static_cast<Fn*>(erased_invocable);
|
||||
return (*typed_invocable)(std::forward<Args>(args)...);
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
template<typename R, typename... Args>
|
||||
Invocable<R(Args...)>::~Invocable() noexcept {
|
||||
if (mDeleter) {
|
||||
mDeleter(mInvocable);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename R, typename... Args>
|
||||
Invocable<R(Args...)>::Invocable(Invocable&& rhs) noexcept
|
||||
: mInvocable(rhs.mInvocable),
|
||||
mDeleter(rhs.mDeleter),
|
||||
mInvoker(rhs.mInvoker) {
|
||||
rhs.mInvocable = nullptr;
|
||||
rhs.mDeleter = nullptr;
|
||||
rhs.mInvoker = nullptr;
|
||||
}
|
||||
|
||||
template<typename R, typename... Args>
|
||||
Invocable<R(Args...)>& Invocable<R(Args...)>::operator=(Invocable&& rhs) noexcept {
|
||||
if (this != &rhs) {
|
||||
std::swap(mInvocable, rhs.mInvocable);
|
||||
std::swap(mDeleter, rhs.mDeleter);
|
||||
std::swap(mInvoker, rhs.mInvoker);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename R, typename... Args>
|
||||
template<typename... OperatorArgs>
|
||||
R Invocable<R(Args...)>::operator()(OperatorArgs&& ... args) {
|
||||
assert(mInvoker && mInvocable);
|
||||
return mInvoker(mInvocable, std::forward<OperatorArgs>(args)...);
|
||||
}
|
||||
|
||||
template<typename R, typename... Args>
|
||||
template<typename... OperatorArgs>
|
||||
R Invocable<R(Args...)>::operator()(OperatorArgs&& ... args) const {
|
||||
assert(mInvoker && mInvocable);
|
||||
return mInvoker(mInvocable, std::forward<OperatorArgs>(args)...);
|
||||
}
|
||||
|
||||
template<typename R, typename... Args>
|
||||
Invocable<R(Args...)>::operator bool() const noexcept {
|
||||
return mInvoker != nullptr && mInvocable != nullptr;
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
|
||||
#endif // TNT_UTILS_INVOKABLE_H
|
||||
46
windows/include/filament/utils/Log.h
Normal file
46
windows/include/filament/utils/Log.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_UTILS_LOG_H
|
||||
#define TNT_UTILS_LOG_H
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
namespace utils {
|
||||
|
||||
struct UTILS_PUBLIC Loggers {
|
||||
// DEBUG level logging stream
|
||||
io::ostream& d;
|
||||
|
||||
// ERROR level logging stream
|
||||
io::ostream& e;
|
||||
|
||||
// WARNING level logging stream
|
||||
io::ostream& w;
|
||||
|
||||
// INFORMATION level logging stream
|
||||
io::ostream& i;
|
||||
|
||||
// VERBOSE level logging stream
|
||||
io::ostream& v;
|
||||
};
|
||||
|
||||
extern UTILS_PUBLIC Loggers const slog;
|
||||
|
||||
} // namespace utils
|
||||
|
||||
#endif // TNT_UTILS_LOG_H
|
||||
26
windows/include/filament/utils/Mutex.h
Normal file
26
windows/include/filament/utils/Mutex.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_UTILS_MUTEX_H
|
||||
#define TNT_UTILS_MUTEX_H
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include <utils/linux/Mutex.h>
|
||||
#else
|
||||
#include <utils/generic/Mutex.h>
|
||||
#endif
|
||||
|
||||
#endif // TNT_UTILS_MUTEX_H
|
||||
112
windows/include/filament/utils/NameComponentManager.h
Normal file
112
windows/include/filament/utils/NameComponentManager.h
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_UTILS_NAMECOMPONENTMANAGER_H
|
||||
#define TNT_UTILS_NAMECOMPONENTMANAGER_H
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/CString.h>
|
||||
#include <utils/Entity.h>
|
||||
#include <utils/EntityInstance.h>
|
||||
#include <utils/SingleInstanceComponentManager.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace utils {
|
||||
|
||||
class EntityManager;
|
||||
|
||||
/**
|
||||
* \class NameComponentManager NameComponentManager.h utils/NameComponentManager.h
|
||||
* \brief Allows clients to associate string labels with entities.
|
||||
*
|
||||
* To access the name of an existing entity, clients should first use NameComponentManager to get a
|
||||
* temporary handle called an \em instance. Please note that instances are ephemeral; clients should
|
||||
* store entities, not instances.
|
||||
*
|
||||
* Usage example:
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
* auto names = new NameComponentManager(EntityManager::get());
|
||||
* names->addComponent(myEntity);
|
||||
* names->setName(names->getInstance(myEntity), "Jeanne d'Arc");
|
||||
* ...
|
||||
* printf("%s\n", names->getName(names->getInstance(myEntity));
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
class UTILS_PUBLIC NameComponentManager : public SingleInstanceComponentManager<utils::CString> {
|
||||
public:
|
||||
using Instance = EntityInstance<NameComponentManager>;
|
||||
|
||||
/**
|
||||
* Creates a new name manager associated with the given entity manager.
|
||||
*
|
||||
* Note that multiple string labels could be associated with each entity simply by
|
||||
* creating multiple instances of NameComponentManager.
|
||||
*/
|
||||
explicit NameComponentManager(EntityManager& em);
|
||||
~NameComponentManager();
|
||||
|
||||
/**
|
||||
* Checks if the given entity already has a name component.
|
||||
*/
|
||||
using SingleInstanceComponentManager::hasComponent;
|
||||
|
||||
/**
|
||||
* Gets a temporary handle that can be used to access the name.
|
||||
*
|
||||
* @return Non-zero handle if the entity has a name component, 0 otherwise.
|
||||
*/
|
||||
Instance getInstance(Entity e) const noexcept {
|
||||
return { SingleInstanceComponentManager::getInstance(e) };
|
||||
}
|
||||
|
||||
/*! \cond PRIVATE */
|
||||
// these are implemented in SingleInstanceComponentManager<>, but we need to
|
||||
// reimplement them in each manager, to ensure they are generated in an implementation file
|
||||
// for backward binary compatibility reasons.
|
||||
size_t getComponentCount() const noexcept;
|
||||
Entity const* getEntities() const noexcept;
|
||||
void gc(const EntityManager& em, size_t ratio = 4) noexcept;
|
||||
/*! \endcond */
|
||||
|
||||
/**
|
||||
* Adds a name component to the given entity if it doesn't already exist.
|
||||
*/
|
||||
void addComponent(Entity e);
|
||||
|
||||
/**
|
||||
* Removes the name component to the given entity if it exists.
|
||||
*/
|
||||
void removeComponent(Entity e);
|
||||
|
||||
/**
|
||||
* Stores a copy of the given string and associates it with the given instance.
|
||||
*/
|
||||
void setName(Instance instance, const char* name) noexcept;
|
||||
|
||||
/**
|
||||
* Retrieves the string associated with the given instance, or nullptr if none exists.
|
||||
*
|
||||
* @return pointer to the copy that was made during setName()
|
||||
*/
|
||||
const char* getName(Instance instance) const noexcept;
|
||||
};
|
||||
|
||||
} // namespace utils
|
||||
|
||||
#endif // TNT_UTILS_NAMECOMPONENTMANAGER_H
|
||||
563
windows/include/filament/utils/Panic.h
Normal file
563
windows/include/filament/utils/Panic.h
Normal file
@@ -0,0 +1,563 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_UTILS_PANIC_H
|
||||
#define TNT_UTILS_PANIC_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <utils/CallStack.h>
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#ifdef __EXCEPTIONS
|
||||
# define UTILS_EXCEPTIONS 1
|
||||
#else
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @defgroup errors Handling Catastrophic Failures (Panics)
|
||||
*
|
||||
* @brief Failure detection and reporting facilities
|
||||
*
|
||||
* ## What's a Panic? ##
|
||||
*
|
||||
* In the context of this document, a _panic_ is a type of error due to a _contract violation_,
|
||||
* it shouldn't be confused with a _result_ or _status_ code. The POSIX API for instance,
|
||||
* unfortunately often conflates the two.
|
||||
* @see <http://en.wikipedia.org/wiki/Design_by_contract>
|
||||
*
|
||||
*
|
||||
* Here we give the following definition of a _panic_:
|
||||
*
|
||||
* 1. Failures to meet a function's own **postconditions**\n
|
||||
* The function cannot establish one of its own postconditions, such as (but not limited to)
|
||||
* producing a valid return value object.
|
||||
*
|
||||
* Often these failures are only detectable at runtime, for instance they can be caused by
|
||||
* arithmetic errors, as it was the case for the Ariane 5 rocket. Ariane 5 crashed because it
|
||||
* reused an inertial module from Ariane 4, which didn't account for the greater horizontal
|
||||
* acceleration of Ariane 5 and caused an overflow in the computations. Ariane 4's module
|
||||
* wasn't per-say buggy, but was improperly used and failed to meet, obviously, certain
|
||||
* postconditions.
|
||||
* @see <http://en.wikipedia.org/wiki/Cluster_(spacecraft)>
|
||||
*
|
||||
* 2. Failures to meet the **preconditions** of any of a function's callees\n
|
||||
* The function cannot meet a precondition of another function it must call, such as a
|
||||
* restriction on a parameter.
|
||||
*
|
||||
* Not to be confused with the case where the preconditions of a function are already
|
||||
* violated upon entry, which indicates a programming error from the caller.
|
||||
*
|
||||
* Typically these failures can be avoided and arise because of programming errors.
|
||||
*
|
||||
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
* ## Failure reporting vs. handling ##
|
||||
*
|
||||
* Very often when a panic, as defined above, is detected, the program has little other choice
|
||||
* but to terminate.\n
|
||||
* Typically these situations can be handled by _assert()_. However, _assert()_ also conflates two
|
||||
* very different concepts: detecting and handling failures.\n
|
||||
* The place where a failure is detected is rarely the place where there is enough
|
||||
* context to decide what to do. _assert()_ terminates the program which, may or may not be
|
||||
* appropriate. At the very least the failure must be logged (which _assert()_ does in a crude way),
|
||||
* but some other actions might need to happen, such as:\n
|
||||
*
|
||||
* - logging the failure in the system-wide logger
|
||||
* - providing enough information in development builds to analyze/debug the problem
|
||||
* - cleanly releasing some resources, such as communication channels with other processes\n
|
||||
* e.g.: to avoid their pre- or postconditions from being violated as well.
|
||||
*
|
||||
* In some _rare_ cases, the failure might even be ignored altogether because it doesn't matter in
|
||||
* the context where it happened. This decision clearly doesn't always lie at the failure-site.
|
||||
*
|
||||
* It is therefore important to separate failure detection from handling.
|
||||
*
|
||||
*
|
||||
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
* ## Failure detection and handling facilities ##
|
||||
*
|
||||
* Clearly, catastrophic failures should be **rare**; in fact they should
|
||||
* never happen, except possibly for "failures to meet a function's own postconditions", which
|
||||
* may depend on external factors and should still be very rare. Yet, when a failure happens, it
|
||||
* must be detected and handled appropriately.\n
|
||||
* Since panics are rare, it is desirable that the handling mechanism be as unobtrusive
|
||||
* as possible, without allowing such failures to go unnoticed or swallowed by mistake. Ideally, the
|
||||
* programmer using an API should have nothing special to do to handle that API's failure
|
||||
* conditions.\n\n
|
||||
*
|
||||
* An important feature of the Panic Handling facilities here is that **panics are not part of
|
||||
* the API of a function or method**\n\n
|
||||
*
|
||||
*
|
||||
* The panic handling facility has the following benefits:
|
||||
* - provides an easy way to detect and report failure
|
||||
* - separates failure detection from handling
|
||||
* - makes it hard for detected failures to be ignored (i.e.: not handled)
|
||||
* - doesn't add burden on the API design
|
||||
* - doesn't add overhead (visual or otherwise) at call sites
|
||||
* - has very little performance overhead for failure detection
|
||||
* - has little to no performance impact for failure handling in the common (success) case
|
||||
* - is flexible and extensible
|
||||
*
|
||||
* Since we have established that failures are **rare**, **exceptional** situations, it would be
|
||||
* appropriate to handle them with an _assert_ mechanism and that's what the API below
|
||||
* provides. However, under-the-hood it uses C++ exceptions as a means to separate
|
||||
* _reporting_ from _handling_.
|
||||
*
|
||||
* \note On devices where exceptions are not supported or appropriate, these APIs can be turned
|
||||
* into a regular _std::terminate()_.
|
||||
*
|
||||
*
|
||||
* ASSERT_PRECONDITION(condition, format, ...)
|
||||
* ASSERT_POSTCONDITION(condition, format, ...)
|
||||
* ASSERT_ARITHMETIC(condition, format, ...)
|
||||
* ASSERT_DESTRUCTOR(condition, format, ...)
|
||||
*
|
||||
*
|
||||
* @see ASSERT_PRECONDITION, ASSERT_POSTCONDITION, ASSERT_ARITHMETIC
|
||||
* @see ASSERT_DESTRUCTOR
|
||||
*
|
||||
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
* ## Writing code that can assert ##
|
||||
*
|
||||
* Because we've separated failure reporting from failure handling, there are some considerations
|
||||
* that need to be thought about when writing code that calls the macros above (i.e.: the program
|
||||
* won't terminate at the point where the failure is detected).\n\n
|
||||
*
|
||||
*
|
||||
* ### Panic guarantees ###
|
||||
*
|
||||
* After the failure condition is reported by a function, additional guarantees may be provided
|
||||
* with regards to the state of the program. The following four levels of guarantee are
|
||||
* generally recognized, each of which is a strict superset of its successors:
|
||||
*
|
||||
* 1. Nothrow exception guarantee\n
|
||||
* The function never asserts. e.g.: This should always be the case with destructors.\n\n
|
||||
*
|
||||
* 2. Strong exception guarantee\n
|
||||
* If the function asserts, the state of the program is rolled back to the state just before
|
||||
* the function call.\n\n
|
||||
*
|
||||
* 3. Basic exception guarantee\n
|
||||
* If the function asserts, the program is in a valid state. It may require cleanup,
|
||||
* but all invariants are intact.\n\n
|
||||
*
|
||||
* 4. No exception guarantee\n
|
||||
* If the function asserts, the program may not be in a valid state: resource leaks, memory
|
||||
* corruption, or other invariant-destroying failures may have occurred.
|
||||
*
|
||||
* In each function, give the **strongest** safety guarantee that won't penalize callers who
|
||||
* don't need it, but **always give at least the basic guarantee**. The RAII (Resource
|
||||
* Acquisition Is Initialization) pattern can help with achieving these guarantees.
|
||||
*
|
||||
* @see [RAII](http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization)
|
||||
*
|
||||
* ### Special considerations for Constructors ###
|
||||
*
|
||||
* Constructors are a bit special because if a failure occurs during their execution, the
|
||||
* destructor won't be called (how could it? since the object wasn't constructed!). This can lead
|
||||
* to leaked resources allocated in the constructor prior to the failure. Thankfully there is
|
||||
* a nice C++ syntax to handle this case:
|
||||
*
|
||||
* @code
|
||||
* Foo::Foo(size_t s) try : m_size(s), m_buf(new uint32_t[s]) {
|
||||
* ASSERT_POSTCONDITION(s&0xF==0,
|
||||
* "object size is %u, but must be multiple of 16", s);
|
||||
* } catch (...) {
|
||||
* delete [] m_buf;
|
||||
* // the exception will be automatically re-thrown
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* Unfortunately, this usage leaks the underlying, exception-based, implementation of the
|
||||
* panic handling macros. For this reason, it is best to keep constructors simple and guarantee
|
||||
* they can't fail. An _init()_ function with a factory can be used for actual initialization.
|
||||
*
|
||||
*
|
||||
* ### Special considerations for Destructors ###
|
||||
*
|
||||
* In C++ destructors cannot throw exceptions and since the above macros internally use exceptions
|
||||
* they cannot be used in destructors. Doing so will result in immediate termination of the
|
||||
* program by _std::terminate()_.\n
|
||||
* It is therefore best to always guarantee that destructors won't fail. In case of such a
|
||||
* failure in a destructor the ASSERT_DESTRUCTOR() macro can be used instead, it
|
||||
* will log the failure but won't terminate the program, instead it'll proceed as if nothing
|
||||
* happened. Generally this will result in some resource leak which, eventually, will cause
|
||||
* another failure (typically a postcondition violation).\n\n
|
||||
*
|
||||
* Rationale for this behavior: There are fundamentally no way to report a failure from a
|
||||
* destructor in C++, violently terminating the process is inadequate because it again conflates
|
||||
* failure reporting and failure handling; for instance a failure in glDeleteTextures() shouldn't
|
||||
* be necessarily fatal (certainly not without saving the user's data first). The alternative
|
||||
* would be for the caller to swallow the failure entirely, but that's not great either because the
|
||||
* failure would go unnoticed. The solution retained here is a compromise.
|
||||
*
|
||||
* @see ASSERT_DESTRUCTOR
|
||||
*
|
||||
* ### Testing Code that Uses Panics ###
|
||||
*
|
||||
* Since panics use exceptions for their underlying implementation, you can test code that uses
|
||||
* panics with EXPECT_THROW by doing the following things:
|
||||
* \li Set panic mode to THROW (default is TERMINATE)
|
||||
* \li Pass Panic to EXPECT_THROW as the exception type
|
||||
*
|
||||
* Example code for your test file:
|
||||
*
|
||||
* @code
|
||||
* #include <MyClass.hpp> // since your code uses panics, this should include utils/Panic.hpp
|
||||
*
|
||||
* using utils::Panic;
|
||||
*
|
||||
* TEST(MyClassTest, value_that_causes_panic) {
|
||||
* EXPECT_THROW(MyClass::function(value_that_causes_panic), Panic);
|
||||
* }
|
||||
*
|
||||
* // ... other tests ...
|
||||
*
|
||||
* int main(int argc, char** argv) {
|
||||
* ::testing::InitGoogleTest(&argc, argv);
|
||||
* Panic::setMode(Panic::Mode::THROW);
|
||||
* return RUN_ALL_TESTS();
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
*/
|
||||
|
||||
namespace utils {
|
||||
|
||||
// -----------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @ingroup errors
|
||||
*
|
||||
* \brief Base class of all exceptions thrown by all the ASSERT macros
|
||||
*
|
||||
* The Panic class provides the std::exception protocol, it is the base exception object
|
||||
* used for all thrown exceptions.
|
||||
*/
|
||||
class UTILS_PUBLIC Panic {
|
||||
public:
|
||||
virtual ~Panic() noexcept;
|
||||
|
||||
/**
|
||||
* @return a detailed description of the error
|
||||
* @see std::exception
|
||||
*/
|
||||
virtual const char* what() const noexcept = 0;
|
||||
|
||||
/**
|
||||
* Get the function name where the panic was detected
|
||||
* @return a C string containing the function name where the panic was detected
|
||||
*/
|
||||
virtual const char* getFunction() const noexcept = 0;
|
||||
|
||||
/**
|
||||
* Get the file name where the panic was detected
|
||||
* @return a C string containing the file name where the panic was detected
|
||||
*/
|
||||
virtual const char* getFile() const noexcept = 0;
|
||||
|
||||
/**
|
||||
* Get the line number in the file where the panic was detected
|
||||
* @return an integer containing the line number in the file where the panic was detected
|
||||
*/
|
||||
virtual int getLine() const noexcept = 0;
|
||||
|
||||
/**
|
||||
* Logs this exception to the system-log
|
||||
*/
|
||||
virtual void log() const noexcept = 0;
|
||||
|
||||
/**
|
||||
* Get the CallStack when the panic was detected
|
||||
* @return the CallStack when the panic was detected
|
||||
*/
|
||||
virtual const CallStack& getCallStack() const noexcept = 0;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @ingroup errors
|
||||
*
|
||||
* \brief Concrete implementation of the Panic interface.
|
||||
*
|
||||
* The TPanic<> class implements the std::exception protocol as well as the Panic
|
||||
* interface common to all exceptions thrown by the framework.
|
||||
*/
|
||||
template <typename T>
|
||||
class UTILS_PUBLIC TPanic : public Panic {
|
||||
public:
|
||||
// std::exception protocol
|
||||
const char* what() const noexcept override;
|
||||
|
||||
// Panic interface
|
||||
const char* getFunction() const noexcept override;
|
||||
const char* getFile() const noexcept override;
|
||||
int getLine() const noexcept override;
|
||||
const CallStack& getCallStack() const noexcept override;
|
||||
void log() const noexcept override;
|
||||
|
||||
/**
|
||||
* Depending on the mode set, either throws an exception of type T with the given reason plus
|
||||
* extra information about the error-site, or logs the error and calls std::terminate().
|
||||
* This function never returns.
|
||||
* @param function the name of the function where the error was detected
|
||||
* @param file the file where the above function in implemented
|
||||
* @param line the line in the above file where the error was detected
|
||||
* @param format printf style string describing the error
|
||||
* @see ASSERT_PRECONDITION, ASSERT_POSTCONDITION, ASSERT_ARITHMETIC
|
||||
* @see PANIC_PRECONDITION, PANIC_POSTCONDITION, PANIC_ARITHMETIC
|
||||
* @see setMode()
|
||||
*/
|
||||
static void panic(char const* function, char const* file, int line, const char* format, ...)
|
||||
UTILS_NORETURN;
|
||||
|
||||
/**
|
||||
* Depending on the mode set, either throws an exception of type T with the given reason plus
|
||||
* extra information about the error-site, or logs the error and calls std::terminate().
|
||||
* This function never returns.
|
||||
* @param function the name of the function where the error was detected
|
||||
* @param file the file where the above function in implemented
|
||||
* @param line the line in the above file where the error was detected
|
||||
* @param s std::string describing the error
|
||||
* @see ASSERT_PRECONDITION, ASSERT_POSTCONDITION, ASSERT_ARITHMETIC
|
||||
* @see PANIC_PRECONDITION, PANIC_POSTCONDITION, PANIC_ARITHMETIC
|
||||
* @see setMode()
|
||||
*/
|
||||
static inline void panic(char const* function, char const* file, int line, const std::string& s)
|
||||
UTILS_NORETURN {
|
||||
panic(function, file, line, s.c_str());
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Creates a Panic.
|
||||
* @param reason a description of the cause of the error
|
||||
*/
|
||||
explicit TPanic(std::string reason);
|
||||
|
||||
/**
|
||||
* Creates a Panic with extra information about the error-site.
|
||||
* @param function the name of the function where the error was detected
|
||||
* @param file the file where the above function in implemented
|
||||
* @param line the line in the above file where the error was detected
|
||||
* @param reason a description of the cause of the error
|
||||
*/
|
||||
TPanic(char const* function, char const* file, int line, std::string reason);
|
||||
|
||||
~TPanic() override;
|
||||
|
||||
private:
|
||||
void buildMessage();
|
||||
|
||||
CallStack m_callstack;
|
||||
std::string m_reason;
|
||||
char const* const m_function = nullptr;
|
||||
char const* const m_file = nullptr;
|
||||
const int m_line = -1;
|
||||
mutable std::string m_msg;
|
||||
};
|
||||
|
||||
namespace details {
|
||||
// these are private, don't use
|
||||
void panicLog(
|
||||
char const* function, char const* file, int line, const char* format, ...) noexcept;
|
||||
} // namespace details
|
||||
|
||||
// -----------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @ingroup errors
|
||||
*
|
||||
* ASSERT_PRECONDITION uses this Panic to report a precondition failure.
|
||||
* @see ASSERT_PRECONDITION
|
||||
*/
|
||||
class UTILS_PUBLIC PreconditionPanic : public TPanic<PreconditionPanic> {
|
||||
// Programming error, can be avoided
|
||||
// e.g.: invalid arguments
|
||||
using TPanic<PreconditionPanic>::TPanic;
|
||||
friend class TPanic<PreconditionPanic>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup errors
|
||||
*
|
||||
* ASSERT_POSTCONDITION uses this Panic to report a postcondition failure.
|
||||
* @see ASSERT_POSTCONDITION
|
||||
*/
|
||||
class UTILS_PUBLIC PostconditionPanic : public TPanic<PostconditionPanic> {
|
||||
// Usually only detectable at runtime
|
||||
// e.g.: dead-lock would occur, arithmetic errors
|
||||
using TPanic<PostconditionPanic>::TPanic;
|
||||
friend class TPanic<PostconditionPanic>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup errors
|
||||
*
|
||||
* ASSERT_ARITHMETIC uses this Panic to report an arithmetic (postcondition) failure.
|
||||
* @see ASSERT_ARITHMETIC
|
||||
*/
|
||||
class UTILS_PUBLIC ArithmeticPanic : public TPanic<ArithmeticPanic> {
|
||||
// A common case of post-condition error
|
||||
// e.g.: underflow, overflow, internal computations errors
|
||||
using TPanic<ArithmeticPanic>::TPanic;
|
||||
friend class TPanic<ArithmeticPanic>;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------------------------
|
||||
} // namespace utils
|
||||
|
||||
#ifndef NDEBUG
|
||||
# define PANIC_FILE(F) (F)
|
||||
# define PANIC_FUNCTION __PRETTY_FUNCTION__
|
||||
#else
|
||||
# define PANIC_FILE(F) ""
|
||||
# define PANIC_FUNCTION __func__
|
||||
#endif
|
||||
|
||||
/**
|
||||
* PANIC_PRECONDITION is a macro that reports a PreconditionPanic
|
||||
* @param format printf-style string describing the error in more details
|
||||
*/
|
||||
#define PANIC_PRECONDITION(format, ...) \
|
||||
::utils::PreconditionPanic::panic(PANIC_FUNCTION, \
|
||||
PANIC_FILE(__FILE__), __LINE__, format, ##__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* PANIC_POSTCONDITION is a macro that reports a PostconditionPanic
|
||||
* @param format printf-style string describing the error in more details
|
||||
*/
|
||||
#define PANIC_POSTCONDITION(format, ...) \
|
||||
::utils::PostconditionPanic::panic(PANIC_FUNCTION, \
|
||||
PANIC_FILE(__FILE__), __LINE__, format, ##__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* PANIC_ARITHMETIC is a macro that reports a ArithmeticPanic
|
||||
* @param format printf-style string describing the error in more details
|
||||
*/
|
||||
#define PANIC_ARITHMETIC(format, ...) \
|
||||
::utils::ArithmeticPanic::panic(PANIC_FUNCTION, \
|
||||
PANIC_FILE(__FILE__), __LINE__, format, ##__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* PANIC_LOG is a macro that logs a Panic, and continues as usual.
|
||||
* @param format printf-style string describing the error in more details
|
||||
*/
|
||||
#define PANIC_LOG(format, ...) \
|
||||
::utils::details::panicLog(PANIC_FUNCTION, \
|
||||
PANIC_FILE(__FILE__), __LINE__, format, ##__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @ingroup errors
|
||||
*
|
||||
* ASSERT_PRECONDITION is a macro that checks the given condition and reports a PreconditionPanic
|
||||
* if it evaluates to false.
|
||||
* @param cond a boolean expression
|
||||
* @param format printf-style string describing the error in more details
|
||||
*/
|
||||
#define ASSERT_PRECONDITION(cond, format, ...) \
|
||||
(!UTILS_LIKELY(cond) ? PANIC_PRECONDITION(format, ##__VA_ARGS__) : (void)0)
|
||||
|
||||
#if defined(UTILS_EXCEPTIONS) || !defined(NDEBUG)
|
||||
#define ASSERT_PRECONDITION_NON_FATAL(cond, format, ...) \
|
||||
(!UTILS_LIKELY(cond) ? PANIC_PRECONDITION(format, ##__VA_ARGS__), false : true)
|
||||
#else
|
||||
#define ASSERT_PRECONDITION_NON_FATAL(cond, format, ...) \
|
||||
(!UTILS_LIKELY(cond) ? PANIC_LOG(format, ##__VA_ARGS__), false : true)
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* @ingroup errors
|
||||
*
|
||||
* ASSERT_POSTCONDITION is a macro that checks the given condition and reports a PostconditionPanic
|
||||
* if it evaluates to false.
|
||||
* @param cond a boolean expression
|
||||
* @param format printf-style string describing the error in more details
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* int& Foo::operator[](size_t index) {
|
||||
* ASSERT_POSTCONDITION(index<m_size, "cannot produce a valid return value");
|
||||
* return m_array[index];
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
#define ASSERT_POSTCONDITION(cond, format, ...) \
|
||||
(!UTILS_LIKELY(cond) ? PANIC_POSTCONDITION(format, ##__VA_ARGS__) : (void)0)
|
||||
|
||||
#if defined(UTILS_EXCEPTIONS) || !defined(NDEBUG)
|
||||
#define ASSERT_POSTCONDITION_NON_FATAL(cond, format, ...) \
|
||||
(!UTILS_LIKELY(cond) ? PANIC_POSTCONDITION(format, ##__VA_ARGS__), false : true)
|
||||
#else
|
||||
#define ASSERT_POSTCONDITION_NON_FATAL(cond, format, ...) \
|
||||
(!UTILS_LIKELY(cond) ? PANIC_LOG(format, ##__VA_ARGS__), false : true)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @ingroup errors
|
||||
*
|
||||
* ASSERT_ARITHMETIC is a macro that checks the given condition and reports a ArithmeticPanic
|
||||
* if it evaluates to false.
|
||||
* @param cond a boolean expression
|
||||
* @param format printf-style string describing the error in more details
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* unt32_t floatToUInt1616(float v) {
|
||||
* v *= 65536;
|
||||
* ASSERT_ARITHMETIC(v>=0 && v<65536, "overflow occurred");
|
||||
* return uint32_t(v);
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
#define ASSERT_ARITHMETIC(cond, format, ...) \
|
||||
(!(cond) ? PANIC_ARITHMETIC(format, ##__VA_ARGS__) : (void)0)
|
||||
|
||||
#if defined(UTILS_EXCEPTIONS) || !defined(NDEBUG)
|
||||
#define ASSERT_ARITHMETIC_NON_FATAL(cond, format, ...) \
|
||||
(!UTILS_LIKELY(cond) ? PANIC_ARITHMETIC(format, ##__VA_ARGS__), false : true)
|
||||
#else
|
||||
#define ASSERT_ARITHMETIC_NON_FATAL(cond, format, ...) \
|
||||
(!UTILS_LIKELY(cond) ? PANIC_LOG(format, ##__VA_ARGS__), false : true)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @ingroup errors
|
||||
*
|
||||
* ASSERT_DESTRUCTOR is a macro that checks the given condition and logs an error
|
||||
* if it evaluates to false.
|
||||
* @param cond a boolean expression
|
||||
* @param format printf-style string describing the error in more details
|
||||
*
|
||||
* @warning Use this macro if a destructor can fail, which should be avoided at all costs.
|
||||
* Unlike the other ASSERT macros, this will never result in the process termination. Instead,
|
||||
* the error will be logged and the program will continue as if nothing happened.
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* Foo::~Foo() {
|
||||
* glDeleteTextures(1, &m_texture);
|
||||
* GLint err = glGetError();
|
||||
* ASSERT_DESTRUCTOR(err == GL_NO_ERROR, "cannot free GL resource!");
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
#define ASSERT_DESTRUCTOR(cond, format, ...) (!(cond) ? PANIC_LOG(format, ##__VA_ARGS__) : (void)0)
|
||||
|
||||
#endif // TNT_UTILS_PANIC_H
|
||||
297
windows/include/filament/utils/Path.h
Normal file
297
windows/include/filament/utils/Path.h
Normal file
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_UTILS_PATH_H
|
||||
#define TNT_UTILS_PATH_H
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace utils {
|
||||
|
||||
/**
|
||||
* An abstract representation of file and directory paths.
|
||||
*/
|
||||
class UTILS_PUBLIC Path {
|
||||
public:
|
||||
/**
|
||||
* Creates a new empty path.
|
||||
*/
|
||||
Path() = default;
|
||||
~Path() = default;
|
||||
|
||||
/**
|
||||
* Creates a new path with the specified pathname.
|
||||
*
|
||||
* @param pathname a non-null pathname string
|
||||
*/
|
||||
Path(const char* pathname);
|
||||
|
||||
/**
|
||||
* Creates a new path with the specified pathname.
|
||||
*
|
||||
* @param pathname a pathname string view
|
||||
*/
|
||||
Path(std::string_view pathname);
|
||||
|
||||
/**
|
||||
* Creates a new path with the specified pathname.
|
||||
*
|
||||
* @param pathname a pathname string
|
||||
*/
|
||||
Path(const std::string& pathname);
|
||||
|
||||
/**
|
||||
* Tests whether the file or directory denoted by this abstract
|
||||
* pathname exists.
|
||||
*
|
||||
* @return true if the file or directory denoted by this
|
||||
* abstract pathname exists, false otherwise
|
||||
*/
|
||||
bool exists() const;
|
||||
|
||||
/**
|
||||
* Tests whether this abstract pathname represents a regular file.
|
||||
* This method can only return true if the path exists.
|
||||
*
|
||||
* @return true if this pathname represents an existing file,
|
||||
* false if the path doesn't exist or represents something
|
||||
* else (directory, symlink, etc.)
|
||||
*/
|
||||
bool isFile() const;
|
||||
|
||||
/**
|
||||
* Tests whether this abstract pathname represents a directory.
|
||||
* This method can only return true if the path exists.
|
||||
*
|
||||
* @return true if this pathname represents an existing directory,
|
||||
* false if the path doesn't exist or represents a file
|
||||
*/
|
||||
bool isDirectory() const;
|
||||
|
||||
/**
|
||||
* Tests whether this path is empty. An empty path does not
|
||||
* exist.
|
||||
*
|
||||
* @return true if the underlying abstract pathname is empty,
|
||||
* false otherwise
|
||||
*/
|
||||
bool isEmpty() const { return m_path.empty(); }
|
||||
|
||||
const char* c_str() const { return m_path.c_str(); }
|
||||
|
||||
/**
|
||||
* Replaces the abstract pathname of this object with the
|
||||
* specified pathname.
|
||||
*
|
||||
* @param pathname a pathname string
|
||||
*/
|
||||
void setPath(const std::string& pathname) {
|
||||
m_path = getCanonicalPath(pathname);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the canonical pathname this path represents
|
||||
*/
|
||||
const std::string& getPath() const { return m_path; }
|
||||
|
||||
/**
|
||||
* Returns the parent of this path as Path.
|
||||
* @return a new path containing the parent of this path
|
||||
*/
|
||||
Path getParent() const;
|
||||
|
||||
/**
|
||||
* Returns ancestor path where "0" is the immediate parent.
|
||||
* @return a new path containing the ancestor of this path
|
||||
*/
|
||||
Path getAncestor(int n) const;
|
||||
|
||||
/**
|
||||
* Returns the name of the file or directory represented by
|
||||
* this abstract pathname.
|
||||
*
|
||||
* @return the name of the file or directory represented by
|
||||
* this abstract pathname, or an empty string if
|
||||
* this path is empty
|
||||
*/
|
||||
std::string getName() const;
|
||||
|
||||
/**
|
||||
* Returns the name of the file or directory represented by
|
||||
* this abstract pathname without its extension.
|
||||
*
|
||||
* @return the name of the file or directory represented by
|
||||
* this abstract pathname, or an empty string if
|
||||
* this path is empty
|
||||
*/
|
||||
std::string getNameWithoutExtension() const;
|
||||
|
||||
/**
|
||||
* Returns the file extension (after the ".") if one is present.
|
||||
* Returns the empty string if no filename is present or if the
|
||||
* path is a directory.
|
||||
*
|
||||
* @return the file extension (if one is present and
|
||||
* this is not a directory), else the empty string.
|
||||
*/
|
||||
std::string getExtension() const;
|
||||
|
||||
/**
|
||||
* Returns the absolute representation of this path.
|
||||
* If this path's pathname starts with a leading '/',
|
||||
* the returned path is equal to this path. Otherwise,
|
||||
* this path's pathname is concatenated with the current
|
||||
* working directory and the result is returned.
|
||||
*
|
||||
* @return a new path containing the absolute representation
|
||||
* of this path
|
||||
*/
|
||||
Path getAbsolutePath() const;
|
||||
|
||||
/**
|
||||
* @return true if this abstract pathname is not empty
|
||||
* and starts with a leading '/', false otherwise
|
||||
*/
|
||||
bool isAbsolute() const;
|
||||
|
||||
/**
|
||||
* Splits this object's abstract pathname in a vector of file
|
||||
* and directory name. If the underlying abstract pathname
|
||||
* starts with a '/', the returned vector's first element
|
||||
* will be the string "/".
|
||||
*
|
||||
* @return a vector of strings, empty if this path is empty
|
||||
*/
|
||||
std::vector<std::string> split() const;
|
||||
|
||||
/**
|
||||
* Concatenates the specified path with this path in a new
|
||||
* path object.
|
||||
*
|
||||
* @note if the pathname to concatenate with starts with
|
||||
* a leading '/' then that pathname is returned without
|
||||
* being concatenated to this object's pathname.
|
||||
*
|
||||
* @param path the path to concatenate with
|
||||
*
|
||||
* @return the concatenation of the two paths
|
||||
*/
|
||||
Path concat(const Path& path) const;
|
||||
|
||||
/**
|
||||
* Concatenates the specified path with this path and
|
||||
* stores the result in this path.
|
||||
*
|
||||
* @note if the pathname to concatenate with starts with
|
||||
* a leading '/' then that pathname replaces this object's
|
||||
* pathname.
|
||||
*
|
||||
* @param path the path to concatenate with
|
||||
*/
|
||||
void concatToSelf(const Path& path);
|
||||
|
||||
operator std::string const&() const { return m_path; }
|
||||
|
||||
Path operator+(const Path& rhs) const { return concat(rhs); }
|
||||
Path& operator+=(const Path& rhs) {
|
||||
concatToSelf(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const Path& rhs) const { return m_path == rhs.m_path; }
|
||||
bool operator!=(const Path& rhs) const { return m_path != rhs.m_path; }
|
||||
|
||||
bool operator<(const Path& rhs) const { return m_path < rhs.m_path; }
|
||||
bool operator>(const Path& rhs) const { return m_path > rhs.m_path; }
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const Path& path);
|
||||
|
||||
/**
|
||||
* Returns a canonical copy of the specified pathname by removing
|
||||
* unnecessary path segments such as ".", ".." and "/".
|
||||
*
|
||||
* @param pathname a pathname string
|
||||
*
|
||||
* @return the canonical representation of the specified pathname
|
||||
*/
|
||||
static std::string getCanonicalPath(const std::string& pathname);
|
||||
|
||||
/**
|
||||
* This method is equivalent to calling root.concat(leaf).
|
||||
*/
|
||||
static Path concat(const std::string& root, const std::string& leaf);
|
||||
|
||||
/**
|
||||
* @return a path representing the current working directory
|
||||
*/
|
||||
static Path getCurrentDirectory();
|
||||
|
||||
/**
|
||||
* @return a path representing the current executable
|
||||
*/
|
||||
static Path getCurrentExecutable();
|
||||
|
||||
/**
|
||||
* @return a path representing a directory where temporary files can be stored
|
||||
*/
|
||||
static Path getTemporaryDirectory();
|
||||
|
||||
/**
|
||||
* Creates a directory denoted by the given path.
|
||||
* This is not recursive and doesn't create intermediate directories.
|
||||
*
|
||||
* @return True if directory was successfully created.
|
||||
* When false, errno should have details on actual error.
|
||||
*/
|
||||
bool mkdir() const;
|
||||
|
||||
/**
|
||||
* Creates a directory denoted by the given path.
|
||||
* This is recursive and parent directories will be created if they do not
|
||||
* exist.
|
||||
*
|
||||
* @return True if directory was successfully created or already exists.
|
||||
* When false, errno should have details on actual error.
|
||||
*/
|
||||
bool mkdirRecursive() const;
|
||||
|
||||
/**
|
||||
* Deletes this file.
|
||||
*
|
||||
* @return True if file was successfully deleted.
|
||||
* When false, errno should have details on actual error.
|
||||
*/
|
||||
bool unlinkFile();
|
||||
|
||||
/**
|
||||
* Lists the contents of this directory, skipping hidden files.
|
||||
*
|
||||
* @return A vector of paths of the contents of the directory. If the path points to a file,
|
||||
* nonexistent directory, or empty directory, an empty vector is returned.
|
||||
*/
|
||||
std::vector<Path> listContents() const;
|
||||
|
||||
private:
|
||||
std::string m_path;
|
||||
};
|
||||
|
||||
} // namespace utils
|
||||
|
||||
#endif // TNT_UTILS_PATH_H
|
||||
66
windows/include/filament/utils/PrivateImplementation-impl.h
Normal file
66
windows/include/filament/utils/PrivateImplementation-impl.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef UTILS_PRIVATEIMPLEMENTATION_IMPL_H
|
||||
#define UTILS_PRIVATEIMPLEMENTATION_IMPL_H
|
||||
|
||||
/*
|
||||
* This looks like a header file, but really it acts as a .cpp, because it's used to
|
||||
* explicitly instantiate the methods of PrivateImplementation<>
|
||||
*/
|
||||
|
||||
#include <utils/PrivateImplementation.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace utils {
|
||||
|
||||
template<typename T>
|
||||
PrivateImplementation<T>::PrivateImplementation() noexcept
|
||||
: mImpl(new T) {
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename ... ARGS>
|
||||
PrivateImplementation<T>::PrivateImplementation(ARGS&& ... args) noexcept
|
||||
: mImpl(new T(std::forward<ARGS>(args)...)) {
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
PrivateImplementation<T>::~PrivateImplementation() noexcept {
|
||||
delete mImpl;
|
||||
}
|
||||
|
||||
#ifndef UTILS_PRIVATE_IMPLEMENTATION_NON_COPYABLE
|
||||
|
||||
template<typename T>
|
||||
PrivateImplementation<T>::PrivateImplementation(PrivateImplementation const& rhs) noexcept
|
||||
: mImpl(new T(*rhs.mImpl)) {
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
PrivateImplementation<T>& PrivateImplementation<T>::operator=(PrivateImplementation<T> const& rhs) noexcept {
|
||||
if (this != &rhs) {
|
||||
*mImpl = *rhs.mImpl;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace utils
|
||||
|
||||
#endif // UTILS_PRIVATEIMPLEMENTATION_IMPL_H
|
||||
61
windows/include/filament/utils/PrivateImplementation.h
Normal file
61
windows/include/filament/utils/PrivateImplementation.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef UTILS_PRIVATEIMPLEMENTATION_H
|
||||
#define UTILS_PRIVATEIMPLEMENTATION_H
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace utils {
|
||||
|
||||
/**
|
||||
* \privatesection
|
||||
* PrivateImplementation is used to hide the implementation details of a class and ensure a higher
|
||||
* level of backward binary compatibility.
|
||||
* The actual implementation is in src/PrivateImplementation-impl.h"
|
||||
*/
|
||||
template<typename T>
|
||||
class PrivateImplementation {
|
||||
public:
|
||||
// none of these methods must be implemented inline because it's important that their
|
||||
// implementation be hidden from the public headers.
|
||||
template<typename ... ARGS>
|
||||
explicit PrivateImplementation(ARGS&& ...) noexcept;
|
||||
PrivateImplementation() noexcept;
|
||||
~PrivateImplementation() noexcept;
|
||||
PrivateImplementation(PrivateImplementation const& rhs) noexcept;
|
||||
PrivateImplementation& operator = (PrivateImplementation const& rhs) noexcept;
|
||||
|
||||
// move ctor and copy operator can be implemented inline and don't need to be exported
|
||||
PrivateImplementation(PrivateImplementation&& rhs) noexcept : mImpl(rhs.mImpl) { rhs.mImpl = nullptr; }
|
||||
PrivateImplementation& operator = (PrivateImplementation&& rhs) noexcept {
|
||||
auto temp = mImpl;
|
||||
mImpl = rhs.mImpl;
|
||||
rhs.mImpl = temp;
|
||||
return *this;
|
||||
}
|
||||
|
||||
protected:
|
||||
T* mImpl = nullptr;
|
||||
inline T* operator->() noexcept { return mImpl; }
|
||||
inline T const* operator->() const noexcept { return mImpl; }
|
||||
};
|
||||
|
||||
} // namespace utils
|
||||
|
||||
#endif // UTILS_PRIVATEIMPLEMENTATION_H
|
||||
318
windows/include/filament/utils/SingleInstanceComponentManager.h
Normal file
318
windows/include/filament/utils/SingleInstanceComponentManager.h
Normal file
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_UTILS_SINGLEINSTANCECOMPONENTMANAGER_H
|
||||
#define TNT_UTILS_SINGLEINSTANCECOMPONENTMANAGER_H
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Entity.h>
|
||||
#include <utils/EntityInstance.h>
|
||||
#include <utils/EntityManager.h>
|
||||
#include <utils/StructureOfArrays.h>
|
||||
|
||||
#include <tsl/robin_map.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace utils {
|
||||
|
||||
class EntityManager;
|
||||
|
||||
/*
|
||||
* Helper class to create single instance component managers.
|
||||
*
|
||||
* This handles the component's storage as a structure-of-arrays, as well
|
||||
* as the garbage collection.
|
||||
*
|
||||
* This is intended to be used as base class for a real component manager. When doing so,
|
||||
* and the real component manager is a public API, make sure to forward the public methods
|
||||
* to the implementation.
|
||||
*
|
||||
*/
|
||||
template <typename ... Elements>
|
||||
class UTILS_PUBLIC SingleInstanceComponentManager {
|
||||
private:
|
||||
|
||||
// this is just to avoid using std::default_random_engine, since we're in a public header.
|
||||
class default_random_engine {
|
||||
uint32_t mState = 1u; // must be 0 < seed < 0x7fffffff
|
||||
public:
|
||||
inline uint32_t operator()() noexcept {
|
||||
return mState = uint32_t((uint64_t(mState) * 48271u) % 0x7fffffffu);
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
static constexpr size_t ENTITY_INDEX = sizeof ... (Elements);
|
||||
|
||||
|
||||
public:
|
||||
using SoA = StructureOfArrays<Elements ..., Entity>;
|
||||
|
||||
using Structure = typename SoA::Structure;
|
||||
|
||||
using Instance = EntityInstanceBase::Type;
|
||||
|
||||
SingleInstanceComponentManager() noexcept {
|
||||
// We always start with a dummy entry because index=0 is reserved. The component
|
||||
// at index = 0, is guaranteed to be default-initialized.
|
||||
// Sub-classes can use this to their advantage.
|
||||
mData.push_back(Structure{});
|
||||
}
|
||||
|
||||
SingleInstanceComponentManager(SingleInstanceComponentManager&&) noexcept {/* = default */}
|
||||
SingleInstanceComponentManager& operator=(SingleInstanceComponentManager&&) noexcept {/* = default */}
|
||||
~SingleInstanceComponentManager() noexcept = default;
|
||||
|
||||
// not copyable
|
||||
SingleInstanceComponentManager(SingleInstanceComponentManager const& rhs) = delete;
|
||||
SingleInstanceComponentManager& operator=(SingleInstanceComponentManager const& rhs) = delete;
|
||||
|
||||
|
||||
// returns true if the given Entity has a component of this Manager
|
||||
bool hasComponent(Entity e) const noexcept {
|
||||
return getInstance(e) != 0;
|
||||
}
|
||||
|
||||
// Get instance of this Entity to be used to retrieve components
|
||||
UTILS_NOINLINE
|
||||
Instance getInstance(Entity e) const noexcept {
|
||||
auto const& map = mInstanceMap;
|
||||
// find() generates quite a bit of code
|
||||
auto pos = map.find(e);
|
||||
return pos != map.end() ? pos->second : 0;
|
||||
}
|
||||
|
||||
// returns the number of components (i.e. size of each arrays)
|
||||
size_t getComponentCount() const noexcept {
|
||||
// The array as an extra dummy component at index 0, so the visible count is 1 less.
|
||||
return mData.size() - 1;
|
||||
}
|
||||
|
||||
bool empty() const noexcept {
|
||||
return getComponentCount() == 0;
|
||||
}
|
||||
|
||||
// returns a pointer to the Entity array. This is basically the list
|
||||
// of entities this component manager handles.
|
||||
// The pointer becomes invalid when adding or removing a component.
|
||||
Entity const* getEntities() const noexcept {
|
||||
return begin<ENTITY_INDEX>();
|
||||
}
|
||||
|
||||
Entity getEntity(Instance i) const noexcept {
|
||||
return elementAt<ENTITY_INDEX>(i);
|
||||
}
|
||||
|
||||
// Add a component to the given Entity. If the entity already has a component from this
|
||||
// manager, this function is a no-op.
|
||||
// This invalidates all pointers components.
|
||||
inline Instance addComponent(Entity e);
|
||||
|
||||
// Removes a component from the given entity.
|
||||
// This invalidates all pointers components.
|
||||
inline Instance removeComponent(Entity e);
|
||||
|
||||
// trigger one round of garbage collection. this is intended to be called on a regular
|
||||
// basis. This gc gives up after it cannot randomly free 'ratio' component in a row.
|
||||
void gc(const EntityManager& em, size_t ratio = 4) noexcept {
|
||||
gc(em, ratio, [this](Entity e) {
|
||||
removeComponent(e);
|
||||
});
|
||||
}
|
||||
|
||||
// return the first instance
|
||||
Instance begin() const noexcept { return 1u; }
|
||||
|
||||
// return the past-the-last instance
|
||||
Instance end() const noexcept { return Instance(begin() + getComponentCount()); }
|
||||
|
||||
// return a pointer to the first element of the ElementIndex'th array
|
||||
template<size_t ElementIndex>
|
||||
typename SoA::template TypeAt<ElementIndex>* begin() noexcept {
|
||||
return mData.template data<ElementIndex>() + 1;
|
||||
}
|
||||
|
||||
template<size_t ElementIndex>
|
||||
typename SoA::template TypeAt<ElementIndex> const* begin() const noexcept {
|
||||
return mData.template data<ElementIndex>() + 1;
|
||||
}
|
||||
|
||||
// return a pointer to the past-the-end element of the ElementIndex'th array
|
||||
template<size_t ElementIndex>
|
||||
typename SoA::template TypeAt<ElementIndex>* end() noexcept {
|
||||
return begin<ElementIndex>() + getComponentCount();
|
||||
}
|
||||
|
||||
template<size_t ElementIndex>
|
||||
typename SoA::template TypeAt<ElementIndex> const* end() const noexcept {
|
||||
return begin<ElementIndex>() + getComponentCount();
|
||||
}
|
||||
|
||||
// return a Slice<>
|
||||
template<size_t ElementIndex>
|
||||
Slice<typename SoA::template TypeAt<ElementIndex>> slice() noexcept {
|
||||
return { begin<ElementIndex>(), end<ElementIndex>() };
|
||||
}
|
||||
|
||||
template<size_t ElementIndex>
|
||||
Slice<const typename SoA::template TypeAt<ElementIndex>> slice() const noexcept {
|
||||
return { begin<ElementIndex>(), end<ElementIndex>() };
|
||||
}
|
||||
|
||||
// return a reference to the index'th element of the ElementIndex'th array
|
||||
template<size_t ElementIndex>
|
||||
typename SoA::template TypeAt<ElementIndex>& elementAt(Instance index) noexcept {
|
||||
assert(index);
|
||||
return data<ElementIndex>()[index];
|
||||
}
|
||||
|
||||
template<size_t ElementIndex>
|
||||
typename SoA::template TypeAt<ElementIndex> const& elementAt(Instance index) const noexcept {
|
||||
assert(index);
|
||||
return data<ElementIndex>()[index];
|
||||
}
|
||||
|
||||
// returns a pointer to the RAW ARRAY of components including the first dummy component
|
||||
// Use with caution.
|
||||
template<size_t ElementIndex>
|
||||
typename SoA::template TypeAt<ElementIndex> const* raw_array() const noexcept {
|
||||
return data<ElementIndex>();
|
||||
}
|
||||
|
||||
// We need our own version of Field because mData is private
|
||||
template<size_t E>
|
||||
struct Field : public SoA::template Field<E> {
|
||||
Field(SingleInstanceComponentManager& soa, EntityInstanceBase::Type i) noexcept
|
||||
: SoA::template Field<E>{ soa.mData, i } {
|
||||
}
|
||||
using SoA::template Field<E>::operator =;
|
||||
};
|
||||
|
||||
protected:
|
||||
template<size_t ElementIndex>
|
||||
typename SoA::template TypeAt<ElementIndex>* data() noexcept {
|
||||
return mData.template data<ElementIndex>();
|
||||
}
|
||||
|
||||
template<size_t ElementIndex>
|
||||
typename SoA::template TypeAt<ElementIndex> const* data() const noexcept {
|
||||
return mData.template data<ElementIndex>();
|
||||
}
|
||||
|
||||
// swap only internals
|
||||
void swap(Instance i, Instance j) noexcept {
|
||||
assert(i);
|
||||
assert(j);
|
||||
if (i && j) {
|
||||
// update the index map
|
||||
auto& map = mInstanceMap;
|
||||
Entity& ei = elementAt<ENTITY_INDEX>(i);
|
||||
Entity& ej = elementAt<ENTITY_INDEX>(j);
|
||||
std::swap(ei, ej);
|
||||
if (ei) {
|
||||
map[ei] = i;
|
||||
}
|
||||
if (ej) {
|
||||
map[ej] = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename REMOVE>
|
||||
void gc(const EntityManager& em, size_t ratio,
|
||||
REMOVE removeComponent) noexcept {
|
||||
Entity const* entities = getEntities();
|
||||
size_t count = getComponentCount();
|
||||
size_t aliveInARow = 0;
|
||||
default_random_engine& rng = mRng;
|
||||
UTILS_NOUNROLL
|
||||
while (count && aliveInARow < ratio) {
|
||||
// note: using the modulo favorizes lower number
|
||||
size_t i = rng() % count;
|
||||
if (UTILS_LIKELY(em.isAlive(entities[i]))) {
|
||||
++aliveInARow;
|
||||
continue;
|
||||
}
|
||||
aliveInARow = 0;
|
||||
count--;
|
||||
removeComponent(entities[i]);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
SoA mData;
|
||||
|
||||
private:
|
||||
// maps an entity to an instance index
|
||||
tsl::robin_map<Entity, Instance, Entity::Hasher> mInstanceMap;
|
||||
default_random_engine mRng;
|
||||
};
|
||||
|
||||
// Keep these outside of the class because CLion has trouble parsing them
|
||||
template<typename ... Elements>
|
||||
typename SingleInstanceComponentManager<Elements ...>::Instance
|
||||
SingleInstanceComponentManager<Elements ...>::addComponent(Entity e) {
|
||||
Instance ci = 0;
|
||||
if (!e.isNull()) {
|
||||
if (!hasComponent(e)) {
|
||||
// this is like a push_back(e);
|
||||
mData.push_back(Structure{}).template back<ENTITY_INDEX>() = e;
|
||||
// index 0 is used when the component doesn't exist
|
||||
ci = Instance(mData.size() - 1);
|
||||
mInstanceMap[e] = ci;
|
||||
} else {
|
||||
// if the entity already has this component, just return its instance
|
||||
ci = mInstanceMap[e];
|
||||
}
|
||||
}
|
||||
assert(ci != 0);
|
||||
return ci;
|
||||
}
|
||||
|
||||
// Keep these outside of the class because CLion has trouble parsing them
|
||||
template <typename ... Elements>
|
||||
typename SingleInstanceComponentManager<Elements ...>::Instance
|
||||
SingleInstanceComponentManager<Elements ... >::removeComponent(Entity e) {
|
||||
auto& map = mInstanceMap;
|
||||
auto pos = map.find(e);
|
||||
if (UTILS_LIKELY(pos != map.end())) {
|
||||
size_t index = pos->second;
|
||||
assert(index != 0);
|
||||
size_t last = mData.size() - 1;
|
||||
if (last != index) {
|
||||
// move the last item to where we removed this component, as to keep
|
||||
// the array tightly packed.
|
||||
mData.forEach([index, last](auto* p) {
|
||||
p[index] = std::move(p[last]);
|
||||
});
|
||||
|
||||
Entity lastEntity = mData.template elementAt<ENTITY_INDEX>(index);
|
||||
map[lastEntity] = index;
|
||||
}
|
||||
mData.pop_back();
|
||||
map.erase(pos);
|
||||
return last;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
} // namespace filament
|
||||
|
||||
#endif // TNT_UTILS_SINGLEINSTANCECOMPONENTMANAGER_H
|
||||
148
windows/include/filament/utils/Slice.h
Normal file
148
windows/include/filament/utils/Slice.h
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_UTILS_SLICE_H
|
||||
#define TNT_UTILS_SLICE_H
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace utils {
|
||||
|
||||
/*
|
||||
* A fixed-size slice of a container
|
||||
*/
|
||||
template <typename T>
|
||||
class Slice {
|
||||
public:
|
||||
using iterator = T*;
|
||||
using const_iterator = T const*;
|
||||
using value_type = T;
|
||||
using reference = T&;
|
||||
using const_reference = T const&;
|
||||
using pointer = T*;
|
||||
using const_pointer = T const*;
|
||||
using size_type = size_t;
|
||||
|
||||
Slice() noexcept = default;
|
||||
|
||||
Slice(const_iterator begin, const_iterator end) noexcept
|
||||
: mBegin(const_cast<iterator>(begin)),
|
||||
mEnd(const_cast<iterator>(end)) {
|
||||
}
|
||||
|
||||
Slice(const_pointer begin, size_type count) noexcept
|
||||
: mBegin(const_cast<iterator>(begin)),
|
||||
mEnd(mBegin + count) {
|
||||
}
|
||||
|
||||
Slice(Slice const& rhs) noexcept = default;
|
||||
Slice(Slice&& rhs) noexcept = default;
|
||||
Slice& operator=(Slice const& rhs) noexcept = default;
|
||||
Slice& operator=(Slice&& rhs) noexcept = default;
|
||||
|
||||
void set(pointer begin, size_type count) UTILS_RESTRICT noexcept {
|
||||
mBegin = begin;
|
||||
mEnd = begin + count;
|
||||
}
|
||||
|
||||
void set(iterator begin, iterator end) UTILS_RESTRICT noexcept {
|
||||
mBegin = begin;
|
||||
mEnd = end;
|
||||
}
|
||||
|
||||
void swap(Slice& rhs) UTILS_RESTRICT noexcept {
|
||||
std::swap(mBegin, rhs.mBegin);
|
||||
std::swap(mEnd, rhs.mEnd);
|
||||
}
|
||||
|
||||
void clear() UTILS_RESTRICT noexcept {
|
||||
mBegin = nullptr;
|
||||
mEnd = nullptr;
|
||||
}
|
||||
|
||||
// size
|
||||
size_t size() const UTILS_RESTRICT noexcept { return mEnd - mBegin; }
|
||||
size_t sizeInBytes() const UTILS_RESTRICT noexcept { return size() * sizeof(T); }
|
||||
bool empty() const UTILS_RESTRICT noexcept { return size() == 0; }
|
||||
|
||||
// iterators
|
||||
iterator begin() UTILS_RESTRICT noexcept { return mBegin; }
|
||||
const_iterator begin() const UTILS_RESTRICT noexcept { return mBegin; }
|
||||
const_iterator cbegin() const UTILS_RESTRICT noexcept { return this->begin(); }
|
||||
iterator end() UTILS_RESTRICT noexcept { return mEnd; }
|
||||
const_iterator end() const UTILS_RESTRICT noexcept { return mEnd; }
|
||||
const_iterator cend() const UTILS_RESTRICT noexcept { return this->end(); }
|
||||
|
||||
// data access
|
||||
reference operator[](size_t n) UTILS_RESTRICT noexcept {
|
||||
assert(n < size());
|
||||
return mBegin[n];
|
||||
}
|
||||
|
||||
const_reference operator[](size_t n) const UTILS_RESTRICT noexcept {
|
||||
assert(n < size());
|
||||
return mBegin[n];
|
||||
}
|
||||
|
||||
reference at(size_t n) UTILS_RESTRICT noexcept {
|
||||
return operator[](n);
|
||||
}
|
||||
|
||||
const_reference at(size_t n) const UTILS_RESTRICT noexcept {
|
||||
return operator[](n);
|
||||
}
|
||||
|
||||
reference front() UTILS_RESTRICT noexcept {
|
||||
assert(!empty());
|
||||
return *mBegin;
|
||||
}
|
||||
|
||||
const_reference front() const UTILS_RESTRICT noexcept {
|
||||
assert(!empty());
|
||||
return *mBegin;
|
||||
}
|
||||
|
||||
reference back() UTILS_RESTRICT noexcept {
|
||||
assert(!empty());
|
||||
return *(this->end() - 1);
|
||||
}
|
||||
|
||||
const_reference back() const UTILS_RESTRICT noexcept {
|
||||
assert(!empty());
|
||||
return *(this->end() - 1);
|
||||
}
|
||||
|
||||
pointer data() UTILS_RESTRICT noexcept {
|
||||
return this->begin();
|
||||
}
|
||||
|
||||
const_pointer data() const UTILS_RESTRICT noexcept {
|
||||
return this->begin();
|
||||
}
|
||||
|
||||
protected:
|
||||
iterator mBegin = nullptr;
|
||||
iterator mEnd = nullptr;
|
||||
};
|
||||
|
||||
} // namespace utils
|
||||
|
||||
#endif // TNT_UTILS_SLICE_H
|
||||
90
windows/include/filament/utils/SpinLock.h
Normal file
90
windows/include/filament/utils/SpinLock.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_UTILS_SPINLOCK_H
|
||||
#define TNT_UTILS_SPINLOCK_H
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <utils/Mutex.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <type_traits>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace utils {
|
||||
namespace details {
|
||||
|
||||
class SpinLock {
|
||||
std::atomic_flag mLock = ATOMIC_FLAG_INIT;
|
||||
|
||||
public:
|
||||
void lock() noexcept {
|
||||
UTILS_PREFETCHW(&mLock);
|
||||
#ifdef __ARM_ACLE
|
||||
// we signal an event on this CPU, so that the first yield() will be a no-op,
|
||||
// and falls through the test_and_set(). This is more efficient than a while { }
|
||||
// construct.
|
||||
UTILS_SIGNAL_EVENT();
|
||||
do {
|
||||
yield();
|
||||
} while (mLock.test_and_set(std::memory_order_acquire));
|
||||
#else
|
||||
goto start;
|
||||
do {
|
||||
yield();
|
||||
start: ;
|
||||
} while (mLock.test_and_set(std::memory_order_acquire));
|
||||
#endif
|
||||
}
|
||||
|
||||
void unlock() noexcept {
|
||||
mLock.clear(std::memory_order_release);
|
||||
#ifdef __ARM_ARCH_7A__
|
||||
// on ARMv7a SEL is needed
|
||||
UTILS_SIGNAL_EVENT();
|
||||
// as well as a memory barrier is needed
|
||||
__dsb(0xA); // ISHST = 0xA (b1010)
|
||||
#else
|
||||
// on ARMv8 we could avoid the call to SE, but we'd need to write the
|
||||
// test_and_set() above by hand, so the WFE only happens without a STRX first.
|
||||
UTILS_BROADCAST_EVENT();
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
inline void yield() noexcept {
|
||||
// on x86 call pause instruction, on ARM call WFE
|
||||
UTILS_WAIT_FOR_EVENT();
|
||||
}
|
||||
};
|
||||
} // namespace details
|
||||
|
||||
#if UTILS_HAS_SANITIZE_THREAD
|
||||
// Active spins with atomics slow down execution too much under ThreadSanitizer.
|
||||
using SpinLock = Mutex;
|
||||
#elif defined(__ARM_ARCH_7A__)
|
||||
// We've had problems with "wfe" on some ARM-V7 devices, causing spurious SIGILL
|
||||
using SpinLock = Mutex;
|
||||
#else
|
||||
using SpinLock = details::SpinLock;
|
||||
#endif
|
||||
|
||||
} // namespace utils
|
||||
|
||||
#endif // TNT_UTILS_SPINLOCK_H
|
||||
691
windows/include/filament/utils/StructureOfArrays.h
Normal file
691
windows/include/filament/utils/StructureOfArrays.h
Normal file
@@ -0,0 +1,691 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_UTILS_STRUCTUREOFARRAYS_H
|
||||
#define TNT_UTILS_STRUCTUREOFARRAYS_H
|
||||
|
||||
#include <utils/Allocator.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Slice.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array> // note: this is safe, see how std::array is used below (inline / private)
|
||||
#include <cstddef>
|
||||
#include <iterator> // for std::random_access_iterator_tag
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
namespace utils {
|
||||
|
||||
template <typename Allocator, typename ... Elements>
|
||||
class StructureOfArraysBase {
|
||||
// number of elements
|
||||
static constexpr const size_t kArrayCount = sizeof...(Elements);
|
||||
|
||||
public:
|
||||
using SoA = StructureOfArraysBase<Allocator, Elements...>;
|
||||
|
||||
using Structure = std::tuple<Elements...>;
|
||||
|
||||
// Type of the Nth array
|
||||
template<size_t N>
|
||||
using TypeAt = typename std::tuple_element_t<N, Structure>;
|
||||
|
||||
// Number of arrays
|
||||
static constexpr size_t getArrayCount() noexcept { return kArrayCount; }
|
||||
|
||||
// Size needed to store "size" array elements
|
||||
static size_t getNeededSize(size_t size) noexcept {
|
||||
return getOffset(kArrayCount - 1, size) + sizeof(TypeAt<kArrayCount - 1>) * size;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
class IteratorValue;
|
||||
template<typename T> class Iterator;
|
||||
using iterator = Iterator<StructureOfArraysBase*>;
|
||||
using const_iterator = Iterator<StructureOfArraysBase const*>;
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
|
||||
/*
|
||||
* An object that represents a reference to the type dereferenced by iterator.
|
||||
* In other words, it's the return type of iterator::operator*(), and since it
|
||||
* cannot be a C++ reference (&), it's an object that acts like it.
|
||||
*/
|
||||
class IteratorValueRef {
|
||||
friend class IteratorValue;
|
||||
friend iterator;
|
||||
friend const_iterator;
|
||||
StructureOfArraysBase* const UTILS_RESTRICT soa;
|
||||
size_t const index;
|
||||
|
||||
IteratorValueRef(StructureOfArraysBase* soa, size_t index) : soa(soa), index(index) { }
|
||||
|
||||
// assigns a value_type to a reference (i.e. assigns to what's pointed to by the reference)
|
||||
template<size_t ... Is>
|
||||
IteratorValueRef& assign(IteratorValue const& rhs, std::index_sequence<Is...>);
|
||||
|
||||
// assigns a value_type to a reference (i.e. assigns to what's pointed to by the reference)
|
||||
template<size_t ... Is>
|
||||
IteratorValueRef& assign(IteratorValue&& rhs, std::index_sequence<Is...>) noexcept;
|
||||
|
||||
// objects pointed to by reference can be swapped, so provide the special swap() function.
|
||||
friend void swap(IteratorValueRef lhs, IteratorValueRef rhs) {
|
||||
lhs.soa->swap(lhs.index, rhs.index);
|
||||
}
|
||||
|
||||
public:
|
||||
// references can be created by copy-assignment only
|
||||
IteratorValueRef(IteratorValueRef const& rhs) noexcept : soa(rhs.soa), index(rhs.index) { }
|
||||
|
||||
// copy the content of a reference to the content of this one
|
||||
IteratorValueRef& operator=(IteratorValueRef const& rhs);
|
||||
|
||||
// move the content of a reference to the content of this one
|
||||
IteratorValueRef& operator=(IteratorValueRef&& rhs) noexcept;
|
||||
|
||||
// copy a value_type to the content of this reference
|
||||
IteratorValueRef& operator=(IteratorValue const& rhs) {
|
||||
return assign(rhs, std::make_index_sequence<kArrayCount>());
|
||||
}
|
||||
|
||||
// move a value_type to the content of this reference
|
||||
IteratorValueRef& operator=(IteratorValue&& rhs) noexcept {
|
||||
return assign(rhs, std::make_index_sequence<kArrayCount>());
|
||||
}
|
||||
|
||||
// access the elements of this reference (i.e. the "fields" of the structure)
|
||||
template<size_t I> TypeAt<I> const& get() const { return soa->elementAt<I>(index); }
|
||||
template<size_t I> TypeAt<I>& get() { return soa->elementAt<I>(index); }
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* The value_type of iterator. This is basically the "structure" of the SoA.
|
||||
* Internally we're using a tuple<> to store the data.
|
||||
* This object is not trivial to construct, as it copies an entry of the SoA.
|
||||
*/
|
||||
class IteratorValue {
|
||||
friend class IteratorValueRef;
|
||||
friend iterator;
|
||||
friend const_iterator;
|
||||
using Type = std::tuple<typename std::decay<Elements>::type...>;
|
||||
Type elements;
|
||||
|
||||
template<size_t ... Is>
|
||||
static Type init(IteratorValueRef const& rhs, std::index_sequence<Is...>) {
|
||||
return Type{ rhs.soa->template elementAt<Is>(rhs.index)... };
|
||||
}
|
||||
|
||||
template<size_t ... Is>
|
||||
static Type init(IteratorValueRef&& rhs, std::index_sequence<Is...>) noexcept {
|
||||
return Type{ std::move(rhs.soa->template elementAt<Is>(rhs.index))... };
|
||||
}
|
||||
|
||||
public:
|
||||
IteratorValue(IteratorValue const& rhs) = default;
|
||||
IteratorValue(IteratorValue&& rhs) noexcept = default;
|
||||
IteratorValue& operator=(IteratorValue const& rhs) = default;
|
||||
IteratorValue& operator=(IteratorValue&& rhs) noexcept = default;
|
||||
|
||||
// initialize and assign from a StructureRef
|
||||
IteratorValue(IteratorValueRef const& rhs)
|
||||
: elements(init(rhs, std::make_index_sequence<kArrayCount>())) {}
|
||||
IteratorValue(IteratorValueRef&& rhs) noexcept
|
||||
: elements(init(rhs, std::make_index_sequence<kArrayCount>())) {}
|
||||
IteratorValue& operator=(IteratorValueRef const& rhs) { return operator=(IteratorValue(rhs)); }
|
||||
IteratorValue& operator=(IteratorValueRef&& rhs) noexcept { return operator=(IteratorValue(rhs)); }
|
||||
|
||||
// access the elements of this value_Type (i.e. the "fields" of the structure)
|
||||
template<size_t I> TypeAt<I> const& get() const { return std::get<I>(elements); }
|
||||
template<size_t I> TypeAt<I>& get() { return std::get<I>(elements); }
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* An iterator to the SoA. This is only intended to be used with STL's algorithm, e.g.: sort().
|
||||
* Normally, SoA is not iterated globally, but rather an array at a time.
|
||||
* Iterating itself is not too costly, as well as dereferencing by reference. However,
|
||||
* dereferencing by value is.
|
||||
*/
|
||||
template<typename CVQualifiedSOAPointer>
|
||||
class Iterator {
|
||||
friend class StructureOfArraysBase;
|
||||
CVQualifiedSOAPointer soa; // don't use restrict, can have aliases if multiple iterators are created
|
||||
size_t index;
|
||||
|
||||
Iterator(CVQualifiedSOAPointer soa, size_t index) : soa(soa), index(index) {}
|
||||
|
||||
public:
|
||||
using value_type = IteratorValue;
|
||||
using reference = IteratorValueRef;
|
||||
using pointer = IteratorValueRef*; // FIXME: this should be a StructurePtr type
|
||||
using difference_type = ptrdiff_t;
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
|
||||
Iterator(Iterator const& rhs) noexcept = default;
|
||||
Iterator& operator=(Iterator const& rhs) = default;
|
||||
|
||||
reference operator*() const { return { soa, index }; }
|
||||
reference operator*() { return { soa, index }; }
|
||||
reference operator[](size_t n) { return *(*this + n); }
|
||||
|
||||
template<size_t I> TypeAt<I> const& get() const { return soa->template elementAt<I>(index); }
|
||||
template<size_t I> TypeAt<I>& get() { return soa->template elementAt<I>(index); }
|
||||
|
||||
Iterator& operator++() { ++index; return *this; }
|
||||
Iterator& operator--() { --index; return *this; }
|
||||
Iterator& operator+=(size_t n) { index += n; return *this; }
|
||||
Iterator& operator-=(size_t n) { index -= n; return *this; }
|
||||
Iterator operator+(size_t n) const { return { soa, index + n }; }
|
||||
Iterator operator-(size_t n) const { return { soa, index - n }; }
|
||||
difference_type operator-(Iterator const& rhs) const { return index - rhs.index; }
|
||||
bool operator==(Iterator const& rhs) const { return (index == rhs.index); }
|
||||
bool operator!=(Iterator const& rhs) const { return (index != rhs.index); }
|
||||
bool operator>=(Iterator const& rhs) const { return (index >= rhs.index); }
|
||||
bool operator> (Iterator const& rhs) const { return (index > rhs.index); }
|
||||
bool operator<=(Iterator const& rhs) const { return (index <= rhs.index); }
|
||||
bool operator< (Iterator const& rhs) const { return (index < rhs.index); }
|
||||
|
||||
// Postfix operator needed by Microsoft STL.
|
||||
const Iterator operator++(int) { Iterator it(*this); index++; return it; }
|
||||
const Iterator operator--(int) { Iterator it(*this); index--; return it; }
|
||||
};
|
||||
|
||||
iterator begin() noexcept { return { this, 0u }; }
|
||||
iterator end() noexcept { return { this, mSize }; }
|
||||
const_iterator begin() const noexcept { return { this, 0u }; }
|
||||
const_iterator end() const noexcept { return { this, mSize }; }
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
StructureOfArraysBase() = default;
|
||||
|
||||
explicit StructureOfArraysBase(size_t capacity) {
|
||||
setCapacity(capacity);
|
||||
}
|
||||
|
||||
// not copyable for now
|
||||
StructureOfArraysBase(StructureOfArraysBase const& rhs) = delete;
|
||||
StructureOfArraysBase& operator=(StructureOfArraysBase const& rhs) = delete;
|
||||
|
||||
// movability is trivial, so support it
|
||||
StructureOfArraysBase(StructureOfArraysBase&& rhs) noexcept {
|
||||
using std::swap;
|
||||
swap(mCapacity, rhs.mCapacity);
|
||||
swap(mSize, rhs.mSize);
|
||||
swap(mArrays, rhs.mArrays);
|
||||
swap(mAllocator, rhs.mAllocator);
|
||||
}
|
||||
|
||||
StructureOfArraysBase& operator=(StructureOfArraysBase&& rhs) noexcept {
|
||||
if (this != &rhs) {
|
||||
using std::swap;
|
||||
swap(mCapacity, rhs.mCapacity);
|
||||
swap(mSize, rhs.mSize);
|
||||
swap(mArrays, rhs.mArrays);
|
||||
swap(mAllocator, rhs.mAllocator);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~StructureOfArraysBase() {
|
||||
destroy_each(0, mSize);
|
||||
mAllocator.free(std::get<0>(mArrays));
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// return the size the array
|
||||
size_t size() const noexcept {
|
||||
return mSize;
|
||||
}
|
||||
|
||||
// return the capacity of the array
|
||||
size_t capacity() const noexcept {
|
||||
return mCapacity;
|
||||
}
|
||||
|
||||
// set the capacity of the array. the capacity cannot be smaller than the current size,
|
||||
// the call is a no-op in that case.
|
||||
UTILS_NOINLINE
|
||||
void setCapacity(size_t capacity) {
|
||||
// allocate enough space for "capacity" elements of each array
|
||||
// capacity cannot change when optional storage is specified
|
||||
if (capacity >= mSize) {
|
||||
// TODO: not entirely sure if "max" of all alignments is always correct
|
||||
constexpr size_t align = std::max({ std::max(alignof(std::max_align_t), alignof(Elements))... });
|
||||
const size_t sizeNeeded = getNeededSize(capacity);
|
||||
void* buffer = mAllocator.alloc(sizeNeeded, align);
|
||||
auto const oldBuffer = std::get<0>(mArrays);
|
||||
|
||||
// move all the items (one array at a time) from the old allocation to the new
|
||||
// this also update the array pointers
|
||||
move_each(buffer, capacity);
|
||||
|
||||
// free the old buffer
|
||||
mAllocator.free(oldBuffer);
|
||||
|
||||
// and make sure to update the capacity
|
||||
mCapacity = capacity;
|
||||
}
|
||||
}
|
||||
|
||||
void ensureCapacity(size_t needed) {
|
||||
if (UTILS_UNLIKELY(needed > mCapacity)) {
|
||||
// not enough space, increase the capacity
|
||||
const size_t capacity = (needed * 3 + 1) / 2;
|
||||
setCapacity(capacity);
|
||||
}
|
||||
}
|
||||
|
||||
// grow or shrink the array to the given size. When growing, new elements are constructed
|
||||
// with their default constructor. when shrinking, discarded elements are destroyed.
|
||||
// If the arrays don't have enough capacity, the capacity is increased accordingly
|
||||
// (the capacity is set to 3/2 of the asked size).
|
||||
UTILS_NOINLINE
|
||||
void resize(size_t needed) {
|
||||
ensureCapacity(needed);
|
||||
resizeNoCheck(needed);
|
||||
if (needed <= mCapacity) {
|
||||
// TODO: see if we should shrink the arrays
|
||||
}
|
||||
}
|
||||
|
||||
void clear() noexcept {
|
||||
resizeNoCheck(0);
|
||||
}
|
||||
|
||||
|
||||
inline void swap(size_t i, size_t j) noexcept {
|
||||
forEach([i, j](auto p) {
|
||||
using std::swap;
|
||||
swap(p[i], p[j]);
|
||||
});
|
||||
}
|
||||
|
||||
// remove and destroy the last element of each array
|
||||
inline void pop_back() noexcept {
|
||||
if (mSize) {
|
||||
destroy_each(mSize - 1, mSize);
|
||||
mSize--;
|
||||
}
|
||||
}
|
||||
|
||||
// create an element at the end of each array
|
||||
StructureOfArraysBase& push_back() noexcept {
|
||||
resize(mSize + 1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
StructureOfArraysBase& push_back(Structure&& args) noexcept {
|
||||
ensureCapacity(mSize + 1);
|
||||
return push_back_unsafe(std::forward<Structure>(args));
|
||||
}
|
||||
|
||||
StructureOfArraysBase& push_back(Elements const& ... args) noexcept {
|
||||
ensureCapacity(mSize + 1);
|
||||
return push_back_unsafe(args...);
|
||||
}
|
||||
|
||||
StructureOfArraysBase& push_back(Elements&& ... args) noexcept {
|
||||
ensureCapacity(mSize + 1);
|
||||
return push_back_unsafe(std::forward<Elements>(args)...);
|
||||
}
|
||||
|
||||
// in C++20 we could use a lambda with explicit template parameter instead
|
||||
struct PushBackUnsafeClosure {
|
||||
size_t last;
|
||||
std::tuple<Elements...> args;
|
||||
inline explicit PushBackUnsafeClosure(size_t last, Structure&& args)
|
||||
: last(last), args(std::forward<Structure>(args)) {}
|
||||
template<size_t I>
|
||||
inline void operator()(TypeAt<I>* p) {
|
||||
new(p + last) TypeAt<I>{ std::get<I>(args) };
|
||||
}
|
||||
};
|
||||
|
||||
StructureOfArraysBase& push_back_unsafe(Structure&& args) noexcept {
|
||||
for_each_index(mArrays,
|
||||
PushBackUnsafeClosure{ mSize++, std::forward<Structure>(args) });
|
||||
return *this;
|
||||
}
|
||||
|
||||
StructureOfArraysBase& push_back_unsafe(Elements const& ... args) noexcept {
|
||||
for_each_index(mArrays,
|
||||
PushBackUnsafeClosure{ mSize++, { args... } });
|
||||
return *this;
|
||||
}
|
||||
|
||||
StructureOfArraysBase& push_back_unsafe(Elements&& ... args) noexcept {
|
||||
for_each_index(mArrays,
|
||||
PushBackUnsafeClosure{ mSize++, { std::forward<Elements>(args)... }});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename F, typename ... ARGS>
|
||||
void forEach(F&& f, ARGS&& ... args) {
|
||||
for_each(mArrays, [&](size_t, auto* p) {
|
||||
f(p, std::forward<ARGS>(args)...);
|
||||
});
|
||||
}
|
||||
|
||||
// return a pointer to the first element of the ElementIndex]th array
|
||||
template<size_t ElementIndex>
|
||||
TypeAt<ElementIndex>* data() noexcept {
|
||||
return std::get<ElementIndex>(mArrays);
|
||||
}
|
||||
|
||||
template<size_t ElementIndex>
|
||||
TypeAt<ElementIndex> const* data() const noexcept {
|
||||
return std::get<ElementIndex>(mArrays);
|
||||
}
|
||||
|
||||
template<size_t ElementIndex>
|
||||
TypeAt<ElementIndex>* begin() noexcept {
|
||||
return std::get<ElementIndex>(mArrays);
|
||||
}
|
||||
|
||||
template<size_t ElementIndex>
|
||||
TypeAt<ElementIndex> const* begin() const noexcept {
|
||||
return std::get<ElementIndex>(mArrays);
|
||||
}
|
||||
|
||||
template<size_t ElementIndex>
|
||||
TypeAt<ElementIndex>* end() noexcept {
|
||||
return std::get<ElementIndex>(mArrays) + size();
|
||||
}
|
||||
|
||||
template<size_t ElementIndex>
|
||||
TypeAt<ElementIndex> const* end() const noexcept {
|
||||
return std::get<ElementIndex>(mArrays) + size();
|
||||
}
|
||||
|
||||
template<size_t ElementIndex>
|
||||
Slice<TypeAt<ElementIndex>> slice() noexcept {
|
||||
return { begin<ElementIndex>(), end<ElementIndex>() };
|
||||
}
|
||||
|
||||
template<size_t ElementIndex>
|
||||
Slice<const TypeAt<ElementIndex>> slice() const noexcept {
|
||||
return { begin<ElementIndex>(), end<ElementIndex>() };
|
||||
}
|
||||
|
||||
// return a reference to the index'th element of the ElementIndex'th array
|
||||
template<size_t ElementIndex>
|
||||
TypeAt<ElementIndex>& elementAt(size_t index) noexcept {
|
||||
return data<ElementIndex>()[index];
|
||||
}
|
||||
|
||||
template<size_t ElementIndex>
|
||||
TypeAt<ElementIndex> const& elementAt(size_t index) const noexcept {
|
||||
return data<ElementIndex>()[index];
|
||||
}
|
||||
|
||||
// return a reference to the last element of the ElementIndex'th array
|
||||
template<size_t ElementIndex>
|
||||
TypeAt<ElementIndex>& back() noexcept {
|
||||
return data<ElementIndex>()[size() - 1];
|
||||
}
|
||||
|
||||
template<size_t ElementIndex>
|
||||
TypeAt<ElementIndex> const& back() const noexcept {
|
||||
return data<ElementIndex>()[size() - 1];
|
||||
}
|
||||
|
||||
template <size_t E, typename IndexType = uint32_t>
|
||||
struct Field {
|
||||
SoA& soa;
|
||||
IndexType i;
|
||||
using Type = typename SoA::template TypeAt<E>;
|
||||
|
||||
UTILS_ALWAYS_INLINE Field& operator = (Field&& rhs) noexcept {
|
||||
soa.elementAt<E>(i) = soa.elementAt<E>(rhs.i);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// auto-conversion to the field's type
|
||||
UTILS_ALWAYS_INLINE operator Type&() noexcept {
|
||||
return soa.elementAt<E>(i);
|
||||
}
|
||||
UTILS_ALWAYS_INLINE operator Type const&() const noexcept {
|
||||
return soa.elementAt<E>(i);
|
||||
}
|
||||
// dereferencing the selected field
|
||||
UTILS_ALWAYS_INLINE Type& operator ->() noexcept {
|
||||
return soa.elementAt<E>(i);
|
||||
}
|
||||
UTILS_ALWAYS_INLINE Type const& operator ->() const noexcept {
|
||||
return soa.elementAt<E>(i);
|
||||
}
|
||||
// address-of the selected field
|
||||
UTILS_ALWAYS_INLINE Type* operator &() noexcept {
|
||||
return &soa.elementAt<E>(i);
|
||||
}
|
||||
UTILS_ALWAYS_INLINE Type const* operator &() const noexcept {
|
||||
return &soa.elementAt<E>(i);
|
||||
}
|
||||
// assignment to the field
|
||||
UTILS_ALWAYS_INLINE Type const& operator = (Type const& other) noexcept {
|
||||
return (soa.elementAt<E>(i) = other);
|
||||
}
|
||||
UTILS_ALWAYS_INLINE Type const& operator = (Type&& other) noexcept {
|
||||
return (soa.elementAt<E>(i) = other);
|
||||
}
|
||||
// comparisons
|
||||
UTILS_ALWAYS_INLINE bool operator==(Type const& other) const {
|
||||
return (soa.elementAt<E>(i) == other);
|
||||
}
|
||||
UTILS_ALWAYS_INLINE bool operator!=(Type const& other) const {
|
||||
return (soa.elementAt<E>(i) != other);
|
||||
}
|
||||
// calling the field
|
||||
template <typename ... ARGS>
|
||||
UTILS_ALWAYS_INLINE decltype(auto) operator()(ARGS&& ... args) noexcept {
|
||||
return soa.elementAt<E>(i)(std::forward<ARGS>(args)...);
|
||||
}
|
||||
template <typename ... ARGS>
|
||||
UTILS_ALWAYS_INLINE decltype(auto) operator()(ARGS&& ... args) const noexcept {
|
||||
return soa.elementAt<E>(i)(std::forward<ARGS>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
template<std::size_t I = 0, typename FuncT, typename... Tp>
|
||||
inline typename std::enable_if<I == sizeof...(Tp), void>::type
|
||||
for_each(std::tuple<Tp...>&, FuncT) {}
|
||||
|
||||
template<std::size_t I = 0, typename FuncT, typename... Tp>
|
||||
inline typename std::enable_if<I < sizeof...(Tp), void>::type
|
||||
for_each(std::tuple<Tp...>& t, FuncT f) {
|
||||
f(I, std::get<I>(t));
|
||||
for_each<I + 1, FuncT, Tp...>(t, f);
|
||||
}
|
||||
|
||||
template<std::size_t I = 0, typename FuncT, typename... Tp>
|
||||
inline typename std::enable_if<I == sizeof...(Tp), void>::type
|
||||
for_each_index(std::tuple<Tp...>&, FuncT) {}
|
||||
|
||||
template<std::size_t I = 0, typename FuncT, typename... Tp>
|
||||
inline typename std::enable_if<I < sizeof...(Tp), void>::type
|
||||
for_each_index(std::tuple<Tp...>& t, FuncT f) {
|
||||
f.template operator()<I>(std::get<I>(t));
|
||||
for_each_index<I + 1, FuncT, Tp...>(t, f);
|
||||
}
|
||||
|
||||
inline void resizeNoCheck(size_t needed) noexcept {
|
||||
assert(mCapacity >= needed);
|
||||
if (needed < mSize) {
|
||||
// we shrink the arrays
|
||||
destroy_each(needed, mSize);
|
||||
} else if (needed > mSize) {
|
||||
// we grow the arrays
|
||||
construct_each(mSize, needed);
|
||||
}
|
||||
// record the new size of the arrays
|
||||
mSize = needed;
|
||||
}
|
||||
|
||||
// this calculates the offset adjusted for all data alignment of a given array
|
||||
static inline size_t getOffset(size_t index, size_t capacity) noexcept {
|
||||
auto offsets = getOffsets(capacity);
|
||||
return offsets[index];
|
||||
}
|
||||
|
||||
static inline std::array<size_t, kArrayCount> getOffsets(size_t capacity) noexcept {
|
||||
// compute the required size of each array
|
||||
const size_t sizes[] = { (sizeof(Elements) * capacity)... };
|
||||
|
||||
// we align each array to at least the same alignment guaranteed by malloc
|
||||
constexpr size_t const alignments[] = { std::max(alignof(std::max_align_t), alignof(Elements))... };
|
||||
|
||||
// hopefully most of this gets unrolled and inlined
|
||||
std::array<size_t, kArrayCount> offsets;
|
||||
offsets[0] = 0;
|
||||
UTILS_UNROLL
|
||||
for (size_t i = 1; i < kArrayCount; i++) {
|
||||
size_t unalignment = (offsets[i - 1] + sizes[i - 1]) % alignments[i];
|
||||
size_t alignment = unalignment ? (alignments[i] - unalignment) : 0;
|
||||
offsets[i] = offsets[i - 1] + (sizes[i - 1] + alignment);
|
||||
assert_invariant(offsets[i] % alignments[i] == 0);
|
||||
}
|
||||
return offsets;
|
||||
}
|
||||
|
||||
void construct_each(size_t from, size_t to) noexcept {
|
||||
forEach([from, to](auto p) {
|
||||
using T = typename std::decay<decltype(*p)>::type;
|
||||
// note: scalar types like int/float get initialized to zero
|
||||
if constexpr (!std::is_trivially_default_constructible_v<T>) {
|
||||
for (size_t i = from; i < to; i++) {
|
||||
new(p + i) T();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void destroy_each(size_t from, size_t to) noexcept {
|
||||
forEach([from, to](auto p) {
|
||||
using T = typename std::decay<decltype(*p)>::type;
|
||||
if constexpr (!std::is_trivially_destructible_v<T>) {
|
||||
for (size_t i = from; i < to; i++) {
|
||||
p[i].~T();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void move_each(void* buffer, size_t capacity) noexcept {
|
||||
auto offsets = getOffsets(capacity);
|
||||
size_t index = 0;
|
||||
if (mSize) {
|
||||
auto size = mSize; // placate a compiler warning
|
||||
forEach([buffer, &index, &offsets, size](auto p) {
|
||||
using T = typename std::decay<decltype(*p)>::type;
|
||||
T* UTILS_RESTRICT b = static_cast<T*>(buffer);
|
||||
|
||||
// go through each element and move them from the old array to the new
|
||||
// then destroy them from the old array
|
||||
T* UTILS_RESTRICT const arrayPointer =
|
||||
reinterpret_cast<T*>(uintptr_t(b) + offsets[index]);
|
||||
|
||||
// for trivial cases, just call memcpy()
|
||||
if constexpr (std::is_trivially_copyable_v<T> &&
|
||||
std::is_trivially_destructible_v<T>) {
|
||||
memcpy(arrayPointer, p, size * sizeof(T));
|
||||
} else {
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
// we move an element by using the in-place move-constructor
|
||||
new(arrayPointer + i) T(std::move(p[i]));
|
||||
if constexpr (!std::is_trivially_destructible_v<T>) {
|
||||
// and delete them by calling the destructor directly
|
||||
p[i].~T();
|
||||
}
|
||||
}
|
||||
}
|
||||
index++;
|
||||
});
|
||||
}
|
||||
|
||||
// update the pointers
|
||||
for_each(mArrays, [buffer, &offsets](size_t i, auto&& p) {
|
||||
using Type = std::remove_reference_t<decltype(p)>;
|
||||
p = Type((char*)buffer + offsets[i]);
|
||||
});
|
||||
}
|
||||
|
||||
// capacity in array elements
|
||||
size_t mCapacity = 0;
|
||||
// size in array elements
|
||||
size_t mSize = 0;
|
||||
// N pointers to each arrays
|
||||
std::tuple<std::add_pointer_t<Elements>...> mArrays{};
|
||||
Allocator mAllocator;
|
||||
};
|
||||
|
||||
|
||||
template<typename Allocator, typename... Elements>
|
||||
inline
|
||||
typename StructureOfArraysBase<Allocator, Elements...>::IteratorValueRef&
|
||||
StructureOfArraysBase<Allocator, Elements...>::IteratorValueRef::operator=(
|
||||
StructureOfArraysBase::IteratorValueRef const& rhs) {
|
||||
return operator=(IteratorValue(rhs));
|
||||
}
|
||||
|
||||
template<typename Allocator, typename... Elements>
|
||||
inline
|
||||
typename StructureOfArraysBase<Allocator, Elements...>::IteratorValueRef&
|
||||
StructureOfArraysBase<Allocator, Elements...>::IteratorValueRef::operator=(
|
||||
StructureOfArraysBase::IteratorValueRef&& rhs) noexcept {
|
||||
return operator=(IteratorValue(rhs));
|
||||
}
|
||||
|
||||
template<typename Allocator, typename... Elements>
|
||||
template<size_t... Is>
|
||||
inline
|
||||
typename StructureOfArraysBase<Allocator, Elements...>::IteratorValueRef&
|
||||
StructureOfArraysBase<Allocator, Elements...>::IteratorValueRef::assign(
|
||||
StructureOfArraysBase::IteratorValue const& rhs, std::index_sequence<Is...>) {
|
||||
// implements IteratorValueRef& IteratorValueRef::operator=(IteratorValue const& rhs)
|
||||
auto UTILS_UNUSED l = { (soa->elementAt<Is>(index) = std::get<Is>(rhs.elements), 0)... };
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename Allocator, typename... Elements>
|
||||
template<size_t... Is>
|
||||
inline
|
||||
typename StructureOfArraysBase<Allocator, Elements...>::IteratorValueRef&
|
||||
StructureOfArraysBase<Allocator, Elements...>::IteratorValueRef::assign(
|
||||
StructureOfArraysBase::IteratorValue&& rhs, std::index_sequence<Is...>) noexcept {
|
||||
// implements IteratorValueRef& IteratorValueRef::operator=(IteratorValue&& rhs) noexcept
|
||||
auto UTILS_UNUSED l = {
|
||||
(soa->elementAt<Is>(index) = std::move(std::get<Is>(rhs.elements)), 0)... };
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename ... Elements>
|
||||
using StructureOfArrays = StructureOfArraysBase<HeapArena<>, Elements ...>;
|
||||
|
||||
} // namespace utils
|
||||
|
||||
#endif // TNT_UTILS_STRUCTUREOFARRAYS_H
|
||||
|
||||
214
windows/include/filament/utils/algorithm.h
Normal file
214
windows/include/filament/utils/algorithm.h
Normal file
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_UTILS_ALGORITHM_H
|
||||
#define TNT_UTILS_ALGORITHM_H
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <type_traits> // for std::enable_if
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace utils {
|
||||
|
||||
namespace details {
|
||||
|
||||
template<typename T>
|
||||
constexpr inline T popcount(T v) noexcept {
|
||||
static_assert(sizeof(T) * CHAR_BIT <= 128, "details::popcount() only support up to 128 bits");
|
||||
constexpr T ONES = ~T(0);
|
||||
v = v - ((v >> 1u) & ONES / 3);
|
||||
v = (v & ONES / 15 * 3) + ((v >> 2u) & ONES / 15 * 3);
|
||||
v = (v + (v >> 4u)) & ONES / 255 * 15;
|
||||
return (T) (v * (ONES / 255)) >> (sizeof(T) - 1) * CHAR_BIT;
|
||||
}
|
||||
|
||||
template<typename T, typename = std::enable_if_t<std::is_unsigned<T>::value>>
|
||||
constexpr inline T clz(T x) noexcept {
|
||||
static_assert(sizeof(T) * CHAR_BIT <= 128, "details::clz() only support up to 128 bits");
|
||||
x |= (x >> 1u);
|
||||
x |= (x >> 2u);
|
||||
x |= (x >> 4u);
|
||||
x |= (x >> 8u);
|
||||
x |= (x >> 16u);
|
||||
if constexpr (sizeof(T) * CHAR_BIT >= 64) { // just to silence compiler warning
|
||||
x |= (x >> 32u);
|
||||
}
|
||||
if constexpr (sizeof(T) * CHAR_BIT >= 128) { // just to silence compiler warning
|
||||
x |= (x >> 64u);
|
||||
}
|
||||
return T(sizeof(T) * CHAR_BIT) - details::popcount(x);
|
||||
}
|
||||
|
||||
template<typename T, typename = std::enable_if_t<std::is_unsigned<T>::value>>
|
||||
constexpr inline T ctz(T x) noexcept {
|
||||
static_assert(sizeof(T) * CHAR_BIT <= 64, "details::ctz() only support up to 64 bits");
|
||||
T c = sizeof(T) * CHAR_BIT;
|
||||
#if defined(_MSC_VER)
|
||||
// equivalent to x & -x, but MSVC yield a warning for using unary minus operator on unsigned types
|
||||
x &= (~x + 1);
|
||||
#else
|
||||
// equivalent to x & (~x + 1), but some compilers generate a better sequence on ARM
|
||||
x &= -x;
|
||||
#endif
|
||||
if (x) c--;
|
||||
if (sizeof(T) * CHAR_BIT >= 64) {
|
||||
if (x & T(0x00000000FFFFFFFF)) c -= 32;
|
||||
}
|
||||
if (x & T(0x0000FFFF0000FFFF)) c -= 16;
|
||||
if (x & T(0x00FF00FF00FF00FF)) c -= 8;
|
||||
if (x & T(0x0F0F0F0F0F0F0F0F)) c -= 4;
|
||||
if (x & T(0x3333333333333333)) c -= 2;
|
||||
if (x & T(0x5555555555555555)) c -= 1;
|
||||
return c;
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
|
||||
constexpr inline UTILS_PUBLIC UTILS_PURE
|
||||
unsigned int UTILS_ALWAYS_INLINE clz(unsigned int x) noexcept {
|
||||
#if __has_builtin(__builtin_clz)
|
||||
return __builtin_clz(x);
|
||||
#else
|
||||
return details::clz(x);
|
||||
#endif
|
||||
}
|
||||
|
||||
constexpr inline UTILS_PUBLIC UTILS_PURE
|
||||
unsigned long UTILS_ALWAYS_INLINE clz(unsigned long x) noexcept {
|
||||
#if __has_builtin(__builtin_clzl)
|
||||
return __builtin_clzl(x);
|
||||
#else
|
||||
return details::clz(x);
|
||||
#endif
|
||||
}
|
||||
|
||||
constexpr inline UTILS_PUBLIC UTILS_PURE
|
||||
unsigned long long UTILS_ALWAYS_INLINE clz(unsigned long long x) noexcept {
|
||||
#if __has_builtin(__builtin_clzll)
|
||||
return __builtin_clzll(x);
|
||||
#else
|
||||
return details::clz(x);
|
||||
#endif
|
||||
}
|
||||
|
||||
constexpr inline UTILS_PUBLIC UTILS_PURE
|
||||
unsigned int UTILS_ALWAYS_INLINE ctz(unsigned int x) noexcept {
|
||||
#if __has_builtin(__builtin_ctz)
|
||||
return __builtin_ctz(x);
|
||||
#else
|
||||
return details::ctz(x);
|
||||
#endif
|
||||
}
|
||||
|
||||
constexpr inline UTILS_PUBLIC UTILS_PURE
|
||||
unsigned long UTILS_ALWAYS_INLINE ctz(unsigned long x) noexcept {
|
||||
#if __has_builtin(__builtin_ctzl)
|
||||
return __builtin_ctzl(x);
|
||||
#else
|
||||
return details::ctz(x);
|
||||
#endif
|
||||
}
|
||||
|
||||
constexpr inline UTILS_PUBLIC UTILS_PURE
|
||||
unsigned long long UTILS_ALWAYS_INLINE ctz(unsigned long long x) noexcept {
|
||||
#if __has_builtin(__builtin_ctzll)
|
||||
return __builtin_ctzll(x);
|
||||
#else
|
||||
return details::ctz(x);
|
||||
#endif
|
||||
}
|
||||
|
||||
constexpr inline UTILS_PUBLIC UTILS_PURE
|
||||
unsigned int UTILS_ALWAYS_INLINE popcount(unsigned int x) noexcept {
|
||||
#if __has_builtin(__builtin_popcount)
|
||||
return __builtin_popcount(x);
|
||||
#else
|
||||
return details::popcount(x);
|
||||
#endif
|
||||
}
|
||||
|
||||
constexpr inline UTILS_PUBLIC UTILS_PURE
|
||||
unsigned long UTILS_ALWAYS_INLINE popcount(unsigned long x) noexcept {
|
||||
#if __has_builtin(__builtin_popcountl)
|
||||
return __builtin_popcountl(x);
|
||||
#else
|
||||
return details::popcount(x);
|
||||
#endif
|
||||
}
|
||||
|
||||
constexpr inline UTILS_PUBLIC UTILS_PURE
|
||||
unsigned long long UTILS_ALWAYS_INLINE popcount(unsigned long long x) noexcept {
|
||||
#if __has_builtin(__builtin_popcountll)
|
||||
return __builtin_popcountll(x);
|
||||
#else
|
||||
return details::popcount(x);
|
||||
#endif
|
||||
}
|
||||
|
||||
constexpr inline UTILS_PUBLIC UTILS_PURE
|
||||
uint8_t UTILS_ALWAYS_INLINE popcount(uint8_t x) noexcept {
|
||||
return (uint8_t)popcount((unsigned int)x);
|
||||
}
|
||||
|
||||
template<typename T,
|
||||
typename = std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value>>
|
||||
constexpr inline UTILS_PUBLIC UTILS_PURE
|
||||
T log2i(T x) noexcept {
|
||||
return (sizeof(x) * 8 - 1u) - clz(x);
|
||||
}
|
||||
|
||||
template<typename RandomAccessIterator, typename COMPARE>
|
||||
inline UTILS_PUBLIC
|
||||
RandomAccessIterator partition_point(
|
||||
RandomAccessIterator first, RandomAccessIterator last, COMPARE pred,
|
||||
bool assume_power_of_two = false) {
|
||||
size_t len = last - first;
|
||||
|
||||
if (!assume_power_of_two) {
|
||||
// handle non power-of-two sized arrays. If it's POT, the next line is a no-op
|
||||
// and gets optimized out if the size is known at compile time.
|
||||
len = 1u << (31 - clz(uint32_t(len))); // next power of two length / 2
|
||||
size_t difference = (last - first) - len;
|
||||
first += !difference || pred(first[len]) ? difference : 0;
|
||||
}
|
||||
|
||||
while (len) {
|
||||
// The number of repetitions here doesn't affect the result. We manually unroll the loop
|
||||
// twice, to guarantee we have at least two iterations without branches (for the case
|
||||
// where the size is not known at compile time
|
||||
first += pred(first[len>>=1u]) ? len : 0;
|
||||
first += pred(first[len>>=1u]) ? len : 0;
|
||||
}
|
||||
first += pred(*first);
|
||||
return first;
|
||||
}
|
||||
|
||||
template <class To, class From>
|
||||
typename std::enable_if_t<
|
||||
(sizeof(To) == sizeof(From)) &&
|
||||
std::is_trivially_copyable<From>::value,
|
||||
To>
|
||||
// constexpr support needs compiler magic
|
||||
bit_cast(const From &src) noexcept {
|
||||
return reinterpret_cast<const To&>(src);
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
|
||||
#endif // TNT_UTILS_ALGORITHM_H
|
||||
330
windows/include/filament/utils/bitset.h
Normal file
330
windows/include/filament/utils/bitset.h
Normal file
@@ -0,0 +1,330 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_UTILS_BITSET_H
|
||||
#define TNT_UTILS_BITSET_H
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <algorithm> // for std::fill
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
#if defined(__ARM_NEON)
|
||||
# if defined(__ARM_ACLE) && defined(__aarch64__)
|
||||
# include <arm_neon.h>
|
||||
# define TNT_UTILS_BITSET_USE_NEON 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
namespace utils {
|
||||
|
||||
/*
|
||||
* This bitset<> class is different from std::bitset<> in that it allows us to control
|
||||
* the exact storage size. This is useful for small bitset (e.g. < 64, on 64-bits machines).
|
||||
* It also allows for lexicographical compares (i.e. sorting).
|
||||
*/
|
||||
|
||||
template<typename T, size_t N = 1,
|
||||
typename = typename std::enable_if<std::is_integral<T>::value &&
|
||||
std::is_unsigned<T>::value>::type>
|
||||
class UTILS_PUBLIC bitset {
|
||||
T storage[N];
|
||||
|
||||
public:
|
||||
static constexpr T BITS_PER_WORD = sizeof(T) * 8;
|
||||
static constexpr T BIT_COUNT = BITS_PER_WORD * N;
|
||||
static constexpr T WORLD_COUNT = N;
|
||||
using container_type = T;
|
||||
|
||||
bitset() noexcept {
|
||||
std::fill(std::begin(storage), std::end(storage), 0);
|
||||
}
|
||||
|
||||
T getBitsAt(size_t n) const noexcept {
|
||||
assert_invariant(n<N);
|
||||
return storage[n];
|
||||
}
|
||||
|
||||
T& getBitsAt(size_t n) noexcept {
|
||||
assert_invariant(n<N);
|
||||
return storage[n];
|
||||
}
|
||||
|
||||
T getValue() const noexcept {
|
||||
static_assert(N == 1, "bitfield must only have one storage word");
|
||||
return storage[0];
|
||||
}
|
||||
|
||||
void setValue(T value) noexcept {
|
||||
static_assert(N == 1, "bitfield must only have one storage word");
|
||||
storage[0] = value;
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
void forEachSetBit(F exec) const noexcept {
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
T v = storage[i];
|
||||
while (v) {
|
||||
T k = utils::ctz(v);
|
||||
v &= ~(T(1) << k);
|
||||
exec(size_t(k + BITS_PER_WORD * i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t size() const noexcept { return N * BITS_PER_WORD; }
|
||||
|
||||
bool test(size_t bit) const noexcept { return operator[](bit); }
|
||||
|
||||
void set(size_t b) noexcept {
|
||||
assert_invariant(b / BITS_PER_WORD < N);
|
||||
storage[b / BITS_PER_WORD] |= T(1) << (b % BITS_PER_WORD);
|
||||
}
|
||||
|
||||
void set(size_t b, bool value) noexcept {
|
||||
assert_invariant(b / BITS_PER_WORD < N);
|
||||
storage[b / BITS_PER_WORD] &= ~(T(1) << (b % BITS_PER_WORD));
|
||||
storage[b / BITS_PER_WORD] |= T(value) << (b % BITS_PER_WORD);
|
||||
}
|
||||
|
||||
void unset(size_t b) noexcept {
|
||||
assert_invariant(b / BITS_PER_WORD < N);
|
||||
storage[b / BITS_PER_WORD] &= ~(T(1) << (b % BITS_PER_WORD));
|
||||
}
|
||||
|
||||
void flip(size_t b) noexcept {
|
||||
assert_invariant(b / BITS_PER_WORD < N);
|
||||
storage[b / BITS_PER_WORD] ^= T(1) << (b % BITS_PER_WORD);
|
||||
}
|
||||
|
||||
|
||||
void reset() noexcept {
|
||||
std::fill(std::begin(storage), std::end(storage), 0);
|
||||
}
|
||||
|
||||
bool operator[](size_t b) const noexcept {
|
||||
assert_invariant(b / BITS_PER_WORD < N);
|
||||
return bool(storage[b / BITS_PER_WORD] & (T(1) << (b % BITS_PER_WORD)));
|
||||
}
|
||||
|
||||
size_t count() const noexcept {
|
||||
#if defined(TNT_UTILS_BITSET_USE_NEON)
|
||||
if (BIT_COUNT % 128 == 0 && BIT_COUNT / 128 < 31) {
|
||||
// Use NEON for bitset multiple of 128 bits.
|
||||
// The intermediate computation can't handle more than 31*128 bits because
|
||||
// intermediate counts must be 8 bits.
|
||||
uint8x16_t const* const p = (uint8x16_t const*) storage;
|
||||
uint8x16_t counts = vcntq_u8(p[0]);
|
||||
for (size_t i = 1; i < BIT_COUNT / 128; ++i) {
|
||||
counts += vcntq_u8(p[i]);
|
||||
}
|
||||
return vaddlvq_u8(counts);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
T r = utils::popcount(storage[0]);
|
||||
for (size_t i = 1; i < N; ++i) {
|
||||
r += utils::popcount(storage[i]);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
bool any() const noexcept {
|
||||
#if defined(TNT_UTILS_BITSET_USE_NEON)
|
||||
if (BIT_COUNT % 128 == 0) {
|
||||
uint64x2_t const* const p = (uint64x2_t const*) storage;
|
||||
uint64x2_t r = p[0];
|
||||
for (size_t i = 1; i < BIT_COUNT / 128; ++i) {
|
||||
r |= p[i];
|
||||
}
|
||||
return bool(r[0] | r[1]);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
T r = storage[0];
|
||||
for (size_t i = 1; i < N; ++i) {
|
||||
r |= storage[i];
|
||||
}
|
||||
return bool(r);
|
||||
}
|
||||
}
|
||||
|
||||
bool none() const noexcept {
|
||||
return !any();
|
||||
}
|
||||
|
||||
bool all() const noexcept {
|
||||
#if defined(TNT_UTILS_BITSET_USE_NEON)
|
||||
if (BIT_COUNT % 128 == 0) {
|
||||
uint64x2_t const* const p = (uint64x2_t const*) storage;
|
||||
uint64x2_t r = p[0];
|
||||
for (size_t i = 1; i < BIT_COUNT / 128; ++i) {
|
||||
r &= p[i];
|
||||
}
|
||||
return T(~(r[0] & r[1])) == T(0);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
T r = storage[0];
|
||||
for (size_t i = 1; i < N; ++i) {
|
||||
r &= storage[i];
|
||||
}
|
||||
return T(~r) == T(0);
|
||||
}
|
||||
}
|
||||
|
||||
bool operator!=(const bitset& b) const noexcept {
|
||||
#if defined(TNT_UTILS_BITSET_USE_NEON)
|
||||
if (BIT_COUNT % 128 == 0) {
|
||||
bitset temp(*this ^ b);
|
||||
uint64x2_t const* const p = (uint64x2_t const*) temp.storage;
|
||||
uint64x2_t r = p[0];
|
||||
for (size_t i = 1; i < BIT_COUNT / 128; ++i) {
|
||||
r |= p[i];
|
||||
}
|
||||
return bool(r[0] | r[1]);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
T r = storage[0] ^ b.storage[0];
|
||||
for (size_t i = 1; i < N; ++i) {
|
||||
r |= storage[i] ^ b.storage[i];
|
||||
}
|
||||
return bool(r);
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(const bitset& b) const noexcept {
|
||||
return !operator!=(b);
|
||||
}
|
||||
|
||||
bitset& operator&=(const bitset& b) noexcept {
|
||||
#if defined(TNT_UTILS_BITSET_USE_NEON)
|
||||
if (BIT_COUNT % 128 == 0) {
|
||||
uint8x16_t* const p = (uint8x16_t*) storage;
|
||||
uint8x16_t const* const q = (uint8x16_t const*) b.storage;
|
||||
for (size_t i = 0; i < BIT_COUNT / 128; ++i) {
|
||||
p[i] &= q[i];
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
storage[i] &= b.storage[i];
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bitset& operator|=(const bitset& b) noexcept {
|
||||
#if defined(TNT_UTILS_BITSET_USE_NEON)
|
||||
if (BIT_COUNT % 128 == 0) {
|
||||
uint8x16_t* const p = (uint8x16_t*) storage;
|
||||
uint8x16_t const* const q = (uint8x16_t const*) b.storage;
|
||||
for (size_t i = 0; i < BIT_COUNT / 128; ++i) {
|
||||
p[i] |= q[i];
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
storage[i] |= b.storage[i];
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bitset& operator^=(const bitset& b) noexcept {
|
||||
#if defined(TNT_UTILS_BITSET_USE_NEON)
|
||||
if (BIT_COUNT % 128 == 0) {
|
||||
uint8x16_t* const p = (uint8x16_t*) storage;
|
||||
uint8x16_t const* const q = (uint8x16_t const*) b.storage;
|
||||
for (size_t i = 0; i < BIT_COUNT / 128; ++i) {
|
||||
p[i] ^= q[i];
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
storage[i] ^= b.storage[i];
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bitset operator~() const noexcept {
|
||||
bitset r;
|
||||
#if defined(TNT_UTILS_BITSET_USE_NEON)
|
||||
if (BIT_COUNT % 128 == 0) {
|
||||
uint8x16_t* const p = (uint8x16_t*) r.storage;
|
||||
uint8x16_t const* const q = (uint8x16_t const*) storage;
|
||||
for (size_t i = 0; i < BIT_COUNT / 128; ++i) {
|
||||
p[i] = ~q[i];
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
r.storage[i] = ~storage[i];
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
private:
|
||||
friend bool operator<(bitset const& lhs, bitset const& rhs) noexcept {
|
||||
return std::lexicographical_compare(
|
||||
std::begin(lhs.storage), std::end(lhs.storage),
|
||||
std::begin(rhs.storage), std::end(rhs.storage)
|
||||
);
|
||||
}
|
||||
|
||||
friend bitset operator&(const bitset& lhs, const bitset& rhs) noexcept {
|
||||
return bitset(lhs) &= rhs;
|
||||
}
|
||||
|
||||
friend bitset operator|(const bitset& lhs, const bitset& rhs) noexcept {
|
||||
return bitset(lhs) |= rhs;
|
||||
}
|
||||
|
||||
friend bitset operator^(const bitset& lhs, const bitset& rhs) noexcept {
|
||||
return bitset(lhs) ^= rhs;
|
||||
}
|
||||
};
|
||||
|
||||
using bitset8 = bitset<uint8_t>;
|
||||
using bitset32 = bitset<uint32_t>;
|
||||
using bitset64 = bitset<uint64_t>;
|
||||
using bitset128 = bitset<uint64_t, 2>;
|
||||
using bitset256 = bitset<uint64_t, 4>;
|
||||
|
||||
static_assert(sizeof(bitset8) == 1, "bitset8 isn't 8 bits!");
|
||||
static_assert(sizeof(bitset32) == 4, "bitset32 isn't 32 bits!");
|
||||
static_assert(sizeof(bitset64) == 8, "bitset64 isn't 64 bits!");
|
||||
static_assert(sizeof(bitset128) == 16, "bitset128 isn't 128 bits!");
|
||||
static_assert(sizeof(bitset256) == 32, "bitset256 isn't 256 bits!");
|
||||
|
||||
} // namespace utils
|
||||
|
||||
#endif // TNT_UTILS_BITSET_H
|
||||
263
windows/include/filament/utils/compiler.h
Normal file
263
windows/include/filament/utils/compiler.h
Normal file
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_UTILS_COMPILER_H
|
||||
#define TNT_UTILS_COMPILER_H
|
||||
|
||||
// compatibility with non-clang compilers...
|
||||
#ifndef __has_attribute
|
||||
#define __has_attribute(x) 0
|
||||
#endif
|
||||
|
||||
#ifndef __has_feature
|
||||
#define __has_feature(x) 0
|
||||
#endif
|
||||
|
||||
#ifndef __has_builtin
|
||||
#define __has_builtin(x) 0
|
||||
#endif
|
||||
|
||||
#if __has_attribute(visibility)
|
||||
# define UTILS_PUBLIC __attribute__((visibility("default")))
|
||||
#else
|
||||
# define UTILS_PUBLIC
|
||||
#endif
|
||||
|
||||
#if __has_attribute(deprecated)
|
||||
# define UTILS_DEPRECATED [[deprecated]]
|
||||
#else
|
||||
# define UTILS_DEPRECATED
|
||||
#endif
|
||||
|
||||
#if __has_attribute(packed)
|
||||
# define UTILS_PACKED __attribute__((packed))
|
||||
#else
|
||||
# define UTILS_PACKED
|
||||
#endif
|
||||
|
||||
#if __has_attribute(noreturn)
|
||||
# define UTILS_NORETURN __attribute__((noreturn))
|
||||
#else
|
||||
# define UTILS_NORETURN
|
||||
#endif
|
||||
|
||||
#if __has_attribute(fallthrough)
|
||||
# define UTILS_FALLTHROUGH [[fallthrough]]
|
||||
#else
|
||||
# define UTILS_FALLTHROUGH
|
||||
#endif
|
||||
|
||||
#if __has_attribute(visibility)
|
||||
# ifndef TNT_DEV
|
||||
# define UTILS_PRIVATE __attribute__((visibility("hidden")))
|
||||
# else
|
||||
# define UTILS_PRIVATE
|
||||
# endif
|
||||
#else
|
||||
# define UTILS_PRIVATE
|
||||
#endif
|
||||
|
||||
#define UTILS_NO_SANITIZE_THREAD
|
||||
#if __has_feature(thread_sanitizer)
|
||||
#undef UTILS_NO_SANITIZE_THREAD
|
||||
#define UTILS_NO_SANITIZE_THREAD __attribute__((no_sanitize("thread")))
|
||||
#endif
|
||||
|
||||
#define UTILS_HAS_SANITIZE_THREAD 0
|
||||
#if __has_feature(thread_sanitizer) || defined(__SANITIZE_THREAD__)
|
||||
#undef UTILS_HAS_SANITIZE_THREAD
|
||||
#define UTILS_HAS_SANITIZE_THREAD 1
|
||||
#endif
|
||||
|
||||
#define UTILS_HAS_SANITIZE_MEMORY 0
|
||||
#if __has_feature(memory_sanitizer)
|
||||
#undef UTILS_HAS_SANITIZE_MEMORY
|
||||
#define UTILS_HAS_SANITIZE_MEMORY 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* helps the compiler's optimizer predicting branches
|
||||
*/
|
||||
#if __has_builtin(__builtin_expect)
|
||||
# ifdef __cplusplus
|
||||
# define UTILS_LIKELY( exp ) (__builtin_expect( !!(exp), true ))
|
||||
# define UTILS_UNLIKELY( exp ) (__builtin_expect( !!(exp), false ))
|
||||
# else
|
||||
# define UTILS_LIKELY( exp ) (__builtin_expect( !!(exp), 1 ))
|
||||
# define UTILS_UNLIKELY( exp ) (__builtin_expect( !!(exp), 0 ))
|
||||
# endif
|
||||
#else
|
||||
# define UTILS_LIKELY( exp ) (!!(exp))
|
||||
# define UTILS_UNLIKELY( exp ) (!!(exp))
|
||||
#endif
|
||||
|
||||
#if __has_builtin(__builtin_prefetch)
|
||||
# define UTILS_PREFETCH( exp ) (__builtin_prefetch(exp))
|
||||
#else
|
||||
# define UTILS_PREFETCH( exp )
|
||||
#endif
|
||||
|
||||
#if __has_builtin(__builtin_assume)
|
||||
# define UTILS_ASSUME( exp ) (__builtin_assume(exp))
|
||||
#else
|
||||
# define UTILS_ASSUME( exp )
|
||||
#endif
|
||||
|
||||
#if (defined(__i386__) || defined(__x86_64__))
|
||||
# define UTILS_HAS_HYPER_THREADING 1 // on x86 we assume we have hyper-threading.
|
||||
#else
|
||||
# define UTILS_HAS_HYPER_THREADING 0
|
||||
#endif
|
||||
|
||||
#if defined(FILAMENT_SINGLE_THREADED)
|
||||
# define UTILS_HAS_THREADING 0
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
# if defined(__EMSCRIPTEN_PTHREADS__) && defined(FILAMENT_WASM_THREADS)
|
||||
# define UTILS_HAS_THREADING 1
|
||||
# else
|
||||
# define UTILS_HAS_THREADING 0
|
||||
# endif
|
||||
#else
|
||||
# define UTILS_HAS_THREADING 1
|
||||
#endif
|
||||
|
||||
#if __has_attribute(noinline)
|
||||
#define UTILS_NOINLINE __attribute__((noinline))
|
||||
#else
|
||||
#define UTILS_NOINLINE
|
||||
#endif
|
||||
|
||||
#if __has_attribute(always_inline)
|
||||
#define UTILS_ALWAYS_INLINE __attribute__((always_inline))
|
||||
#else
|
||||
#define UTILS_ALWAYS_INLINE
|
||||
#endif
|
||||
|
||||
#if __has_attribute(pure)
|
||||
#define UTILS_PURE __attribute__((pure))
|
||||
#else
|
||||
#define UTILS_PURE
|
||||
#endif
|
||||
|
||||
#if __has_attribute(maybe_unused) || (defined(_MSC_VER) && _MSC_VER >= 1911)
|
||||
#define UTILS_UNUSED [[maybe_unused]]
|
||||
#define UTILS_UNUSED_IN_RELEASE [[maybe_unused]]
|
||||
#elif __has_attribute(unused)
|
||||
#define UTILS_UNUSED __attribute__((unused))
|
||||
#define UTILS_UNUSED_IN_RELEASE __attribute__((unused))
|
||||
#else
|
||||
#define UTILS_UNUSED
|
||||
#define UTILS_UNUSED_IN_RELEASE
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1900
|
||||
# define UTILS_RESTRICT __restrict
|
||||
#elif (defined(__clang__) || defined(__GNUC__))
|
||||
# define UTILS_RESTRICT __restrict__
|
||||
#else
|
||||
# define UTILS_RESTRICT
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1900
|
||||
# define UTILS_HAS_FEATURE_CXX_THREAD_LOCAL 1
|
||||
#elif __has_feature(cxx_thread_local)
|
||||
# define UTILS_HAS_FEATURE_CXX_THREAD_LOCAL 1
|
||||
#else
|
||||
# define UTILS_HAS_FEATURE_CXX_THREAD_LOCAL 0
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
// MSVC does not support loop unrolling hints
|
||||
# define UTILS_UNROLL
|
||||
# define UTILS_NOUNROLL
|
||||
#else
|
||||
// C++11 allows pragmas to be specified as part of defines using the _Pragma syntax.
|
||||
# define UTILS_UNROLL _Pragma("unroll")
|
||||
# define UTILS_NOUNROLL _Pragma("nounroll")
|
||||
#endif
|
||||
|
||||
#if __has_feature(cxx_rtti) || defined(_CPPRTTI)
|
||||
# define UTILS_HAS_RTTI 1
|
||||
#else
|
||||
# define UTILS_HAS_RTTI 0
|
||||
#endif
|
||||
|
||||
#ifdef __ARM_ACLE
|
||||
# include <arm_acle.h>
|
||||
# define UTILS_WAIT_FOR_INTERRUPT() __wfi()
|
||||
# define UTILS_WAIT_FOR_EVENT() __wfe()
|
||||
# define UTILS_BROADCAST_EVENT() __sev()
|
||||
# define UTILS_SIGNAL_EVENT() __sevl()
|
||||
# define UTILS_PAUSE() __yield()
|
||||
# define UTILS_PREFETCHW(addr) __pldx(1, 0, 0, addr)
|
||||
#else // !__ARM_ACLE
|
||||
# if (defined(__i386__) || defined(__x86_64__))
|
||||
# define UTILS_X86_PAUSE {__asm__ __volatile__( "rep; nop" : : : "memory" );}
|
||||
# define UTILS_WAIT_FOR_INTERRUPT() UTILS_X86_PAUSE
|
||||
# define UTILS_WAIT_FOR_EVENT() UTILS_X86_PAUSE
|
||||
# define UTILS_BROADCAST_EVENT()
|
||||
# define UTILS_SIGNAL_EVENT()
|
||||
# define UTILS_PAUSE() UTILS_X86_PAUSE
|
||||
# define UTILS_PREFETCHW(addr) UTILS_PREFETCH(addr)
|
||||
# else // !x86
|
||||
# define UTILS_WAIT_FOR_INTERRUPT()
|
||||
# define UTILS_WAIT_FOR_EVENT()
|
||||
# define UTILS_BROADCAST_EVENT()
|
||||
# define UTILS_SIGNAL_EVENT()
|
||||
# define UTILS_PAUSE()
|
||||
# define UTILS_PREFETCHW(addr) UTILS_PREFETCH(addr)
|
||||
# endif // x86
|
||||
#endif // __ARM_ACLE
|
||||
|
||||
|
||||
// ssize_t is a POSIX type.
|
||||
#if defined(WIN32) || defined(_WIN32)
|
||||
#include <Basetsd.h>
|
||||
typedef SSIZE_T ssize_t;
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# define UTILS_EMPTY_BASES __declspec(empty_bases)
|
||||
#else
|
||||
# define UTILS_EMPTY_BASES
|
||||
#endif
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32)
|
||||
#define IMPORTSYMB __declspec(dllimport)
|
||||
#else
|
||||
#define IMPORTSYMB
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__PRETTY_FUNCTION__)
|
||||
# define __PRETTY_FUNCTION__ __FUNCSIG__
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# define UTILS_WARNING_PUSH _Pragma("warning( push )")
|
||||
# define UTILS_WARNING_POP _Pragma("warning( pop )")
|
||||
# define UTILS_WARNING_ENABLE_PADDED _Pragma("warning(1: 4324)")
|
||||
#elif defined(__clang__)
|
||||
# define UTILS_WARNING_PUSH _Pragma("clang diagnostic push")
|
||||
# define UTILS_WARNING_POP _Pragma("clang diagnostic pop")
|
||||
# define UTILS_WARNING_ENABLE_PADDED _Pragma("clang diagnostic warning \"-Wpadded\"")
|
||||
#else
|
||||
# define UTILS_WARNING_PUSH
|
||||
# define UTILS_WARNING_POP
|
||||
# define UTILS_WARNING_ENABLE_PADDED
|
||||
#endif
|
||||
|
||||
#endif // TNT_UTILS_COMPILER_H
|
||||
68
windows/include/filament/utils/compressed_pair.h
Normal file
68
windows/include/filament/utils/compressed_pair.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_UTILS_COMPRESSED_PAIR_H
|
||||
#define TNT_UTILS_COMPRESSED_PAIR_H
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace utils {
|
||||
|
||||
template<typename T, bool>
|
||||
struct dependent_type : public T {
|
||||
};
|
||||
|
||||
template<typename T1, typename T2,
|
||||
std::enable_if_t<!std::is_same_v<T1, T2>, bool> = true>
|
||||
struct compressed_pair : private T1, private T2 {
|
||||
|
||||
template<bool Dummy = true, typename = std::enable_if_t<
|
||||
dependent_type<std::is_default_constructible<T1>, Dummy>::value &&
|
||||
dependent_type<std::is_default_constructible<T2>, Dummy>::value>>
|
||||
compressed_pair() : T1(), T2() {}
|
||||
|
||||
template<typename U1, typename U2>
|
||||
compressed_pair(U1&& other1, U2&& other2)
|
||||
: T1(std::forward<U1>(other1)),
|
||||
T2(std::forward<U2>(other2)) {}
|
||||
|
||||
T1& first() noexcept {
|
||||
return static_cast<T1&>(*this);
|
||||
}
|
||||
|
||||
T2& second() noexcept {
|
||||
return static_cast<T2&>(*this);
|
||||
}
|
||||
|
||||
T1 const& first() const noexcept {
|
||||
return static_cast<T1 const&>(*this);
|
||||
}
|
||||
|
||||
T2 const& second() const noexcept {
|
||||
return static_cast<T2 const&>(*this);
|
||||
}
|
||||
|
||||
void swap(compressed_pair& other) noexcept {
|
||||
using std::swap;
|
||||
swap(first(), other.first());
|
||||
swap(second(), other.second());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace utils
|
||||
|
||||
#endif // TNT_UTILS_COMPRESSED_PAIR_H
|
||||
33
windows/include/filament/utils/debug.h
Normal file
33
windows/include/filament/utils/debug.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_UTILS_DEBUG_H
|
||||
#define TNT_UTILS_DEBUG_H
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
namespace utils {
|
||||
void panic(const char *func, const char * file, int line, const char *assertion) noexcept;
|
||||
} // namespace filament
|
||||
|
||||
#ifdef NDEBUG
|
||||
# define assert_invariant(e) ((void)0)
|
||||
#else
|
||||
# define assert_invariant(e) \
|
||||
(UTILS_LIKELY(e) ? ((void)0) : utils::panic(__func__, __FILE__, __LINE__, #e))
|
||||
#endif // NDEBUG
|
||||
|
||||
#endif // TNT_UTILS_DEBUG_H
|
||||
28
windows/include/filament/utils/generic/Mutex.h
Normal file
28
windows/include/filament/utils/generic/Mutex.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_UTILS_GENERIC_MUTEX_H
|
||||
#define TNT_UTILS_GENERIC_MUTEX_H
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace utils {
|
||||
|
||||
using Mutex = std::mutex;
|
||||
|
||||
} // namespace utils
|
||||
|
||||
#endif // TNT_UTILS_GENERIC_MUTEX_H
|
||||
113
windows/include/filament/utils/memalign.h
Normal file
113
windows/include/filament/utils/memalign.h
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_UTILS_MEMALIGN_H
|
||||
#define TNT_UTILS_MEMALIGN_H
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if defined(WIN32)
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
namespace utils {
|
||||
|
||||
inline void* aligned_alloc(size_t size, size_t align) noexcept {
|
||||
// 'align' must be a power of two and a multiple of sizeof(void*)
|
||||
align = (align < sizeof(void*)) ? sizeof(void*) : align;
|
||||
assert(align && !(align & align - 1));
|
||||
assert((align % sizeof(void*)) == 0);
|
||||
|
||||
void* p = nullptr;
|
||||
|
||||
#if defined(WIN32)
|
||||
p = ::_aligned_malloc(size, align);
|
||||
#else
|
||||
::posix_memalign(&p, align, size);
|
||||
#endif
|
||||
return p;
|
||||
}
|
||||
|
||||
inline void aligned_free(void* p) noexcept {
|
||||
#if defined(WIN32)
|
||||
::_aligned_free(p);
|
||||
#else
|
||||
::free(p);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* This allocator can be used with std::vector for instance to ensure all items are aligned
|
||||
* to their alignof(). e.g.
|
||||
*
|
||||
* template<typename T>
|
||||
* using aligned_vector = std::vector<T, utils::STLAlignedAllocator<T>>;
|
||||
*
|
||||
* aligned_vector<Foo> foos;
|
||||
*
|
||||
*/
|
||||
template<typename TYPE>
|
||||
class STLAlignedAllocator {
|
||||
static_assert(!(alignof(TYPE) & (alignof(TYPE) - 1)), "alignof(T) must be a power of two");
|
||||
|
||||
public:
|
||||
using value_type = TYPE;
|
||||
using pointer = TYPE*;
|
||||
using const_pointer = const TYPE*;
|
||||
using reference = TYPE&;
|
||||
using const_reference = const TYPE&;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using propagate_on_container_move_assignment = std::true_type;
|
||||
using is_always_equal = std::true_type;
|
||||
|
||||
template<typename T>
|
||||
struct rebind { using other = STLAlignedAllocator<T>; };
|
||||
|
||||
inline STLAlignedAllocator() noexcept = default;
|
||||
|
||||
template<typename T>
|
||||
inline explicit STLAlignedAllocator(const STLAlignedAllocator<T>&) noexcept {}
|
||||
|
||||
inline ~STLAlignedAllocator() noexcept = default;
|
||||
|
||||
inline pointer allocate(size_type n) noexcept {
|
||||
return (pointer)aligned_alloc(n * sizeof(value_type), alignof(TYPE));
|
||||
}
|
||||
|
||||
inline void deallocate(pointer p, size_type) {
|
||||
aligned_free(p);
|
||||
}
|
||||
|
||||
// stateless allocators are always equal
|
||||
template<typename T>
|
||||
bool operator==(const STLAlignedAllocator<T>&) const noexcept {
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool operator!=(const STLAlignedAllocator<T>&) const noexcept {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace utils
|
||||
|
||||
#endif // TNT_UTILS_MEMALIGN_H
|
||||
140
windows/include/filament/utils/ostream.h
Normal file
140
windows/include/filament/utils/ostream.h
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_UTILS_OSTREAM_H
|
||||
#define TNT_UTILS_OSTREAM_H
|
||||
|
||||
#include <utils/bitset.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/PrivateImplementation.h>
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
namespace utils::io {
|
||||
|
||||
struct ostream_;
|
||||
|
||||
class UTILS_PUBLIC ostream : protected utils::PrivateImplementation<ostream_> {
|
||||
friend struct ostream_;
|
||||
|
||||
public:
|
||||
virtual ~ostream();
|
||||
|
||||
ostream& operator<<(short value) noexcept;
|
||||
ostream& operator<<(unsigned short value) noexcept;
|
||||
|
||||
ostream& operator<<(char value) noexcept;
|
||||
ostream& operator<<(unsigned char value) noexcept;
|
||||
|
||||
ostream& operator<<(int value) noexcept;
|
||||
ostream& operator<<(unsigned int value) noexcept;
|
||||
|
||||
ostream& operator<<(long value) noexcept;
|
||||
ostream& operator<<(unsigned long value) noexcept;
|
||||
|
||||
ostream& operator<<(long long value) noexcept;
|
||||
ostream& operator<<(unsigned long long value) noexcept;
|
||||
|
||||
ostream& operator<<(float value) noexcept;
|
||||
ostream& operator<<(double value) noexcept;
|
||||
ostream& operator<<(long double value) noexcept;
|
||||
|
||||
ostream& operator<<(bool value) noexcept;
|
||||
|
||||
ostream& operator<<(const void* value) noexcept;
|
||||
|
||||
ostream& operator<<(const char* string) noexcept;
|
||||
ostream& operator<<(const unsigned char* string) noexcept;
|
||||
|
||||
ostream& operator<<(std::string const& s) noexcept;
|
||||
ostream& operator<<(std::string_view const& s) noexcept;
|
||||
|
||||
ostream& operator<<(ostream& (* f)(ostream&)) noexcept { return f(*this); }
|
||||
|
||||
ostream& dec() noexcept;
|
||||
ostream& hex() noexcept;
|
||||
|
||||
protected:
|
||||
ostream& print(const char* format, ...) noexcept;
|
||||
|
||||
class Buffer {
|
||||
public:
|
||||
Buffer() noexcept;
|
||||
~Buffer() noexcept;
|
||||
|
||||
Buffer(const Buffer&) = delete;
|
||||
Buffer& operator=(const Buffer&) = delete;
|
||||
|
||||
const char* get() const noexcept { return buffer; }
|
||||
|
||||
std::pair<char*, size_t> grow(size_t s) noexcept;
|
||||
void advance(ssize_t n) noexcept;
|
||||
void reset() noexcept;
|
||||
|
||||
private:
|
||||
void reserve(size_t newSize) noexcept;
|
||||
|
||||
char* buffer = nullptr; // buffer address
|
||||
char* curr = nullptr; // current pointer
|
||||
size_t size = 0; // size remaining
|
||||
size_t capacity = 0; // total capacity of the buffer
|
||||
};
|
||||
|
||||
Buffer& getBuffer() noexcept;
|
||||
Buffer const& getBuffer() const noexcept;
|
||||
|
||||
private:
|
||||
virtual ostream& flush() noexcept = 0;
|
||||
|
||||
friend ostream& hex(ostream& s) noexcept;
|
||||
friend ostream& dec(ostream& s) noexcept;
|
||||
friend ostream& endl(ostream& s) noexcept;
|
||||
friend ostream& flush(ostream& s) noexcept;
|
||||
|
||||
enum type {
|
||||
SHORT, USHORT, CHAR, UCHAR, INT, UINT, LONG, ULONG, LONG_LONG, ULONG_LONG, FLOAT, DOUBLE,
|
||||
LONG_DOUBLE
|
||||
};
|
||||
|
||||
const char* getFormat(type t) const noexcept;
|
||||
};
|
||||
|
||||
// handles utils::bitset
|
||||
inline ostream& operator << (ostream& o, utils::bitset32 const& s) noexcept {
|
||||
return o << (void*)uintptr_t(s.getValue());
|
||||
}
|
||||
|
||||
// handles vectors from libmath (but we do this generically, without needing a dependency on libmath)
|
||||
template<template<typename T> class VECTOR, typename T>
|
||||
inline ostream& operator<<(ostream& stream, const VECTOR<T>& v) {
|
||||
stream << "< ";
|
||||
for (size_t i = 0; i < v.size() - 1; i++) {
|
||||
stream << v[i] << ", ";
|
||||
}
|
||||
stream << v[v.size() - 1] << " >";
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline ostream& hex(ostream& s) noexcept { return s.hex(); }
|
||||
inline ostream& dec(ostream& s) noexcept { return s.dec(); }
|
||||
inline ostream& endl(ostream& s) noexcept { s << '\n'; return s.flush(); }
|
||||
inline ostream& flush(ostream& s) noexcept { return s.flush(); }
|
||||
|
||||
} // namespace utils::io
|
||||
|
||||
#endif // TNT_UTILS_OSTREAM_H
|
||||
51
windows/include/filament/utils/unwindows.h
Normal file
51
windows/include/filament/utils/unwindows.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#if defined (WIN32)
|
||||
|
||||
#ifdef max
|
||||
#undef max
|
||||
#endif
|
||||
|
||||
#ifdef min
|
||||
#undef min
|
||||
#endif
|
||||
|
||||
#ifdef far
|
||||
#undef far
|
||||
#endif
|
||||
|
||||
#ifdef near
|
||||
#undef near
|
||||
#endif
|
||||
|
||||
#ifdef ERROR
|
||||
#undef ERROR
|
||||
#endif
|
||||
|
||||
#ifdef OPAQUE
|
||||
#undef OPAQUE
|
||||
#endif
|
||||
|
||||
#ifdef TRANSPARENT
|
||||
#undef TRANSPARENT
|
||||
#endif
|
||||
|
||||
#ifdef PURE
|
||||
#undef PURE
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user