// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved. #pragma once #include "Engine/Threading/MainThreadTask.h" #include "Engine/Core/Log.h" #include "ManagedCLR/MMethod.h" #include "ManagedCLR/MUtils.h" /// /// Helper class for easy invoking managed code on main thread before all game systems update. /// /// class FLAXENGINE_API MainThreadManagedInvokeAction : public MainThreadTask { public: struct ParamsBuilder { public: int32 Count; int32 IsRef[8]; int32 Offsets[8]; Array Data; public: /// /// Initializes a new instance of the struct. /// ParamsBuilder() : Count(0) , Data(4 * sizeof(int64)) // set initial capacity to reduce allocations { } /// /// Initializes a new instance of the struct. /// /// The initial capacity for parameters data storage (in bytes). ParamsBuilder(int32 capacity) : Count(0) , Data(capacity) { } public: template void AddParam(const T& value) { ASSERT(Count < 8); IsRef[Count] = false; Offsets[Count++] = Data.Count(); Data.Add((byte*)&value, sizeof(T)); } template void AddParam(T* value) { ASSERT(Count < 8); IsRef[Count] = true; Offsets[Count++] = Data.Count(); Data.Add((byte*)&value, sizeof(void*)); } FORCE_INLINE void AddParam(const bool& value) { int32 val = value ? 1 : 0; AddParam(val); } #if USE_MONO FORCE_INLINE void AddParam(const String& value) { MonoString* val = MUtils::ToString(value); AddParam(val); } FORCE_INLINE void AddParam(const StringView& value) { MonoString* val = MUtils::ToString(value); AddParam(val); } FORCE_INLINE void AddParam(const String& value, MonoDomain* domain) { MonoString* val = MUtils::ToString(value, domain); AddParam(val); } FORCE_INLINE void AddParam(const StringView& value, MonoDomain* domain) { MonoString* val = MUtils::ToString(value, domain); AddParam(val); } #endif void GetParams(void* params[8]) { for (int32 i = 0; i < Count; i++) { if (IsRef[i]) Platform::MemoryCopy(¶ms[i], &Data[Offsets[i]], sizeof(void*)); else params[i] = &Data[Offsets[i]]; } } }; private: MObject* _instance; MMethod* _method; void* _methodThunk; LogType _exceptionLevel; ParamsBuilder _params; public: MainThreadManagedInvokeAction(MObject* instance, MMethod* method, LogType exceptionLevel) : _instance(instance) , _method(method) , _methodThunk(nullptr) , _exceptionLevel(exceptionLevel) , _params(0) { } MainThreadManagedInvokeAction(MObject* instance, MMethod* method, LogType exceptionLevel, const ParamsBuilder& params) : _instance(instance) , _method(method) , _methodThunk(nullptr) , _exceptionLevel(exceptionLevel) , _params(params) { } MainThreadManagedInvokeAction(void* methodThunk, LogType exceptionLevel, const ParamsBuilder& params) : _instance(nullptr) , _method(nullptr) , _methodThunk(methodThunk) , _exceptionLevel(exceptionLevel) , _params(params) { } public: /// /// Starts the new task or invokes this action now if already running on a main thread. /// /// The managed method. /// The managed instance object. /// The exception logging error level. /// The created task. static MainThreadManagedInvokeAction* Invoke(MMethod* method, MObject* instance = nullptr, LogType exceptionLevel = LogType::Error); /// /// Starts the new task or invokes this action now if already running on a main thread. /// /// The managed method. /// The method parameters. /// The managed instance object. /// The exception logging error level. /// The created task. static MainThreadManagedInvokeAction* Invoke(MMethod* method, ParamsBuilder& params, MObject* instance = nullptr, LogType exceptionLevel = LogType::Error); /// /// Starts the new task or invokes this action now if already running on a main thread. /// /// The managed method thunk (must return void and has up to 4 parameters). /// The method parameters. /// The exception logging error level. /// The created task. static MainThreadManagedInvokeAction* Invoke(void* methodThunk, ParamsBuilder& params, LogType exceptionLevel = LogType::Error); /// /// Invokes action now. /// /// The managed method. /// The method parameters. /// The managed instance object. /// The exception logging error level. static void InvokeNow(MMethod* method, ParamsBuilder& params, MObject* instance = nullptr, LogType exceptionLevel = LogType::Error); protected: // [MainThreadTask] bool Run() override; };