GameObjectPool fixes

This commit is contained in:
Timerix 2024-09-14 21:28:18 +05:00
parent 3df4361779
commit 47574e9b30
6 changed files with 80 additions and 27 deletions

View File

@ -11,10 +11,19 @@ GameObject::GameObject(Mono::Object managed_obj, GameObject *parent)
: object_handle(managed_obj), parent(parent) : object_handle(managed_obj), parent(parent)
{} {}
GameObject::GameObject(GameObject &&o) :
transform(o.transform),
components(std::move(o.components)),
object_handle(std::move(o.object_handle)),
parent(o.parent)
{
}
GameObject &GameObject::operator=(GameObject &&o){ GameObject &GameObject::operator=(GameObject &&o){
transform = o.transform; transform = o.transform;
components = std::move(o.components); components = std::move(o.components);
object_handle = std::move(o.object_handle); object_handle = std::move(o.object_handle);
parent = o.parent;
return *this; return *this;
} }

View File

@ -32,10 +32,15 @@ class GameObject {
GameObject* parent; GameObject* parent;
public: public:
GameObject(Mono::Object managed_obj, GameObject* parent); GameObject(Mono::Object managed_obj, GameObject* parent);
GameObject(const GameObject& o) = delete;
GameObject(GameObject&& o);
GameObject& operator=(GameObject&& o); GameObject& operator=(GameObject&& o);
inline Transform& getTransform(){ return transform; } inline Transform& getTransform() { return transform; }
inline const Transform& getTransform() const { return transform; }
inline Mono::ObjectHandle& getObjectHandle() { return object_handle; }
inline const Mono::ObjectHandle& getObjectHandle() const { return object_handle; }
bool tryGetComponent(const std::u16string& name, Component** out_component); bool tryGetComponent(const std::u16string& name, Component** out_component);
bool tryAddComponent(const std::u16string& name, Component&& component); bool tryAddComponent(const std::u16string& name, Component&& component);
}; };

View File

@ -9,7 +9,7 @@ GameObjectPool::GameObjectPool(u32 size)
first_unused_index = 0; first_unused_index = 0;
buffer = new char[size*sizeof(GameObject)]; buffer = new char[size*sizeof(GameObject)];
used_indices = new u64[size/64]; used_indices = new u64[size/64];
std::memset(buffer, 0, size*sizeof(GameObject)); // std::memset(buffer, 0, size*sizeof(GameObject));
std::memset(used_indices, 0, size/8); std::memset(used_indices, 0, size/8);
} }
@ -26,7 +26,7 @@ GameObjectPool::~GameObjectPool()
bool GameObjectPool::isIndexUsed(u32 index) bool GameObjectPool::isIndexUsed(u32 index)
{ {
return ( used_indices[index/64] & (1<<(index%64)) ) != 0; return ( used_indices[index/64] & (u64(1)<<(index%64)) ) != 0;
} }
u32 GameObjectPool::getNearestUnusedIndex(u32 startIndex) u32 GameObjectPool::getNearestUnusedIndex(u32 startIndex)
@ -38,7 +38,7 @@ u32 GameObjectPool::getNearestUnusedIndex(u32 startIndex)
u32 i = startIndex/64; u32 i = startIndex/64;
// mark previous bits as used // mark previous bits as used
u64 u = used_indices[i] | ( (1<<startIndex%64) -1 ); u64 u = used_indices[i] | ( (u64(1)<<startIndex%64) -1 );
while(u == u64(-1)){ while(u == u64(-1)){
i++; i++;
if(i == size/64) if(i == size/64)
@ -61,7 +61,7 @@ u32 GameObjectPool::getNearestUsedIndex(u32 startIndex)
u32 i = startIndex/64; u32 i = startIndex/64;
// mark previous bits as unused // mark previous bits as unused
u64 u = used_indices[i] & !( (1<<startIndex%64) -1 ); u64 u = used_indices[i] & !( (u64(1)<<startIndex%64) -1 );
while(u == 0){ while(u == 0){
i++; i++;
if(i == size/64) if(i == size/64)
@ -84,25 +84,25 @@ GameObject& GameObjectPool::get(u32 index)
return ((GameObject*)buffer)[index]; return ((GameObject*)buffer)[index];
} }
std::pair<u32, GameObject&> GameObjectPool::put_new(GameObject&& new_obj) std::pair<u32, GameObject&> GameObjectPool::emplace(GameObject&& new_obj)
{ {
u32 i = first_unused_index; u32 i = first_unused_index;
if(i == (u32)-1) if(i == u32(-1))
throw UsefulException("can't put new GameObject to GameObjectPool because it's full"); throw UsefulException("can't put new GameObject to GameObjectPool because it's full");
GameObject& r = ( ((GameObject*)buffer)[i] = std::move(new_obj) ); GameObject& r = ( ((GameObject*)buffer)[i] = std::move(new_obj) );
used_indices[i] |= 1<<(i%64); // mark index bit as used used_indices[i/64] |= u64(1)<<(i%64); // mark index bit as used
first_unused_index = getNearestUnusedIndex(i+1); first_unused_index = getNearestUnusedIndex(i+1);
return std::pair<u32, GameObject&>(i, r); return std::pair<u32, GameObject&>(i, r);
} }
void GameObjectPool::remove(u32 index) void GameObjectPool::erase(u32 index)
{ {
if(index >= size) if(index >= size)
throw UsefulException(format("index %i is out of size %i", index, size)); throw UsefulException(format("index %i is out of size %i", index, size));
if(!isIndexUsed(index)) if(!isIndexUsed(index))
throw UsefulException(format("there is no object at index %i", index)); throw UsefulException(format("there is no object at index %i", index));
((GameObject*)buffer)[index].~GameObject(); ((GameObject*)buffer)[index].~GameObject();
used_indices[index/64] ^= ~(1<<(index%64)); // mark index bit as unused used_indices[index/64] &= ~(u64(1)<<(index%64)); // mark index bit as unused
if(index < first_unused_index) if(index < first_unused_index)
first_unused_index = index; first_unused_index = index;
} }

View File

@ -1,5 +1,32 @@
#include "GameObject.hpp" #include "GameObject.hpp"
/*
Fixed array stkring deleted elements indices as bits in array of u64.
Fast emplace, erase and lookup.
------------------------[construct]------------------------
operation 'GameObjectPool::GameObjectPool()' took 1.0549 ms
operation 'other_collections_construct' took 0.0133 ms
-------------------------[emplace]-------------------------
operation 'GameObjectPool::emplace' took 8.0557 ms
operation 'vector::emplace_back' took 11.3735 ms
operation 'set::emplace' took 80.5633 ms
operation 'list::emplace_front' took 18.1442 ms
operation 'forward_list::emplace_front' took 11.5467 ms
--------------------------[erase]--------------------------
operation 'GameObjectPool::erase' took 0.2745 ms
operation 'vector::erase' took 15790.6 ms
operation 'set::erase' took 1.2697 ms
operation 'list::erase_after' took 0.93 ms
operation 'forward_list::erase_after' took 1.1127 ms
-------------------------[iterate]-------------------------
operation 'GameObjectPool::iterate' took 1.1166 ms
operation 'vector::iterate' took 0.8883 ms
operation 'set::iterate' took 2.8011 ms
operation 'list::iterate' took 2.0766 ms
operation 'forward_list::iterate' took 2.0823 ms
*/
class GameObjectPool { class GameObjectPool {
void* buffer; void* buffer;
u64* used_indices; u64* used_indices;
@ -9,16 +36,14 @@ class GameObjectPool {
bool isIndexUsed(u32 index); bool isIndexUsed(u32 index);
u32 getNearestUnusedIndex(u32 startIndex); u32 getNearestUnusedIndex(u32 startIndex);
u32 getNearestUsedIndex(u32 startIndex); u32 getNearestUsedIndex(u32 startIndex);
friend class iterator;
public: public:
///@param size must be a multiple of 64 ///@param size must be a multiple of 64
GameObjectPool(u32 size); GameObjectPool(u32 size);
~GameObjectPool(); ~GameObjectPool();
GameObject& get(u32 index); GameObject& get(u32 index);
std::pair<u32, GameObject&> put_new(GameObject&& new_obj); std::pair<u32, GameObject&> emplace(GameObject&& new_obj);
void remove(u32 index); void erase(u32 index);
class iterator { class iterator {
GameObjectPool* p; GameObjectPool* p;
@ -34,4 +59,6 @@ public:
inline iterator begin() { return iterator(this, 0); } inline iterator begin() { return iterator(this, 0); }
inline iterator end() { return iterator(this, -1); } inline iterator end() { return iterator(this, -1); }
friend class iterator;
}; };

View File

@ -128,13 +128,12 @@ class ObjectHandle {
Object object; Object object;
u32 gc_handle; u32 gc_handle;
public: public:
inline ObjectHandle() : object(nullptr), gc_handle(0) {}
inline ObjectHandle(Object obj) : object(obj) { gc_handle = mono_gchandle_new(obj, false); } inline ObjectHandle(Object obj) : object(obj) { gc_handle = mono_gchandle_new(obj, false); }
inline ObjectHandle(const ObjectHandle& o) = delete; inline ObjectHandle(const ObjectHandle& o) = delete;
inline ObjectHandle(ObjectHandle&& o) { inline ObjectHandle(ObjectHandle&& o)
object = o.object; : object(o.object), gc_handle(o.gc_handle)
gc_handle = o.gc_handle; { o.gc_handle = 0; };
o.gc_handle = 0;
};
inline ObjectHandle& operator=(ObjectHandle&& o) { inline ObjectHandle& operator=(ObjectHandle&& o) {
object = o.object; object = o.object;
gc_handle = o.gc_handle; gc_handle = o.gc_handle;
@ -142,7 +141,8 @@ public:
return *this; return *this;
}; };
inline ~ObjectHandle() { if(gc_handle) mono_gchandle_free(gc_handle); } inline ~ObjectHandle() { if(gc_handle) mono_gchandle_free(gc_handle); }
inline Object getObject() { return object; } inline Object getObject() const { return object; }
inline u32 getGCHandle() const { return gc_handle; }
}; };
} }

View File

@ -8,11 +8,22 @@
#include "UsefulException.hpp" #include "UsefulException.hpp"
#include "Mono/Mono.hpp" #include "Mono/Mono.hpp"
#include "Game/GameObjectPool.hpp" #include "Game/GameObjectPool.hpp"
#include <bitset>
#include <bit>
using namespace ougge; using namespace ougge;
#define GAMEOBJECTPOOL_SIZE 64*1024
#define optime(N, LABEL, CODE) {\
nsec_t b = getMonotonicTimeNsec();\
for(u32 i = 0; i < (u32)N; i++) {\
CODE ;\
}\
nsec_t e = getMonotonicTimeNsec();\
nsec_t t = e-b;\
std::cout<<"operation '"<<LABEL<<"' took "<<t/1e6f<<" ms"<<std::endl;\
}\
std::vector<GUI::UpdateFunc_t> updateCallbacks; std::vector<GUI::UpdateFunc_t> updateCallbacks;
void update(f64 deltaTime){ void update(f64 deltaTime){
@ -28,15 +39,16 @@ int main(int argc, const char** argv){
Mono::RuntimeJIT mono; Mono::RuntimeJIT mono;
std::cout<<"initialized mono jit runtime"<<std::endl; std::cout<<"initialized mono jit runtime"<<std::endl;
GameObjectPool p(64*512); GameObjectPool p(GAMEOBJECTPOOL_SIZE);
auto a = mono.loadAssembly("Ougge.dll"); auto a = mono.loadAssembly("Ougge.dll");
auto gameObjectClass = a->getClass("Ougge", "GameObject"); auto gameObjectClass = a->getClass("Ougge", "GameObject");
for(int i = 0; i < 32*1024; i++){ for(int i = 0; i < GAMEOBJECTPOOL_SIZE; i++){
Mono::Object gameObjectManaged = mono_object_new(mono.getDomain(), gameObjectClass); Mono::Object gameObjectManaged = mono_object_new(mono.getDomain(), gameObjectClass);
p.put_new(GameObject(gameObjectManaged, nullptr)); p.emplace(GameObject(gameObjectManaged, nullptr));
GameObject& o = p.get(0); GameObject& o = p.get(0);
std::cout<<'['<<i<<"] "<<o.getTransform()<<std::endl; if(i+1 % 8192 == 0)
std::cout<<'['<<i<<"] "<<o.getTransform()<<std::endl;
} }
return 0; return 0;