diff --git a/Coral.vcxproj b/Coral.vcxproj index cf959681..0b8b35ea 100644 --- a/Coral.vcxproj +++ b/Coral.vcxproj @@ -301,6 +301,7 @@ + @@ -324,7 +325,6 @@ - @@ -862,13 +862,13 @@ + - diff --git a/Include/Assets/Level.h b/Include/Assets/Level.h index 3c166797..3cd797de 100644 --- a/Include/Assets/Level.h +++ b/Include/Assets/Level.h @@ -33,9 +33,13 @@ namespace CE Level& operator=(const Level&) = delete; void CreateFromWorld(const World& world); - World CreateWorld(bool callBeginPlayImmediately) const; + void LoadIntoWorld(World& world) const; - static World CreateDefaultWorld(); + // Will never return nullptr + std::unique_ptr CreateWorld(bool callBeginPlayImmediately) const; + + // Will never return nullptr + static std::unique_ptr CreateDefaultWorld(); protected: void OnSave(AssetSaveInfo& saveInfo) const override; @@ -71,7 +75,7 @@ namespace CE // when loading the level. Instead of // discarding the world, we return this // one on the first call to CreateWorld - mutable std::optional mWorld{}; + mutable std::unique_ptr mWorld{}; mutable std::optional mSerializedWorld{}; friend ReflectAccess; diff --git a/Include/Assets/Script.h b/Include/Assets/Script.h index 65e625f8..9f8ccdf5 100644 --- a/Include/Assets/Script.h +++ b/Include/Assets/Script.h @@ -96,9 +96,10 @@ namespace CE // so if this ever changes, you'll know exactly where you need to start fixing things. static constexpr bool sIsTypeIdTheSameAsNameHash = true; - // Each component script secretly gets assigned a field of type entt::entity with this name. - // Upon constructing the component, the correct entity id is assigned. + // Each component script secretly gets assigned a field of type entt::entity and World* with these names. + // Upon constructing the component, the correct entity id and world is assigned. static constexpr Name sNameOfOwnerField = "Owner"_Name; + static constexpr Name sNameOfWorldField = "World"_Name; void CollectErrors(ScriptErrorInserter inserter) const; diff --git a/Include/Components/GridSpawnerComponent.h b/Include/Components/GridSpawnerComponent.h index 6fbbea3d..98808827 100644 --- a/Include/Components/GridSpawnerComponent.h +++ b/Include/Components/GridSpawnerComponent.h @@ -10,10 +10,10 @@ namespace CE { public: void OnConstruct(World&, entt::entity owner); - void OnBeginPlay(World&, entt::entity); + void OnBeginPlay(World& world, entt::entity); - void ClearGrid(); - void SpawnGrid(); + void ClearGrid(World& world); + void SpawnGrid(World& world); std::vector> mTiles{}; diff --git a/Include/Core/ThreadPool.h b/Include/Core/ThreadPool.h new file mode 100644 index 00000000..fcf5cea6 --- /dev/null +++ b/Include/Core/ThreadPool.h @@ -0,0 +1,54 @@ +#pragma once +#include +#include + +namespace CE +{ + class ThreadPool final : + public EngineSubsystem + { + friend EngineSubsystem; + + ThreadPool(); + ~ThreadPool(); // joins all threads + public: + + template + decltype(auto) Enqueue(F&& callable, A &&...arguments); + + size_t NumberOfThreads() const { return mThreads.size(); } + + private: + std::vector mThreads{}; + std::queue> mTasks{}; + std::mutex mMutex{}; + std::condition_variable mCondition{}; + bool mStopped{}; + }; + + // add new work item to the pool + template + decltype(auto) ThreadPool::Enqueue(F&& f, Args&&... args) + { + using return_type = std::invoke_result_t; + + auto task = std::make_shared>( + std::bind(std::forward(f), std::forward(args)...) + ); + + std::future res = task->get_future(); + { + std::unique_lock lock{ mMutex }; + mTasks.emplace([task]{ (*task)(); }); + } + mCondition.notify_one(); + return res; + } + + + template + bool IsFutureReady(const std::future& f) + { + return f.valid() && f.wait_for(std::chrono::seconds{}) == std::future_status::ready; + } +} \ No newline at end of file diff --git a/Include/EditorSystems/AssetEditorSystems/ScriptEditorSystem.h b/Include/EditorSystems/AssetEditorSystems/ScriptEditorSystem.h index 8445cbcd..6ce5c2d2 100644 --- a/Include/EditorSystems/AssetEditorSystems/ScriptEditorSystem.h +++ b/Include/EditorSystems/AssetEditorSystems/ScriptEditorSystem.h @@ -1,9 +1,8 @@ -#include "Utilities/ASync.h" #ifdef EDITOR #pragma once #include "EditorSystems/AssetEditorSystems/AssetEditorSystem.h" -#include +#include #include "imnodes/imgui_node_editor.h" #include "Assets/Script.h" @@ -202,8 +201,10 @@ namespace CE void InitialiseAllNodesTheUserCanAdd(); std::vector mAllNodesTheUserCanAdd{}; - ASyncThread mNodePopularityCalculateThread{}; - bool mShouldWeStopCountingNodePopularity{}; + + // The popularity is stored inside of each node in mAllNodesTheUserCanAdd + std::future mNodePopularityCalculations{}; + std::atomic mShouldWeStopCountingNodePopularity{}; ax::NodeEditor::PinId mPinTheUserRightClicked{}; ax::NodeEditor::PinId mPinTheUserIsTryingToLink{}; diff --git a/Include/EditorSystems/ContentBrowserEditorSystem.h b/Include/EditorSystems/ContentBrowserEditorSystem.h index 61ccde63..c5ced8b9 100644 --- a/Include/EditorSystems/ContentBrowserEditorSystem.h +++ b/Include/EditorSystems/ContentBrowserEditorSystem.h @@ -1,8 +1,9 @@ #ifdef EDITOR #pragma once +#include + #include "EditorSystems/EditorSystem.h" #include "Core/AssetManager.h" -#include "Utilities/ASync.h" namespace CE { @@ -25,7 +26,6 @@ namespace CE private: struct ContentFolder { - ContentFolder() = default; ContentFolder(const std::filesystem::path& path, std::string&& name, ContentFolder* parent) : mActualPath(path), mFolderName(std::move(name)), @@ -33,10 +33,10 @@ namespace CE {} ContentFolder(const ContentFolder&) = delete; - ContentFolder(ContentFolder&&) noexcept = default; + ContentFolder(ContentFolder&&) noexcept = delete; ContentFolder& operator=(const ContentFolder&) = delete; - ContentFolder& operator=(ContentFolder&&) noexcept = default; + ContentFolder& operator=(ContentFolder&&) noexcept = delete; // Will be empty for the root folder std::filesystem::path mActualPath{}; @@ -46,6 +46,7 @@ namespace CE ContentFolder* mParent{}; std::vector> mContent{}; }; + void RequestUpdateToFolderGraph(); void DisplayFolderHierarchyPanel(); @@ -87,11 +88,11 @@ namespace CE static MetaType Reflect(); REFLECT_AT_START_UP(ContentBrowserEditorSystem); - ContentFolder mRootFolder{ {}, "All", nullptr }; - std::reference_wrapper mSelectedFolder = mRootFolder; + std::unique_ptr mRootFolder = std::make_unique(std::filesystem::path{}, "All", nullptr); + std::reference_wrapper mSelectedFolder = *mRootFolder; std::filesystem::path mSelectedFolderPath{}; - ASyncFuture mPendingRootFolder{}; + std::future> mPendingRootFolder{}; float mFolderHierarchyPanelWidthPercentage = .25f; float mContentPanelWidthPercentage = .75f; diff --git a/Include/EditorSystems/ImporterSystem.h b/Include/EditorSystems/ImporterSystem.h index 34c14396..daad5343 100644 --- a/Include/EditorSystems/ImporterSystem.h +++ b/Include/EditorSystems/ImporterSystem.h @@ -1,14 +1,11 @@ -#include "Utilities/ASync.h" -#include "Utilities/Time.h" #ifdef EDITOR #include "EditorSystems/EditorSystem.h" #include #include "Core/AssetManager.h" -#include "Assets/Core/AssetLoadInfo.h" #include "Assets/Importers/Importer.h" -#include "Utilities/MemFunctions.h" +#include "Utilities/Time.h" namespace CE { @@ -51,7 +48,7 @@ namespace CE struct ImportFuture { ImportRequest mImportRequest{}; - ASyncFuture>> mImportResult{}; + std::future>> mImportResult{}; }; struct DirToWatch @@ -113,7 +110,7 @@ namespace CE std::array mDirectoriesToWatch{}; Cooldown mCheckDirectoryCooldown{ 10.0f }; - ASyncFuture> mChangedFilesInDirectoriesToWatch{}; + std::future> mChangedFilesInDirectoriesToWatch{}; static inline bool sExcludeDuplicates{}; static inline bool sIgnoreReadOnly = true; diff --git a/Include/EditorSystems/ThumbnailEditorSystem.h b/Include/EditorSystems/ThumbnailEditorSystem.h index 1ad0aa72..c19ac8fe 100644 --- a/Include/EditorSystems/ThumbnailEditorSystem.h +++ b/Include/EditorSystems/ThumbnailEditorSystem.h @@ -12,7 +12,7 @@ namespace CE class Texture; class World; - using GetThumbnailRet = std::variant, World>; + using GetThumbnailRet = std::variant, std::unique_ptr>; class ThumbnailEditorSystem final : public EditorSystem @@ -72,9 +72,7 @@ namespace CE struct CurrentlyGenerating { - CurrentlyGenerating(World&& world, WeakAssetHandle<> forAsset); - - World mWorld; + std::unique_ptr mWorld{}; WeakAssetHandle<> mForAsset{}; FrameBuffer mFrameBuffer{ sGeneratedThumbnailResolution }; Timer mNumSecondsSinceLastRequested{}; diff --git a/Include/Scripting/ScriptPin.h b/Include/Scripting/ScriptPin.h index 64c8dbad..e219d88c 100644 --- a/Include/Scripting/ScriptPin.h +++ b/Include/Scripting/ScriptPin.h @@ -1,17 +1,10 @@ #pragma once #include "ScriptIds.h" -#include "imnodes/imgui_node_editor.h" -#include "Meta/MetaAny.h" -#include "Meta/MetaTypeTraits.h" #include "Meta/Fwd/MetaFuncFwd.h" +#include "Meta/MetaTypeTraits.h" #include "Scripting/ScriptErrors.h" -namespace CE -{ - struct MetaFuncNamedParam; -} - namespace CE { enum class ScriptPinKind : bool diff --git a/Include/Utilities/ASync.h b/Include/Utilities/ASync.h deleted file mode 100644 index 0f242efb..00000000 --- a/Include/Utilities/ASync.h +++ /dev/null @@ -1,81 +0,0 @@ -#pragma once - -namespace CE -{ - namespace Internal - { - struct Job; - } - - // Like a regular thread, except this one - // does not slow down the main thread. - class ASyncThread - { - public: - ASyncThread() = default; - ASyncThread(std::function&& work); - - ASyncThread(const ASyncThread&) = delete; - ASyncThread(ASyncThread&&) noexcept = default; - - ASyncThread& operator=(const ASyncThread&) = delete; - ASyncThread& operator=(ASyncThread&&) noexcept = default; - - ~ASyncThread(); - - bool WasLaunched() const; - - void CancelOrJoin(); - void CancelOrDetach(); - void Join(); - void Detach(); - - private: - std::shared_ptr mJob{}; - }; - - template - class ASyncFuture - { - public: - ASyncFuture() = default; - ASyncFuture(std::function&& work) : - mValue(std::make_shared>()), - mThread( - [value = mValue, work] - { - const_cast>&>(value)->emplace(work()); - } - ) - { - } - - ASyncFuture(const ASyncFuture&) = delete; - ASyncFuture(ASyncFuture&&) noexcept = default; - - ASyncFuture& operator=(const ASyncFuture&) = delete; - ASyncFuture& operator=(ASyncFuture&&) noexcept = default; - - ASyncThread& GetThread() { return mThread; } - const ASyncThread& GetThread() const { return mThread; } - - bool IsReady() const { return mValue != nullptr && mValue->has_value(); } - - T& Get(); - - private: - std::shared_ptr> mValue{}; - ASyncThread mThread{}; - }; - - template - T& ASyncFuture::Get() - { - if (mThread.WasLaunched()) - { - mThread.Join(); - } - - return **mValue; - } -} diff --git a/Include/Utilities/Imgui/WorldInspect.h b/Include/Utilities/Imgui/WorldInspect.h index 1424c154..fb71a869 100644 --- a/Include/Utilities/Imgui/WorldInspect.h +++ b/Include/Utilities/Imgui/WorldInspect.h @@ -21,7 +21,7 @@ namespace CE The provided doUndoStack must remain alive for the duration of this objects lifetime. */ - WorldInspectHelper(World&& worldThatHasNotYetBegunPlay); + WorldInspectHelper(std::unique_ptr worldThatHasNotYetBegunPlay); ~WorldInspectHelper(); /* diff --git a/Include/Utilities/Reflect/ReflectComponentType.h b/Include/Utilities/Reflect/ReflectComponentType.h index f68982ed..0e46d33c 100644 --- a/Include/Utilities/Reflect/ReflectComponentType.h +++ b/Include/Utilities/Reflect/ReflectComponentType.h @@ -36,12 +36,10 @@ namespace CE MetaFunc& addComponentFunc = entityType.AddFunc( [](MetaFunc::DynamicArgs args, MetaFunc::RVOBuffer) -> FuncResult { - World* world = World::TryGetWorldAtTopOfStack(); - ASSERT(world != nullptr && "Reached a scripting context without pushing a world"); + World& world = *static_cast(args[0].GetData()); + const entt::entity entity = *static_cast(args[1].GetData()); - entt::entity entity = *static_cast(args[0].GetData()); - - Registry& reg = world->GetRegistry(); + Registry& reg = world.GetRegistry(); if (reg.HasComponent(entity)) { @@ -61,7 +59,7 @@ namespace CE }, Internal::GetAddComponentFuncName(type.GetName()), MetaFunc::Return{ isEmpty ? MakeTypeTraits() : MakeTypeTraits() }, - MetaFunc::Params{ { MakeTypeTraits(), "Entity" } }); + MetaFunc::Params{ { MakeTypeTraits() }, { MakeTypeTraits() } }); if (type.GetProperties().Has(Props::sIsScriptableTag)) { diff --git a/Include/World/EventManager.h b/Include/World/EventManager.h index bafd9d6e..45145778 100644 --- a/Include/World/EventManager.h +++ b/Include/World/EventManager.h @@ -29,8 +29,6 @@ namespace CE MetaFunc::DynamicArgs argsProvided, std::span argFormsProvided); - // mWorld needs to be updated in World::World(World&&), so we give access to World to do so. - friend class World; std::reference_wrapper mWorld; std::unordered_map> mBoundEvents{}; diff --git a/Include/World/Physics.h b/Include/World/Physics.h index a10caf42..755655f2 100644 --- a/Include/World/Physics.h +++ b/Include/World/Physics.h @@ -52,8 +52,6 @@ namespace CE static MetaType Reflect(); REFLECT_AT_START_UP(Physics); - // mWorld needs to be updated in World::World(World&&), so we give access to World to do so. - friend class World; std::reference_wrapper mWorld; BVHS mBVHs; diff --git a/Include/World/Registry.h b/Include/World/Registry.h index ed485d40..5d554475 100644 --- a/Include/World/Registry.h +++ b/Include/World/Registry.h @@ -157,9 +157,6 @@ namespace CE void CallEndPlayEventsForEntity(entt::sparse_set& storage, entt::entity entity, const BoundEvent& endPlayEvent); - // mWorld needs to be updated in World::World(World&&), so we give access to World to do so. - friend World; - std::reference_wrapper mWorld; entt::registry mRegistry{}; @@ -217,8 +214,6 @@ namespace CE } }(); - World::PushWorld(mWorld); - if constexpr (isEmpty) { mRegistry.emplace(toEntity, std::forward(additionalArgs)...); @@ -236,8 +231,6 @@ namespace CE events.mOnBeginPlay->mFunc.get().InvokeUncheckedUnpacked(GetWorld(), toEntity); } } - - World::PopWorld(); } else { @@ -271,8 +264,6 @@ namespace CE } } - World::PopWorld(); - return component; } } @@ -307,7 +298,6 @@ namespace CE template void Registry::RemoveComponent(entt::entity fromEntity) { - World::PushWorld(mWorld); static constexpr TypeId componentClassTypeId = MakeStrippedTypeId(); entt::sparse_set* storage = Storage(componentClassTypeId); ASSERT(storage != nullptr); @@ -335,7 +325,6 @@ namespace CE } storage->erase(fromEntity); - World::PopWorld(); } template @@ -358,8 +347,6 @@ namespace CE return; } - World::PushWorld(mWorld); - if constexpr (sIsReflectable) { static std::optional endPlayEvent = @@ -379,7 +366,6 @@ namespace CE { if (!storage->contains(fromEntity)) { - World::PopWorld(); return; } @@ -388,7 +374,6 @@ namespace CE } storage->remove(fromEntity); - World::PopWorld(); } template diff --git a/Include/World/World.h b/Include/World/World.h index 07a9e04b..9a336463 100644 --- a/Include/World/World.h +++ b/Include/World/World.h @@ -18,14 +18,15 @@ namespace CE { public: World(bool beginPlayImmediately); - World(World&& other) noexcept; - World(const World&) = delete; - ~World(); + World(World&&) = delete; + World(const World&) = delete; - World& operator=(World&& other) noexcept; + World& operator=(World&&) noexcept = delete; World& operator=(const World&) = delete; + ~World(); + void Tick(float deltaTime); void Render(FrameBuffer* renderTarget = nullptr); @@ -75,11 +76,6 @@ namespace CE void RequestEndplay(); - static void PushWorld(World& world); - static void PopWorld(); - - static World* TryGetWorldAtTopOfStack(); - /** * \brief Will request a transition to a different level. * diff --git a/Include/World/WorldViewport.h b/Include/World/WorldViewport.h index 501921d6..b77661bc 100644 --- a/Include/World/WorldViewport.h +++ b/Include/World/WorldViewport.h @@ -23,8 +23,6 @@ namespace CE glm::vec3 ScreenToWorldPlane(glm::vec2 screenPosition, float planeHeight) const; private: - // mWorld needs to be updated in World::World(World&&), so we give access to World to do so. - friend class World; std::reference_wrapper mWorld; // In pixels diff --git a/Source/Assets/Level.cpp b/Source/Assets/Level.cpp index bf225626..ca7d9c18 100644 --- a/Source/Assets/Level.cpp +++ b/Source/Assets/Level.cpp @@ -44,7 +44,7 @@ CE::Level::Level(std::string_view name) : CE::Level::Level(AssetLoadInfo& loadInfo) : Asset(loadInfo), - mWorld(false) + mWorld(std::make_unique(false)) { BinaryGSONObject savedData{}; savedData.LoadFromBinary(loadInfo.GetStream()); @@ -178,7 +178,7 @@ void CE::Level::OnSave(AssetSaveInfo& saveInfo) const { if (!mSerializedWorld.has_value()) { - if (!mWorld.has_value()) + if (mWorld == nullptr) { LOG(LogAssets, Error, "Cannot save level {}, mSerializedComponents and mWorld are null", GetName()); @@ -213,49 +213,49 @@ void CE::Level::CreateFromWorld(const World& world) mSerializedWorld.emplace(Archiver::Serialize(world)); } -CE::World CE::Level::CreateWorld(const bool callBeginPlayImmediately) const +void CE::Level::LoadIntoWorld(World& world) const { - if (mWorld.has_value()) + if (!mSerializedWorld.has_value()) { - World world = std::move(*mWorld); - mWorld.reset(); + LOG(LogAssets, Warning, "Failed to load {} into world, mSerializedWorld was null", GetName()); + return; + } + Archiver::Deserialize(world, *mSerializedWorld); +} + +std::unique_ptr CE::Level::CreateWorld(const bool callBeginPlayImmediately) const +{ + if (mWorld != nullptr) + { if (!mSerializedWorld.has_value()) { - mSerializedWorld.emplace(Archiver::Serialize(world)); + mSerializedWorld.emplace(Archiver::Serialize(*mWorld)); } if (callBeginPlayImmediately) { - world.BeginPlay(); + mWorld->BeginPlay(); } - return world; + return std::move(mWorld); } - World world{ false }; - - if (mSerializedWorld.has_value()) - { - Archiver::Deserialize(world, *mSerializedWorld); - } - else - { - LOG(LogAssets, Warning, "mWorld and mSerializedWorld were both null for {}", GetName()); - } + std::unique_ptr world = std::make_unique(false); + LoadIntoWorld(*world); if (callBeginPlayImmediately) { - world.BeginPlay(); + world->BeginPlay(); } return world; } -CE::World CE::Level::CreateDefaultWorld() +std::unique_ptr CE::Level::CreateDefaultWorld() { - World world{ false }; - Registry& reg = world.GetRegistry(); + std::unique_ptr world = std::make_unique(false); + Registry& reg = world->GetRegistry(); { const entt::entity camera = reg.Create(); diff --git a/Source/Assets/Script.cpp b/Source/Assets/Script.cpp index a72df196..8ff2f261 100644 --- a/Source/Assets/Script.cpp +++ b/Source/Assets/Script.cpp @@ -16,6 +16,16 @@ #include "Utilities/Reflect/ReflectAssetType.h" #include "Utilities/Reflect/ReflectComponentType.h" +// We've hardcoded our scripts to always have a +// While fields normally dont support ptr/ref types, +// we can make a little wrapper around our pointer +struct WorldPointer +{ + CE::World* mData{}; + static CE::MetaType Reflect(); + REFLECT_AT_START_UP(WorldPointer); +}; + CE::Script::Script(std::string_view name) : Asset(name, MakeTypeId