image_framework_ymj/include/open3d/3rdparty/utils/SingleInstanceComponentManager.h
2024-12-06 16:25:16 +08:00

315 lines
11 KiB
C++
Executable File

/*
* 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/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 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();
}
SingleInstanceComponentManager(SingleInstanceComponentManager&& rhs) noexcept {/* = default */}
SingleInstanceComponentManager& operator=(SingleInstanceComponentManager&& rhs) 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;
#pragma 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> 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().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