diff options
author | Loek Le Blansch <loek@pipeframe.xyz> | 2024-10-17 17:07:55 +0200 |
---|---|---|
committer | Loek Le Blansch <loek@pipeframe.xyz> | 2024-10-17 17:07:55 +0200 |
commit | e0fab8139a4dd8a33e54cb7fdea85d2bd3feaeb9 (patch) | |
tree | d7cc1a2583899d4557e54a2b33dee342f0405a2e /mwe | |
parent | d21afe5b33b4cb3f5cf1917f4d15f402de41a032 (diff) | |
parent | d9889e4501c1f3ebd649b81816e80d1b40d14c87 (diff) |
Merge branch 'max/POC-ECS-memory-efficient' of github.com:lonkaars/crepe
Diffstat (limited to 'mwe')
-rw-r--r-- | mwe/ecs-memory-efficient/CMakeLists.txt | 17 | ||||
-rw-r--r-- | mwe/ecs-memory-efficient/inc/ComponentManager.h | 36 | ||||
-rw-r--r-- | mwe/ecs-memory-efficient/inc/ComponentManager.hpp | 15 | ||||
-rw-r--r-- | mwe/ecs-memory-efficient/inc/Components.h | 33 | ||||
-rw-r--r-- | mwe/ecs-memory-efficient/inc/ContiguousContainer.h | 34 | ||||
-rw-r--r-- | mwe/ecs-memory-efficient/inc/ContiguousContainer.hpp | 84 | ||||
-rw-r--r-- | mwe/ecs-memory-efficient/inc/GameObjectMax.h | 24 | ||||
-rw-r--r-- | mwe/ecs-memory-efficient/inc/GameObjectMax.hpp | 16 | ||||
-rw-r--r-- | mwe/ecs-memory-efficient/src/ComponentManager.cpp | 21 | ||||
-rw-r--r-- | mwe/ecs-memory-efficient/src/Components.cpp | 11 | ||||
-rw-r--r-- | mwe/ecs-memory-efficient/src/GameObjectMax.cpp | 7 | ||||
-rw-r--r-- | mwe/ecs-memory-efficient/src/main.cpp | 54 |
12 files changed, 352 insertions, 0 deletions
diff --git a/mwe/ecs-memory-efficient/CMakeLists.txt b/mwe/ecs-memory-efficient/CMakeLists.txt new file mode 100644 index 0000000..d072907 --- /dev/null +++ b/mwe/ecs-memory-efficient/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.5) +project(ecs-memory-efficient) + +# Set the C++ standard (optional, but good practice) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +# Use the debug mode (otherwise breakpoints are not compiled) +set(CMAKE_BUILD_TYPE Debug) + +add_executable(ecs-memory-efficient + src/main.cpp + src/ComponentManager.cpp + src/Components.cpp + src/GameObjectMax.cpp +) +target_include_directories(ecs-memory-efficient PRIVATE "${CMAKE_SOURCE_DIR}/inc") diff --git a/mwe/ecs-memory-efficient/inc/ComponentManager.h b/mwe/ecs-memory-efficient/inc/ComponentManager.h new file mode 100644 index 0000000..066795a --- /dev/null +++ b/mwe/ecs-memory-efficient/inc/ComponentManager.h @@ -0,0 +1,36 @@ +#pragma once + +#include "Components.h" +#include "ContiguousContainer.h" + +class ComponentManager { +public: + static ComponentManager& GetInstance(); //Singleton + + ComponentManager(const ComponentManager&) = delete; //Singleton + ComponentManager(ComponentManager&&) = delete; //Singleton + ComponentManager& operator=(const ComponentManager&) = delete; //Singleton + ComponentManager& operator=(ComponentManager&&) = delete; //Singleton + + template<typename... Args> + void addSpriteComponent(Args&&... args); + template<typename... Args> + void addRigidbodyComponent(Args&&... args); + template<typename... Args> + void addColiderComponent(Args&&... args); + + std::vector<std::reference_wrapper<Sprite>> getAllSpriteReferences(); + std::vector<std::reference_wrapper<Rigidbody>> getAllRigidbodyReferences(); + std::vector<std::reference_wrapper<Colider>> getAllColiderReferences(); + +private: + static ComponentManager mInstance; //Singleton + + ComponentManager(); //Singleton + + ContiguousContainer<Sprite> mSpriteContainer; + ContiguousContainer<Rigidbody> mRigidbodyContainer; + ContiguousContainer<Colider> mColiderContainer; +}; + +#include "ComponentManager.hpp" diff --git a/mwe/ecs-memory-efficient/inc/ComponentManager.hpp b/mwe/ecs-memory-efficient/inc/ComponentManager.hpp new file mode 100644 index 0000000..1607c0c --- /dev/null +++ b/mwe/ecs-memory-efficient/inc/ComponentManager.hpp @@ -0,0 +1,15 @@ + +template<typename... Args> +void ComponentManager::addSpriteComponent(Args&&... args) { + mSpriteContainer.pushBack(std::forward<Args>(args)...); +} + +template<typename... Args> +void ComponentManager::addRigidbodyComponent(Args&&... args) { + mRigidbodyContainer.pushBack(std::forward<Args>(args)...); +} + +template<typename... Args> +void ComponentManager::addColiderComponent(Args&&... args){ + mColiderContainer.pushBack(std::forward<Args>(args)...); +} diff --git a/mwe/ecs-memory-efficient/inc/Components.h b/mwe/ecs-memory-efficient/inc/Components.h new file mode 100644 index 0000000..98c5fe7 --- /dev/null +++ b/mwe/ecs-memory-efficient/inc/Components.h @@ -0,0 +1,33 @@ +#pragma once + +#include <string> + +class Component { +public: + Component(); + + bool mActive; +}; + +class Sprite : public Component { +public: + Sprite(std::string path); + + std::string mPath; +}; + +class Rigidbody : public Component { +public: + Rigidbody(int mass, int gravityScale, int bodyType); + + int mMass; + int mGravityScale; + int mBodyType; +}; + +class Colider : public Component { +public: + Colider(int size); + + int mSize; +}; diff --git a/mwe/ecs-memory-efficient/inc/ContiguousContainer.h b/mwe/ecs-memory-efficient/inc/ContiguousContainer.h new file mode 100644 index 0000000..dd0321e --- /dev/null +++ b/mwe/ecs-memory-efficient/inc/ContiguousContainer.h @@ -0,0 +1,34 @@ +#pragma once + +#include <cstdlib> // For malloc and free +#include <new> // For placement new +#include <utility> // For std::move and std::forward +#include <stdexcept> // For std::bad_alloc +#include <vector> // For returning references + +template<typename T> +class ContiguousContainer { +public: + ContiguousContainer(); + ~ContiguousContainer(); + + // Use perfect forwarding for pushBack + template<typename... Args> + void pushBack(Args&&... args); + + void popBack(); + T& operator[](size_t index); + size_t getSize() const; + + // Function to return references to all stored objects + std::vector<std::reference_wrapper<T>> getAllReferences(); + +private: + T* mData; + size_t mSize; + size_t mCapacity; + + void resize(size_t new_capacity); // Resize function to allocate more space +}; + +#include "ContiguousContainer.hpp" diff --git a/mwe/ecs-memory-efficient/inc/ContiguousContainer.hpp b/mwe/ecs-memory-efficient/inc/ContiguousContainer.hpp new file mode 100644 index 0000000..878a85f --- /dev/null +++ b/mwe/ecs-memory-efficient/inc/ContiguousContainer.hpp @@ -0,0 +1,84 @@ +template<typename T> +ContiguousContainer<T>::ContiguousContainer() + : mSize(0), mCapacity(10) { + // Allocate memory for 10 objects initially + mData = static_cast<T*>(malloc(mCapacity * sizeof(T))); + if (!mData) { + throw std::bad_alloc(); + } +} + +template<typename T> +ContiguousContainer<T>::~ContiguousContainer() { + // Destroy all constructed objects + for (size_t i = 0; i < mSize; ++i) { + mData[i].~T(); + } + // Free the allocated memory + free(mData); +} + +template<typename T> +template<typename... Args> +void ContiguousContainer<T>::pushBack(Args&&... args) { + if (mSize == mCapacity) { + // Double the capacity if the container is full + resize(mCapacity * 2); + } + // Use placement new with perfect forwarding to construct the object in place + new (mData + mSize) T(std::forward<Args>(args)...); + ++mSize; +} + +template<typename T> +void ContiguousContainer<T>::popBack() { + if (mSize > 0) { + --mSize; + // Explicitly call the destructor + mData[mSize].~T(); + } +} + +template<typename T> +T& ContiguousContainer<T>::operator[](size_t index) { + if (index >= mSize) { + throw std::out_of_range("Index out of range"); + } + return mData[index]; +} + +template<typename T> +size_t ContiguousContainer<T>::getSize() const { + return mSize; +} + +// Function that returns a vector of references to all stored objects +template<typename T> +std::vector<std::reference_wrapper<T>> ContiguousContainer<T>::getAllReferences() { + std::vector<std::reference_wrapper<T>> references; + references.reserve(mSize); // Reserve space to avoid reallocation + for (size_t i = 0; i < mSize; ++i) { + references.push_back(std::ref(mData[i])); + } + return references; +} + +template<typename T> +void ContiguousContainer<T>::resize(size_t new_capacity) { + // Allocate new memory block with the updated capacity + T* new_data = static_cast<T*>(malloc(new_capacity * sizeof(T))); + if (!new_data) { + throw std::bad_alloc(); + } + + // Move or copy existing objects to the new memory block + for (size_t i = 0; i < mSize; ++i) { + new (new_data + i) T(std::move(mData[i])); // Move the objects + mData[i].~T(); // Call the destructor for the old object + } + + // Free the old memory block + free(mData); + mData = new_data; + mCapacity = new_capacity; +} diff --git a/mwe/ecs-memory-efficient/inc/GameObjectMax.h b/mwe/ecs-memory-efficient/inc/GameObjectMax.h new file mode 100644 index 0000000..62cd3e6 --- /dev/null +++ b/mwe/ecs-memory-efficient/inc/GameObjectMax.h @@ -0,0 +1,24 @@ +#pragma once + +#include <cstdint> +#include <string> + +class GameObject { +public: + GameObject(std::uint32_t id, std::string name, std::string tag, int layer); + + template<typename... Args> + void addSpriteComponent(Args&&... args); + template<typename... Args> + void addRigidbodyComponent(Args&&... args); + template<typename... Args> + void addColiderComponent(Args&&... args); + + std::uint32_t mId; + std::string mName; + std::string mTag; + bool mActive; + int mLayer; +}; + +#include "GameObjectMax.hpp" diff --git a/mwe/ecs-memory-efficient/inc/GameObjectMax.hpp b/mwe/ecs-memory-efficient/inc/GameObjectMax.hpp new file mode 100644 index 0000000..aac9811 --- /dev/null +++ b/mwe/ecs-memory-efficient/inc/GameObjectMax.hpp @@ -0,0 +1,16 @@ +#include "ComponentManager.h" + +template <typename... Args> +void GameObject::addSpriteComponent(Args&&... args) { + ComponentManager::GetInstance().addSpriteComponent(std::forward<Args>(args)...); +} + +template <typename... Args> +void GameObject::addRigidbodyComponent(Args&&... args) { + ComponentManager::GetInstance().addRigidbodyComponent(std::forward<Args>(args)...); +} + +template <typename... Args> +void GameObject::addColiderComponent(Args&&... args) { + ComponentManager::GetInstance().addColiderComponent(std::forward<Args>(args)...); +} diff --git a/mwe/ecs-memory-efficient/src/ComponentManager.cpp b/mwe/ecs-memory-efficient/src/ComponentManager.cpp new file mode 100644 index 0000000..20d0ce0 --- /dev/null +++ b/mwe/ecs-memory-efficient/src/ComponentManager.cpp @@ -0,0 +1,21 @@ +#include "ComponentManager.h" + +ComponentManager ComponentManager::mInstance; + +ComponentManager& ComponentManager::GetInstance() { + return mInstance; +} + +ComponentManager::ComponentManager() {} + +std::vector<std::reference_wrapper<Sprite>> ComponentManager::getAllSpriteReferences() { + return mSpriteContainer.getAllReferences(); +} + +std::vector<std::reference_wrapper<Rigidbody>> ComponentManager::getAllRigidbodyReferences() { + return mRigidbodyContainer.getAllReferences(); +} + +std::vector<std::reference_wrapper<Colider>> ComponentManager::getAllColiderReferences() { + return mColiderContainer.getAllReferences(); +} diff --git a/mwe/ecs-memory-efficient/src/Components.cpp b/mwe/ecs-memory-efficient/src/Components.cpp new file mode 100644 index 0000000..c8347b3 --- /dev/null +++ b/mwe/ecs-memory-efficient/src/Components.cpp @@ -0,0 +1,11 @@ +#include "Components.h" +#include <iostream> + +Component::Component() : mActive(true) {} + +Sprite::Sprite(std::string path) : mPath(path) {} + +Rigidbody::Rigidbody(int mass, int gravityScale, int bodyType) + : mMass(mass), mGravityScale(gravityScale), mBodyType(bodyType) {} + +Colider::Colider(int size) : mSize(size) {} diff --git a/mwe/ecs-memory-efficient/src/GameObjectMax.cpp b/mwe/ecs-memory-efficient/src/GameObjectMax.cpp new file mode 100644 index 0000000..b0c5af7 --- /dev/null +++ b/mwe/ecs-memory-efficient/src/GameObjectMax.cpp @@ -0,0 +1,7 @@ +#include "GameObjectMax.h" + +#include "ComponentManager.h" + +GameObject::GameObject(std::uint32_t id, std::string name, std::string tag, + int layer) + : mId(id), mName(name), mTag(tag), mActive(true), mLayer(layer) {} diff --git a/mwe/ecs-memory-efficient/src/main.cpp b/mwe/ecs-memory-efficient/src/main.cpp new file mode 100644 index 0000000..c25816b --- /dev/null +++ b/mwe/ecs-memory-efficient/src/main.cpp @@ -0,0 +1,54 @@ +#include <iostream> +#include <vector> +#include <cstdint> +#include <chrono> + +#include "ComponentManager.h" +#include "GameObjectMax.h" +#include "Components.h" + +int main() { + auto startAdding = std::chrono::high_resolution_clock::now(); + + GameObject* gameObject[100000]; + + for(int i = 0; i < 100000; ++i) { + gameObject[i] = new GameObject(i, "Name", "Tag", 0); + + gameObject[i]->addSpriteComponent("C:/Test"); + gameObject[i]->addRigidbodyComponent(0, 0, i); + gameObject[i]->addColiderComponent(i); + } + + auto stopAdding = std::chrono::high_resolution_clock::now(); + + //This is what systems would do: + + std::vector<std::reference_wrapper<Sprite>> allSprites = ComponentManager::GetInstance().getAllSpriteReferences(); + for(Sprite& sprite : allSprites) { + //std::cout << sprite.mPath << std::endl; + } + //std::cout << std::endl; + + std::vector<std::reference_wrapper<Rigidbody>> allRigidbody = ComponentManager::GetInstance().getAllRigidbodyReferences(); + for(Rigidbody& rigidbody : allRigidbody) { + //std::cout << rigidbody.mMass << " " << rigidbody.mGravityScale << " " << rigidbody.mBodyType << std::endl; + } + //std::cout << std::endl; + + std::vector<std::reference_wrapper<Colider>> allColider = ComponentManager::GetInstance().getAllColiderReferences(); + for(Colider& colider : allColider) { + //std::cout << colider.mSize << std::endl; + } + + auto stopLooping = std::chrono::high_resolution_clock::now(); + + for (int i = 0; i < 100000; ++i) { + delete gameObject[i]; + } + + auto Addtime = std::chrono::duration_cast<std::chrono::microseconds>(stopAdding - startAdding); + auto LoopTime = std::chrono::duration_cast<std::chrono::microseconds>(stopLooping - stopAdding); + std::cout << "AddTime: " << Addtime.count() << " us" << std::endl; + std::cout << "LoopTime: " << LoopTime.count() << " us" << std::endl; +} |