From 76ffb658db601378c39e3f0639ad69fd9ca34107 Mon Sep 17 00:00:00 2001 From: Jordon Brooks Date: Tue, 6 May 2025 23:21:04 +0100 Subject: [PATCH] Implemented initial concept of Objects and GC --- .vscode/settings.json | 83 ++++++++++- FluxEngine/meson.build | 3 + FluxEngine/src/private/Engine/Engine.cpp | 131 ++++++++++++------ .../src/private/Engine/GarbageCollector.cpp | 80 +++++++++++ .../src/private/Framework/BaseObject.cpp | 59 ++++++++ FluxEngine/src/private/Framework/Object.cpp | 23 +++ FluxEngine/src/private/Log.cpp | 98 ++++++++----- FluxEngine/src/public/Engine/Engine.h | 85 ++++++------ .../src/public/Engine/GarbageCollector.h | 28 ++++ FluxEngine/src/public/Framework/BaseObject.h | 33 +++++ FluxEngine/src/public/Framework/Object.h | 39 ++++++ FluxEngine/src/public/Log.h | 40 +++--- Sandbox/src/private/SandboxApp.cpp | 37 ++++- 13 files changed, 599 insertions(+), 140 deletions(-) create mode 100644 FluxEngine/src/private/Engine/GarbageCollector.cpp create mode 100644 FluxEngine/src/private/Framework/BaseObject.cpp create mode 100644 FluxEngine/src/private/Framework/Object.cpp create mode 100644 FluxEngine/src/public/Engine/GarbageCollector.h create mode 100644 FluxEngine/src/public/Framework/BaseObject.h create mode 100644 FluxEngine/src/public/Framework/Object.h diff --git a/.vscode/settings.json b/.vscode/settings.json index d78211f..193d25d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,5 +2,86 @@ "C_Cpp.default.compileCommands": "d:\\FluxEngine\\builddir/compile_commands.json", "C_Cpp.default.configurationProvider": "mesonbuild.mesonbuild", "cmake.ignoreCMakeListsMissing": true, - "C_Cpp.errorSquiggles": "enabled" + "C_Cpp.errorSquiggles": "enabled", + "files.associations": { + "algorithm": "cpp", + "array": "cpp", + "atomic": "cpp", + "bit": "cpp", + "bitset": "cpp", + "cctype": "cpp", + "charconv": "cpp", + "chrono": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "compare": "cpp", + "complex": "cpp", + "concepts": "cpp", + "condition_variable": "cpp", + "coroutine": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "exception": "cpp", + "expected": "cpp", + "filesystem": "cpp", + "format": "cpp", + "forward_list": "cpp", + "fstream": "cpp", + "functional": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "ios": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "iterator": "cpp", + "limits": "cpp", + "list": "cpp", + "locale": "cpp", + "map": "cpp", + "memory": "cpp", + "mutex": "cpp", + "new": "cpp", + "optional": "cpp", + "ostream": "cpp", + "ratio": "cpp", + "regex": "cpp", + "source_location": "cpp", + "span": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "stop_token": "cpp", + "streambuf": "cpp", + "string": "cpp", + "system_error": "cpp", + "thread": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "typeinfo": "cpp", + "unordered_map": "cpp", + "utility": "cpp", + "variant": "cpp", + "vector": "cpp", + "xfacet": "cpp", + "xhash": "cpp", + "xiosbase": "cpp", + "xlocale": "cpp", + "xlocbuf": "cpp", + "xlocinfo": "cpp", + "xlocmes": "cpp", + "xlocmon": "cpp", + "xlocnum": "cpp", + "xloctime": "cpp", + "xmemory": "cpp", + "xstring": "cpp", + "xtr1common": "cpp", + "xtree": "cpp", + "xutility": "cpp", + "*.rh": "cpp" + } } \ No newline at end of file diff --git a/FluxEngine/meson.build b/FluxEngine/meson.build index 4b45802..e7bb69b 100644 --- a/FluxEngine/meson.build +++ b/FluxEngine/meson.build @@ -3,6 +3,9 @@ flux_inc = include_directories('src/public') flux_sources = files( 'src/private/Engine/Engine.cpp', 'src/private/Log.cpp', + 'src/private/Engine/GarbageCollector.cpp', + 'src/private/Framework/BaseObject.cpp', + 'src/private/Framework/Object.cpp', ) # 1) Grab the active C++ compiler diff --git a/FluxEngine/src/private/Engine/Engine.cpp b/FluxEngine/src/private/Engine/Engine.cpp index a84bcd6..4e4073b 100644 --- a/FluxEngine/src/private/Engine/Engine.cpp +++ b/FluxEngine/src/private/Engine/Engine.cpp @@ -1,58 +1,111 @@ #include "Engine/Engine.h" +#include "Log.h" +#include "Framework/Object.h" +#include "Engine/GarbageCollector.h" -namespace Flux +bool FluxEngine::GIsRunning = false; +FluxEngine* FluxEngine::GInstance = nullptr; + +FluxEngine* FluxEngine::CreateInstance() { - bool FluxEngine::GIsRunning = false; - FluxEngine* FluxEngine::GInstance = nullptr; - - FluxEngine::FluxEngine() : _Logger(*new Logger()) + if (GInstance != nullptr) { - if (GIsRunning) + FluxEngine* Engine = GetInstance(); + + if (Engine) { - GetLogger()->LogTrace("Engine already running!"); - bErrorState = true; - return; + Engine->GetLogger()->LogTrace("Engine already running!"); + return nullptr; } - - if (GInstance != nullptr) - { - FluxEngine* Engine = GetInstance(); - - if (Engine) - { - Engine->GetLogger()->LogTrace("Engine already running!"); - } - return; - } - - GInstance = this; } - FluxEngine::~FluxEngine() + FluxEngine* Engine = new FluxEngine(); + if (Engine == nullptr) { - GetLogger()->LogTrace("Engine shutting down!"); + GetLogger()->LogTrace("Failed to create engine instance!"); + return nullptr; } - void FluxEngine::Start() - { - if (bErrorState) - { - GetLogger()->LogTrace("Engine failed to start!"); - return; - } + Engine->GetLogger()->LogTrace("Engine instance created!"); + Engine->Init(); + Engine->Start(); + return Engine; +} - GetLogger()->LogTrace("Engine started!"); - GIsRunning = true; - EngineLoop(); +GarbageCollector& FluxEngine::GetGarbageCollector() +{ + return _GarbageCollector; +} + +void FluxEngine::Init() +{ + +} + +FluxEngine::FluxEngine() : _Logger(*new Logger()), _GarbageCollector(*new GarbageCollector(this)) +{ + if (GIsRunning) + { + GetLogger()->LogTrace("Engine already running!"); + bErrorState = true; + return; } - void Flux::FluxEngine::EngineLoop() + if (GInstance != nullptr) { - while (IsRunning()) + FluxEngine* Engine = GetInstance(); + + if (Engine) { - GetLogger()->LogTrace("Loop running!"); - // Main engine loop - // Update, render, etc. + Engine->GetLogger()->LogTrace("Engine already running!"); } + return; + } + + GInstance = this; +} + +FluxEngine::~FluxEngine() +{ + GetLogger()->LogTrace("Engine shutting down!"); +} + +int32_t FluxEngine::Start() +{ + if (bErrorState) + { + GetLogger()->LogTrace("Engine failed to start!"); + return -1; + } + + GetLogger()->LogTrace("Engine started!"); + GIsRunning = true; + EngineLoop(); + + return bErrorState ? -1 : 0; +} + +Logger* FluxEngine::GetLogger() +{ + if (GInstance != nullptr) + { + FluxEngine* Engine = GetInstance(); + if (Engine) + { + return &Engine->_Logger; + } + } + + static Logger DefaultLogger; + return &DefaultLogger; +} + +void FluxEngine::EngineLoop() +{ + while (IsRunning()) + { + //GetLogger()->LogTrace("Loop running!"); + // Main engine loop + // Update, render, etc. } } diff --git a/FluxEngine/src/private/Engine/GarbageCollector.cpp b/FluxEngine/src/private/Engine/GarbageCollector.cpp new file mode 100644 index 0000000..fd01694 --- /dev/null +++ b/FluxEngine/src/private/Engine/GarbageCollector.cpp @@ -0,0 +1,80 @@ +#include "Engine/GarbageCollector.h" +#include "Engine/Engine.h" +#include +#include "Log.h" +#include "Framework/Object.h" + +void GarbageCollector::Init() +{ + BaseObject::Init(); + + LOG_INFO("Garbage Collector Initialized"); +} + +void GarbageCollector::CollectGarbage() +{ + // Implement garbage collection logic here + // Create a iterator to traverse the list of objects and remove unreachable / nullptr objects + + for (auto it = _RootObjects.begin(); it != _RootObjects.end();) + { + BaseObject* object = it->get(); + if (!object || object->IsValid()) + { + LOG_WARN("Removing unreachable object from Garbage Collector: " + object->GetName()); + it = _RootObjects.erase(it); + } + else { ++it; } + } +} + +GarbageCollector::GarbageCollector(FluxEngine* engine) : BaseObject(engine) +{ + Init(); +} + +GarbageCollector::~GarbageCollector() +{ + _RootObjects.clear(); +} + +void GarbageCollector::AddObject(std::shared_ptr ObjectToAdd) +{ + if (ObjectToAdd && ObjectToAdd->IsValid()) + { + _RootObjects.push_back(ObjectToAdd->shared_from_this()); + LOG_INFO("Added object to Garbage Collector: " + ObjectToAdd->GetName()); + } + else + { + LOG_ERROR("Failed to add object to Garbage Collector: " + (ObjectToAdd ? ObjectToAdd->GetName() : "nullptr")); + } +} + +void GarbageCollector::RemoveObject(std::shared_ptr ObjectToRemove) +{ + if (!ObjectToRemove) + { + LOG_ERROR("Failed to remove object from GC: nullptr"); + return; + } + + auto oldSize = _RootObjects.size(); + auto newEnd = std::remove_if( + _RootObjects.begin(), _RootObjects.end(), + [ObjectToRemove](const std::shared_ptr& obj) { + return obj.get() == ObjectToRemove.get(); + } + ); + + if (newEnd != _RootObjects.end()) + { + _RootObjects.erase(newEnd, _RootObjects.end()); + LOG_INFO("Removed object from GC: {0}", object->GetName()); + } + else + { + LOG_ERROR("RemoveObject(): {0} not found in GC", + object->GetName()); + } +} diff --git a/FluxEngine/src/private/Framework/BaseObject.cpp b/FluxEngine/src/private/Framework/BaseObject.cpp new file mode 100644 index 0000000..a3d65b6 --- /dev/null +++ b/FluxEngine/src/private/Framework/BaseObject.cpp @@ -0,0 +1,59 @@ +#include "Framework/BaseObject.h" +#include "Engine/Engine.h" +#include "Log.h" + +BaseObject::BaseObject(FluxEngine* Engine) +{ + _CachedEngine = Engine; +} + +BaseObject::~BaseObject() +{ + LOG_INFO("Destroying object: {0}", GetName()); +} + +void BaseObject::Init() +{ + if (bIsInitialized || bUnreachable) + { + return; + } + + _Name = std::string(typeid(*this).name()) + "_" + std::to_string(reinterpret_cast(this)); + bIsInitialized = true; +} + +void BaseObject::Destroy() +{ + if (bUnreachable) + { + return; + } + + bUnreachable = true; +} + +bool BaseObject::IsValid() +{ + return bIsInitialized; +} + +bool BaseObject::IsUnreachable(BaseObject* Object) +{ + return Object && Object->IsValid(); +} + +std::string BaseObject::GetName() const +{ + return _Name; +} + +FluxEngine* BaseObject::GetEngine() const +{ + if (_CachedEngine == nullptr) + { + _CachedEngine = FluxEngine::GetInstance(); + } + + return _CachedEngine; +} \ No newline at end of file diff --git a/FluxEngine/src/private/Framework/Object.cpp b/FluxEngine/src/private/Framework/Object.cpp new file mode 100644 index 0000000..bcf34dd --- /dev/null +++ b/FluxEngine/src/private/Framework/Object.cpp @@ -0,0 +1,23 @@ +#include "Framework/Object.h" +#include "Engine/Engine.h" +#include "Log.h" +#include + +Object::~Object() +{ + LOG_INFO("Destroying object: {0}", GetName()); +} + +void Object::Init() +{ + BaseObject::Init(); + + FluxEngine* engine = GetEngine(); + if (!engine) + { + LOG_ERROR("Failed to initialize object: {0}", GetName()); + return; + } + + engine->GetGarbageCollector().AddObject(shared_from_this()); +} \ No newline at end of file diff --git a/FluxEngine/src/private/Log.cpp b/FluxEngine/src/private/Log.cpp index 2121ae1..68c9c4d 100644 --- a/FluxEngine/src/private/Log.cpp +++ b/FluxEngine/src/private/Log.cpp @@ -3,48 +3,82 @@ #include "spdlog/spdlog.h" #include "spdlog/sinks/stdout_color_sinks.h" -namespace Flux +struct LoggerImpl { - struct LoggerImpl - { - LoggerImpl() = default; - ~LoggerImpl() = default; + LoggerImpl() = default; + ~LoggerImpl() = default; - std::shared_ptr _logger; - }; + std::shared_ptr _logger; +}; - Logger::~Logger() = default; +spdlog::logger *Logger::GetLogger() const +{ + return _impl._logger.get(); +} - spdlog::logger *Logger::GetLogger() const - { - return _impl._logger.get(); +Logger::Logger() : _impl(* new LoggerImpl()) +{ + // Initialize the logger + if (GetLogger()) { + return; } - Logger::Logger() : _impl(* new LoggerImpl()) - { - // Initialize the logger - if (GetLogger()) { - return; - } + // Create a new logger instance + spdlog::set_pattern("%^[%T] %n: %v%$"); - // Create a new logger instance - spdlog::set_pattern("%^[%T] %n: %v%$"); - - _impl._logger = spdlog::stdout_color_mt("FLUX"); - _impl._logger->set_level(spdlog::level::trace); - - LogTrace("Logger initialized!"); + std::shared_ptr logger = spdlog::get("FLUX"); + if (logger) { + _impl._logger = logger; + return; } + + _impl._logger = spdlog::stdout_color_mt("FLUX"); + _impl._logger->set_level(spdlog::level::trace); - void Logger::LogTrace(const std::string &message) + LogTrace("Logger initialized!"); +} + +Logger::~Logger() +{ + +} + +void Logger::LogTrace(const std::string& message) +{ + if (auto logger = GetLogger()) { - spdlog::logger* logger = GetLogger(); - - if (!logger) - { - return; - } - logger->trace(message); } } + +void Logger::LogInfo(const std::string& message) +{ + if (auto logger = GetLogger()) + { + logger->info(message); + } +} + +void Logger::LogWarn(const std::string& message) +{ + if (auto logger = GetLogger()) + { + logger->warn(message); + } +} + +void Logger::LogError(const std::string& message) +{ + if (auto logger = GetLogger()) + { + logger->error(message); + } +} + +void Logger::LogFatal(const std::string& message) +{ + if (auto logger = GetLogger()) + { + logger->critical(message); + } +} diff --git a/FluxEngine/src/public/Engine/Engine.h b/FluxEngine/src/public/Engine/Engine.h index b1aa301..c454605 100644 --- a/FluxEngine/src/public/Engine/Engine.h +++ b/FluxEngine/src/public/Engine/Engine.h @@ -1,56 +1,61 @@ #pragma once #include "Core.h" -#include "Log.h" -#include +#include +#include "Framework/Object.h" +#include "Engine/GarbageCollector.h" -namespace Flux +class Logger; + + +class FLUX_API FluxEngine { - class FLUX_API FluxEngine +public: + static FluxEngine* CreateInstance(); + + template + static std::unique_ptr CreateApplication(Args&&... args) { - public: - FluxEngine(); - ~FluxEngine(); + static_assert(std::is_base_of::value, "T must inherit from FluxEngine"); + std::unique_ptr app = std::make_unique(std::forward(args)...); + app->Init(); + return app; + } - void Start(); + FluxEngine(); + ~FluxEngine(); - static FluxEngine* GetInstance() - { - return GInstance; - } + virtual void Init(); - static bool IsRunning() - { - return GIsRunning; - } + virtual int32_t Start(); - static Logger* GetLogger() - { - if (GIsRunning || GInstance != nullptr) - { - FluxEngine* Engine = GetInstance(); - if (Engine) - { - return &Engine->_Logger; - } - } + static FluxEngine* GetInstance() + { + return GInstance; + } - return nullptr; - } + static bool IsRunning() + { + return GIsRunning; + } - void RequestExit() - { - GIsRunning = false; - } + static Logger* GetLogger(); - void EngineLoop(); + void RequestExit() + { + GIsRunning = false; + } - private: - Flux::Logger& _Logger; + GarbageCollector& GetGarbageCollector(); - static bool GIsRunning; - static FluxEngine* GInstance; + void EngineLoop(); - bool bErrorState = false; - }; -} \ No newline at end of file +private: + Logger& _Logger; + GarbageCollector& _GarbageCollector; + + static bool GIsRunning; + static FluxEngine* GInstance; + + bool bErrorState = false; +}; \ No newline at end of file diff --git a/FluxEngine/src/public/Engine/GarbageCollector.h b/FluxEngine/src/public/Engine/GarbageCollector.h new file mode 100644 index 0000000..2323767 --- /dev/null +++ b/FluxEngine/src/public/Engine/GarbageCollector.h @@ -0,0 +1,28 @@ +#pragma once + +#include "Core.h" +#include +#include + +#include "Framework/BaseObject.h" + +class FluxEngine; +class Object; + +class GarbageCollector : public BaseObject +{ +public: + FLUX_API explicit GarbageCollector(FluxEngine* engine); + FLUX_API ~GarbageCollector(); + + FLUX_API void AddObject(std::shared_ptr ObjectToAdd); + FLUX_API void RemoveObject(std::shared_ptr ObjectToRemove); + + FLUX_API void CollectGarbage(); + +protected: + virtual void Init() override; + +private: + std::vector> _RootObjects; +}; \ No newline at end of file diff --git a/FluxEngine/src/public/Framework/BaseObject.h b/FluxEngine/src/public/Framework/BaseObject.h new file mode 100644 index 0000000..2f6d854 --- /dev/null +++ b/FluxEngine/src/public/Framework/BaseObject.h @@ -0,0 +1,33 @@ +#pragma once + +#include "Core.h" +#include +#include + +class FluxEngine; + +class FLUX_API BaseObject +{ +public: + BaseObject(FluxEngine* Engine); + BaseObject() = default; + virtual ~BaseObject(); + + virtual void Destroy(); + + virtual void Init(); + + static bool IsUnreachable(BaseObject* Object); + + virtual bool IsValid(); + + std::string GetName() const; + + FluxEngine* GetEngine() const; + +private: + mutable FluxEngine* _CachedEngine = nullptr; + bool bIsInitialized = false; + bool bUnreachable = false; + std::string _Name = ""; +}; \ No newline at end of file diff --git a/FluxEngine/src/public/Framework/Object.h b/FluxEngine/src/public/Framework/Object.h new file mode 100644 index 0000000..dbbca6c --- /dev/null +++ b/FluxEngine/src/public/Framework/Object.h @@ -0,0 +1,39 @@ +#pragma once + +#include "Core.h" +#include + +#include "Framework/BaseObject.h" + +class FluxEngine; + +class FLUX_API Object : public BaseObject, public std::enable_shared_from_this +{ +public: + template + static std::shared_ptr NewObject(Args&&... args) + { + static_assert(std::is_base_of::value, "T must inherit from Object"); + + std::shared_ptr ptr = std::make_shared(std::forward(args)...); + ptr->Init(); + return ptr; + } + + Object() = default; + virtual ~Object(); + + // Copy constructor + Object(const Object& other) = default; + + // Move constructor + Object(Object&& other) noexcept = default; + + // Copy assignment operator + Object& operator=(const Object& other) = default; + + // Move assignment operator + Object& operator=(Object&& other) noexcept = default; + + virtual void Init() override; +}; \ No newline at end of file diff --git a/FluxEngine/src/public/Log.h b/FluxEngine/src/public/Log.h index e7c690b..538f66f 100644 --- a/FluxEngine/src/public/Log.h +++ b/FluxEngine/src/public/Log.h @@ -10,34 +10,28 @@ namespace spdlog class logger; } -namespace Flux -{ - struct LoggerImpl; +struct LoggerImpl; - class FLUX_API Logger - { - public: +class FLUX_API Logger +{ +public: Logger(); ~Logger(); void LogTrace(const std::string& message); - private: - spdlog::logger* GetLogger() const; + void LogInfo(const std::string& message); + void LogWarn(const std::string& message); + void LogError(const std::string& message); + void LogFatal(const std::string& message); - Flux::LoggerImpl& _impl; - }; -} +private: + spdlog::logger* GetLogger() const; -// Core log macros -/*#define FLUX_CORE_TRACE(...) ::Flux::Log::GetCoreLogger()->trace(__VA_ARGS__) -#define FLUX_CORE_INFO(...) ::Flux::Log::GetCoreLogger()->info(__VA_ARGS__) -#define FLUX_CORE_WARN(...) ::Flux::Log::GetCoreLogger()->warn(__VA_ARGS__) -#define FLUX_CORE_ERROR(...) ::Flux::Log::GetCoreLogger()->error(__VA_ARGS__) -#define FLUX_CORE_FATAL(...) ::Flux::Log::GetCoreLogger()->fatal(__VA_ARGS__) + LoggerImpl& _impl; +}; -// Client log macros -#define FLUX_TRACE(...) ::Flux::Log::GetCoreLogger()->trace(__VA_ARGS__) -#define FLUX_INFO(...) ::Flux::Log::GetCoreLogger()->info(__VA_ARGS__) -#define FLUX_WARN(...) ::Flux::Log::GetCoreLogger()->warn(__VA_ARGS__) -#define FLUX_ERROR(...) ::Flux::Log::GetCoreLogger()->error(__VA_ARGS__) -#define FLUX_FATAL(...) ::Flux::Log::GetCoreLogger()->fatal(__VA_ARGS__)*/ +#define LOG_TRACE(message) ::FluxEngine::GetLogger()->LogTrace(message) +#define LOG_INFO(message) ::FluxEngine::GetLogger()->LogInfo(message) +#define LOG_WARN(message) ::FluxEngine::GetLogger()->LogWarn(message) +#define LOG_ERROR(message) ::FluxEngine::GetLogger()->LogError(message) +#define LOG_FATAL(message) ::FluxEngine::GetLogger()->LogFatal(message) diff --git a/Sandbox/src/private/SandboxApp.cpp b/Sandbox/src/private/SandboxApp.cpp index ced09ed..c16b031 100644 --- a/Sandbox/src/private/SandboxApp.cpp +++ b/Sandbox/src/private/SandboxApp.cpp @@ -1,9 +1,36 @@ #include "Flux.h" +#include "Framework/Object.h" -int main (int argc, char** argv) +class SandboxObject : public Object { - Flux::FluxEngine engine; - engine.Start(); - - return 0; +public: + SandboxObject() = default; + virtual ~SandboxObject() = default; + + virtual void Init() override + { + Object::Init(); + LOG_INFO("SandboxObject Initialized: {0}", GetName()); + } +}; + +class Sandbox : public FluxEngine +{ +public: + Sandbox() = default; + virtual ~Sandbox() = default; + + virtual void Init() override + { + FluxEngine::Init(); + + std::shared_ptr Obj = Object::NewObject(); + } +}; + +int main(int argc, char** argv) +{ + std::unique_ptr app = FluxEngine::CreateApplication(); + app->Init(); + return app->Start(); } \ No newline at end of file