* better froxel records compression We used to compress froxel records by reusing the previous record on the left if it was identical, now we additionally check the record above if that fails. In practice this saves 10% to 30% space in the froxel record buffer, which is a scarce resource for us. e.g. if a light occupied 3x3 froxels in a given z-slice, without compression 9 records would be used, with left-only compression, only 3, and with left+above compression, only 1. * use size_t for in and out parameters this helps the compiler in many places, not having to cast from 64 to 32 bits. * implement bitset’s any() and all() operations with NEON * empty froxels are a common case, handle it first
377 lines
11 KiB
C++
377 lines
11 KiB
C++
/*
|
|
* 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 <algorithm>
|
|
#include <atomic>
|
|
|
|
#include <assert.h>
|
|
#include <stddef.h>
|
|
|
|
#include <utils/compiler.h>
|
|
|
|
namespace utils {
|
|
|
|
/*
|
|
* A fixed-size slice of a container
|
|
*/
|
|
template <typename T, typename SIZE_TYPE = uint32_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_TYPE;
|
|
|
|
Slice() noexcept = default;
|
|
|
|
template <typename Iter>
|
|
Slice(Iter begin, Iter end) noexcept
|
|
: mBegin(iterator(begin)),
|
|
mEndOffset(size_type(iterator(end)-iterator(begin))) {
|
|
}
|
|
|
|
template <typename Iter>
|
|
Slice(Iter begin, size_type count) noexcept
|
|
: mBegin(begin), mEndOffset(size_type(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;
|
|
|
|
template<typename Iter>
|
|
void set(Iter begin, size_type count) UTILS_RESTRICT noexcept {
|
|
mBegin = &*begin;
|
|
mEndOffset = size_type(count);
|
|
}
|
|
|
|
template<typename Iter>
|
|
void set(Iter begin, Iter end) UTILS_RESTRICT noexcept {
|
|
mBegin = &*begin;
|
|
mEndOffset = size_type(end - begin);
|
|
}
|
|
|
|
void swap(Slice& rhs) UTILS_RESTRICT noexcept {
|
|
std::swap(mBegin, rhs.mBegin);
|
|
std::swap(mEndOffset, rhs.mEndOffset);
|
|
}
|
|
|
|
void clear() UTILS_RESTRICT noexcept {
|
|
mBegin = nullptr;
|
|
mEndOffset = 0;
|
|
}
|
|
|
|
// size
|
|
size_t size() const UTILS_RESTRICT noexcept { return mEndOffset; }
|
|
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 &mBegin[mEndOffset]; }
|
|
const_iterator end() const UTILS_RESTRICT noexcept { return &mBegin[mEndOffset]; }
|
|
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;
|
|
size_type mEndOffset = 0;
|
|
};
|
|
|
|
/*
|
|
* A fixed-capacity (but growable) slice of a container
|
|
*/
|
|
template<typename T, typename SIZE_TYPE = uint32_t>
|
|
class UTILS_PRIVATE GrowingSlice : public Slice<T, SIZE_TYPE> {
|
|
public:
|
|
using iterator = typename Slice<T, SIZE_TYPE>::iterator;
|
|
using const_iterator = typename Slice<T, SIZE_TYPE>::const_iterator;
|
|
using value_type = typename Slice<T, SIZE_TYPE>::value_type;
|
|
using reference = typename Slice<T, SIZE_TYPE>::reference;
|
|
using const_reference = typename Slice<T, SIZE_TYPE>::const_reference;
|
|
using pointer = typename Slice<T, SIZE_TYPE>::pointer;
|
|
using const_pointer = typename Slice<T, SIZE_TYPE>::const_pointer;
|
|
using size_type = typename Slice<T, SIZE_TYPE>::size_type;
|
|
|
|
GrowingSlice() noexcept = default;
|
|
|
|
template<typename Iter>
|
|
GrowingSlice(Iter begin, size_type count) noexcept
|
|
: Slice<T, SIZE_TYPE>(begin, size_type(0)),
|
|
mCapOffset(count) {
|
|
}
|
|
|
|
GrowingSlice& operator=(GrowingSlice const& rhs) noexcept = default;
|
|
GrowingSlice& operator=(GrowingSlice&& rhs) noexcept = default;
|
|
|
|
// size
|
|
size_t remain() const UTILS_RESTRICT noexcept { return mCapOffset - this->mEndOffset; }
|
|
size_t capacity() const UTILS_RESTRICT noexcept { return mCapOffset; }
|
|
|
|
template<typename Iter>
|
|
void set(Iter begin, size_type count) UTILS_RESTRICT noexcept {
|
|
this->Slice<T, SIZE_TYPE>::set(begin, count);
|
|
mCapOffset = count;
|
|
}
|
|
|
|
template<typename Iter>
|
|
void set(Iter begin, Iter end) UTILS_RESTRICT noexcept {
|
|
this->Slice<T, SIZE_TYPE>::set(begin, end);
|
|
mCapOffset = size_type(end - begin);
|
|
}
|
|
|
|
void swap(GrowingSlice& rhs) UTILS_RESTRICT noexcept {
|
|
Slice<T, SIZE_TYPE>::swap(rhs);
|
|
std::swap(mCapOffset, rhs.mCapOffset);
|
|
}
|
|
|
|
void clear() UTILS_RESTRICT noexcept {
|
|
this->mEndOffset = 0;
|
|
}
|
|
|
|
void resize(size_type count) UTILS_RESTRICT noexcept {
|
|
assert(count < mCapOffset);
|
|
this->mEndOffset = size_type(count);
|
|
}
|
|
|
|
T* grow(size_type count) UTILS_RESTRICT noexcept {
|
|
assert(this->size() + count <= mCapOffset);
|
|
size_t offset = this->mEndOffset;
|
|
this->mEndOffset += count;
|
|
return this->mBegin + offset;
|
|
}
|
|
|
|
// data access
|
|
void push_back(T const& item) UTILS_RESTRICT noexcept {
|
|
T* const p = this->grow(1);
|
|
*p = item;
|
|
}
|
|
|
|
void push_back(T&& item) UTILS_RESTRICT noexcept {
|
|
T* const p = this->grow(1);
|
|
*p = std::move(item);
|
|
}
|
|
|
|
template<typename ... ARGS>
|
|
void emplace_back(ARGS&& ... args) UTILS_RESTRICT noexcept {
|
|
T* const p = this->grow(1);
|
|
new(p) T(std::forward<ARGS>(args)...);
|
|
}
|
|
|
|
private:
|
|
// we use size_type == uint32_t to reduce the size on 64-bits machines
|
|
size_type mCapOffset = 0;
|
|
};
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
/*
|
|
* A fixed-capacity (but atomically growable) slice of a container
|
|
*/
|
|
template <typename T, typename SIZE_TYPE = uint32_t>
|
|
class AtomicGrowingSlice {
|
|
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_TYPE;
|
|
|
|
AtomicGrowingSlice() noexcept = default;
|
|
|
|
template<typename Iter>
|
|
AtomicGrowingSlice(Iter begin, Iter end) noexcept
|
|
: mBegin(iterator(begin)),
|
|
mEndOffset(0),
|
|
mCapOffset(size_type(iterator(end) - iterator(begin))) {
|
|
}
|
|
|
|
template<typename Iter>
|
|
AtomicGrowingSlice(Iter begin, size_type count) noexcept
|
|
: mBegin(iterator(begin)), mEndOffset(0), mCapOffset(size_type(count)) {
|
|
}
|
|
|
|
template<typename Iter>
|
|
void set(Iter begin, size_type count) noexcept {
|
|
assert(mBegin == nullptr);
|
|
mBegin = iterator(begin);
|
|
mEndOffset.store(0, std::memory_order_relaxed);
|
|
mCapOffset = count;
|
|
}
|
|
|
|
// clear
|
|
void clear() noexcept {
|
|
mEndOffset.store(0, std::memory_order_relaxed);
|
|
}
|
|
|
|
// size
|
|
size_type size() const noexcept { return mEndOffset.load(std::memory_order_relaxed); }
|
|
bool empty() const noexcept { return size() == 0; }
|
|
size_type remain() const noexcept { return mCapOffset - size(); }
|
|
size_type capacity() const noexcept { return mCapOffset; }
|
|
|
|
// iterators
|
|
iterator begin() noexcept { return mBegin; }
|
|
const_iterator begin() const noexcept { return mBegin; }
|
|
const_iterator cbegin() const noexcept { return begin(); }
|
|
iterator end() noexcept { return &mBegin[size()]; }
|
|
const_iterator end() const noexcept { return &mBegin[size()]; }
|
|
const_iterator cend() const noexcept { return end(); }
|
|
|
|
// data access
|
|
reference operator[](size_type n) noexcept {
|
|
assert(n < size());
|
|
return mBegin[n];
|
|
}
|
|
|
|
const_reference operator[](size_type n) const noexcept {
|
|
assert(n < size());
|
|
return mBegin[n];
|
|
}
|
|
|
|
reference at(size_type n) noexcept {
|
|
return operator[](n);
|
|
}
|
|
|
|
const_reference at(size_type n) const noexcept {
|
|
return operator[](n);
|
|
}
|
|
|
|
reference front() noexcept {
|
|
assert(!empty());
|
|
return *mBegin;
|
|
}
|
|
|
|
const_reference front() const noexcept {
|
|
assert(!empty());
|
|
return *mBegin;
|
|
}
|
|
|
|
reference back() noexcept {
|
|
assert(!empty());
|
|
return *(end() - 1);
|
|
}
|
|
|
|
const_reference back() const noexcept {
|
|
assert(!empty());
|
|
return *(end() - 1);
|
|
}
|
|
|
|
pointer data() noexcept {
|
|
return begin();
|
|
}
|
|
|
|
const_pointer data() const noexcept {
|
|
return begin();
|
|
}
|
|
|
|
T* grow(size_type count) noexcept {
|
|
size_type offset = this->mEndOffset.load(std::memory_order_relaxed);
|
|
do {
|
|
if (UTILS_UNLIKELY(offset + count > mCapOffset)) {
|
|
return nullptr;
|
|
}
|
|
} while (UTILS_UNLIKELY(!this->mEndOffset.compare_exchange_weak(offset, offset + count,
|
|
std::memory_order_relaxed, std::memory_order_relaxed)));
|
|
return this->mBegin + offset;
|
|
}
|
|
|
|
// data access
|
|
void push_back(T const& item) noexcept {
|
|
T* const p = this->grow(1);
|
|
*p = item;
|
|
}
|
|
|
|
void push_back(T&& item) noexcept {
|
|
T* const p = this->grow(1);
|
|
*p = std::move(item);
|
|
}
|
|
|
|
template<typename ... ARGS>
|
|
void emplace_back(ARGS&& ... args) noexcept {
|
|
T* const p = this->grow(1);
|
|
new(p) T(std::forward<ARGS>(args)...);
|
|
}
|
|
|
|
private:
|
|
iterator mBegin = nullptr;
|
|
std::atomic<size_type> mEndOffset = ATOMIC_VAR_INIT(0);
|
|
size_type mCapOffset = 0;
|
|
};
|
|
|
|
} // namespace utils
|
|
|
|
#endif // TNT_UTILS_SLICE_H
|