From 92254eefcc265360266d6c34341d1630506b2cef Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 25 Nov 2025 00:20:14 -0800 Subject: [PATCH 1/7] SImplify some code and update code for platforms --- Source/Engine/Engine/NativeInterop.cs | 9 +++-- .../Platform/Android/AndroidFileSystem.cpp | 20 +--------- .../Engine/Platform/Base/FileSystemBase.cpp | 37 ++++++++++++++++++ Source/Engine/Platform/Base/FileSystemBase.h | 2 +- .../Engine/Platform/Unix/UnixFileSystem.cpp | 39 +------------------ Source/Engine/Scripting/Runtime/DotNet.cpp | 6 +-- 6 files changed, 50 insertions(+), 63 deletions(-) diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs index 209eb54e2..564c53801 100644 --- a/Source/Engine/Engine/NativeInterop.cs +++ b/Source/Engine/Engine/NativeInterop.cs @@ -79,10 +79,11 @@ namespace FlaxEngine.Interop NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), NativeLibraryImportResolver); // Change default culture to match with Mono runtime default culture - CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture; - CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.InvariantCulture; - System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; - System.Threading.Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture; + var culture = CultureInfo.InvariantCulture; + CultureInfo.DefaultThreadCurrentCulture = culture; + CultureInfo.DefaultThreadCurrentUICulture = culture; + System.Threading.Thread.CurrentThread.CurrentCulture = culture; + System.Threading.Thread.CurrentThread.CurrentUICulture = culture; } diff --git a/Source/Engine/Platform/Android/AndroidFileSystem.cpp b/Source/Engine/Platform/Android/AndroidFileSystem.cpp index 930b4017b..27fefb94e 100644 --- a/Source/Engine/Platform/Android/AndroidFileSystem.cpp +++ b/Source/Engine/Platform/Android/AndroidFileSystem.cpp @@ -422,24 +422,8 @@ bool AndroidFileSystem::getFilesFromDirectoryTop(Array& results, const c if (S_ISREG(statEntry.st_mode) != 0) { // Validate with filter - const int32 fullPathLength = StringUtils::Length(fullPath); - const int32 searchPatternLength = StringUtils::Length(searchPattern); - if (searchPatternLength == 0 || StringUtils::Compare(searchPattern, "*") == 0) - { - // All files - } - else if (searchPattern[0] == '*' && searchPatternLength < fullPathLength && StringUtils::Compare(fullPath + fullPathLength - searchPatternLength + 1, searchPattern + 1) == 0) - { - // Path ending - } - else - { - // TODO: implement all cases in a generic way - continue; - } - - // Add file - results.Add(String(fullPath)); + if (FileSystem::PathFilterHelper(fullPath, searchPattern)) + results.Add(String(fullPath)); } } diff --git a/Source/Engine/Platform/Base/FileSystemBase.cpp b/Source/Engine/Platform/Base/FileSystemBase.cpp index 1ac5aa6ea..36b4182de 100644 --- a/Source/Engine/Platform/Base/FileSystemBase.cpp +++ b/Source/Engine/Platform/Base/FileSystemBase.cpp @@ -7,6 +7,7 @@ #include "Engine/Core/Types/StringView.h" #include "Engine/Core/Collections/Array.h" #include "Engine/Core/Math/Math.h" +#include "Engine/Core/Log.h" #include "Engine/Engine/Globals.h" bool FileSystemBase::ShowOpenFileDialog(Window* parentWindow, const StringView& initialDirectory, const StringView& filter, bool multiSelect, const StringView& title, Array& filenames) @@ -313,3 +314,39 @@ bool FileSystemBase::DirectoryCopyHelper(const String& dst, const String& src, b return false; } + +bool FileSystemBase::PathFilterHelper(const char* path, const char* searchPattern) +{ + // Validate with filter + const int32 pathLength = StringUtils::Length(path); + const int32 searchPatternLength = StringUtils::Length(searchPattern); + if (searchPatternLength == 0 || + StringUtils::Compare(searchPattern, "*") == 0 || + StringUtils::Compare(searchPattern, "*.*") == 0) + { + // All files + return true; + } + else if (searchPattern[0] == '*' && searchPatternLength < pathLength && StringUtils::Compare(path + pathLength - searchPatternLength + 1, searchPattern + 1, searchPatternLength - 1) == 0) + { + // Path ending + return true; + } + else if (searchPattern[0] == '*' && searchPatternLength > 2 && searchPattern[searchPatternLength - 1] == '*') + { + // Contains pattern + bool match = false; + for (int32 i = 0; i < pathLength - searchPatternLength - 1; i++) + { + int32 len = Math::Min(searchPatternLength - 2, pathLength - i); + if (StringUtils::Compare(&path[i], &searchPattern[1], len) == 0) + return true; + } + } + else + { + // TODO: implement all cases in a generic way + LOG(Warning, "DirectoryGetFiles: Wildcard filter is not implemented"); + } + return false; +} diff --git a/Source/Engine/Platform/Base/FileSystemBase.h b/Source/Engine/Platform/Base/FileSystemBase.h index 844d5fa5f..1a4980d95 100644 --- a/Source/Engine/Platform/Base/FileSystemBase.h +++ b/Source/Engine/Platform/Base/FileSystemBase.h @@ -284,6 +284,6 @@ public: /// Relative path static String ConvertAbsolutePathToRelative(const String& basePath, const String& path); -private: static bool DirectoryCopyHelper(const String& dst, const String& src, bool withSubDirectories); + static bool PathFilterHelper(const char* path, const char* searchPattern); }; diff --git a/Source/Engine/Platform/Unix/UnixFileSystem.cpp b/Source/Engine/Platform/Unix/UnixFileSystem.cpp index 528df5b88..d85f60563 100644 --- a/Source/Engine/Platform/Unix/UnixFileSystem.cpp +++ b/Source/Engine/Platform/Unix/UnixFileSystem.cpp @@ -367,43 +367,8 @@ bool UnixFileSystem::getFilesFromDirectoryTop(Array& results, const char if (S_ISREG(statEntry.st_mode) != 0) { // Validate with filter - const int32 fullPathLength = StringUtils::Length(fullPath); - const int32 searchPatternLength = StringUtils::Length(searchPattern); - if (searchPatternLength == 0 || - StringUtils::Compare(searchPattern, "*") == 0 || - StringUtils::Compare(searchPattern, "*.*") == 0) - { - // All files - } - else if (searchPattern[0] == '*' && searchPatternLength < fullPathLength && StringUtils::Compare(fullPath + fullPathLength - searchPatternLength + 1, searchPattern + 1, searchPatternLength - 1) == 0) - { - // Path ending - } - else if (searchPattern[0] == '*' && searchPatternLength > 2 && searchPattern[searchPatternLength-1] == '*') - { - // Contains pattern - bool match = false; - for (int32 i = 0; i < pathLength - searchPatternLength - 1; i++) - { - int32 len = Math::Min(searchPatternLength - 2, pathLength - i); - if (StringUtils::Compare(&entry->d_name[i], &searchPattern[1], len) == 0) - { - match = true; - break; - } - } - if (!match) - continue; - } - else - { - // TODO: implement all cases in a generic way - LOG(Warning, "DirectoryGetFiles: Wildcard filter is not implemented"); - continue; - } - - // Add file - results.Add(String(fullPath)); + if (FileSystem::PathFilterHelper(fullPath, searchPattern)) + results.Add(String(fullPath)); } } diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index d8e5acaff..2e98094b2 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -2155,9 +2155,9 @@ bool InitHostfxr() #endif // Platform-specific setup -#if PLATFORM_IOS || PLATFORM_SWITCH - setenv("MONO_AOT_MODE", "aot", 1); - setenv("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT", "1", 1); +#if PLATFORM_IOS || PLATFORM_SWITCH || PLATFORM_PS4 || PLATFORM_PS5 + Platform::SetEnvironmentVariable(TEXT("MONO_AOT_MODE"), TEXT("aot")); + Platform::SetEnvironmentVariable(TEXT("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT"), TEXT("1")); #endif #ifdef USE_MONO_AOT_MODULE From bea75f51bd85b54cfc3cd779bc1cbe52c03a2de5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 26 Nov 2025 00:02:40 -0800 Subject: [PATCH 2/7] Fix AOT libs cooking to avoid file dirtying for more accurate iterative cooking --- .../Cooker/Steps/CompileScriptsStep.cpp | 5 ++- .../Cooker/Steps/PrecompileAssembliesStep.cpp | 1 + .../Flax.Build/Build/DotNet/DotNetAOT.cs | 2 +- Source/Tools/Flax.Build/Build/EngineModule.cs | 3 +- .../Tools/Flax.Build/Utilities/Utilities.cs | 44 +++++++++++++++++-- 5 files changed, 48 insertions(+), 7 deletions(-) diff --git a/Source/Editor/Cooker/Steps/CompileScriptsStep.cpp b/Source/Editor/Cooker/Steps/CompileScriptsStep.cpp index c0a8e452a..1fdb1e7c0 100644 --- a/Source/Editor/Cooker/Steps/CompileScriptsStep.cpp +++ b/Source/Editor/Cooker/Steps/CompileScriptsStep.cpp @@ -10,9 +10,10 @@ #include "Engine/Serialization/JsonTools.h" #include "Engine/Serialization/JsonWriters.h" #include "Editor/Cooker/PlatformTools.h" +#include "Engine/Engine/Globals.h" #include "Editor/Editor.h" #include "Editor/ProjectInfo.h" -#include "Engine/Engine/Globals.h" +#include "Editor/Utilities/EditorUtilities.h" #if PLATFORM_MAC #include #endif @@ -127,7 +128,7 @@ bool CompileScriptsStep::DeployBinaries(CookingData& data, const String& path, c const String dst = dstPath / StringUtils::GetFileName(file); if (dst == file) continue; - if (FileSystem::CopyFile(dst, file)) + if (EditorUtilities::CopyFileIfNewer(dst, file)) { data.Error(String::Format(TEXT("Failed to copy file from {0} to {1}."), file, dst)); return true; diff --git a/Source/Editor/Cooker/Steps/PrecompileAssembliesStep.cpp b/Source/Editor/Cooker/Steps/PrecompileAssembliesStep.cpp index 472a3cca6..626b532a6 100644 --- a/Source/Editor/Cooker/Steps/PrecompileAssembliesStep.cpp +++ b/Source/Editor/Cooker/Steps/PrecompileAssembliesStep.cpp @@ -59,6 +59,7 @@ bool PrecompileAssembliesStep::Perform(CookingData& data) data.StepProgress(infoMsg, 0); // Override Newtonsoft.Json with AOT-version (one that doesn't use System.Reflection.Emit) + // TODO: remove it since EngineModule does properly reference AOT lib now EditorUtilities::CopyFileIfNewer(data.ManagedCodeOutputPath / TEXT("Newtonsoft.Json.dll"), Globals::StartupFolder / TEXT("Source/Platforms/DotNet/AOT/Newtonsoft.Json.dll")); FileSystem::DeleteFile(data.ManagedCodeOutputPath / TEXT("Newtonsoft.Json.xml")); FileSystem::DeleteFile(data.ManagedCodeOutputPath / TEXT("Newtonsoft.Json.pdb")); diff --git a/Source/Tools/Flax.Build/Build/DotNet/DotNetAOT.cs b/Source/Tools/Flax.Build/Build/DotNet/DotNetAOT.cs index e421b6999..96551a91d 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/DotNetAOT.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/DotNetAOT.cs @@ -462,7 +462,7 @@ namespace Flax.Build else { // Copy to the destination folder - Utilities.FileCopy(assemblyPath, Path.Combine(dotnetOutputPath, assemblyFileName)); + Utilities.FileCopy(assemblyPath, Path.Combine(dotnetOutputPath, assemblyFileName), Utilities.CopyMode.OverrideIfNewer); } }; if (Configuration.MaxConcurrency > 1 && Configuration.ConcurrencyProcessorScale > 0.0f && !dotnetAotDebug) diff --git a/Source/Tools/Flax.Build/Build/EngineModule.cs b/Source/Tools/Flax.Build/Build/EngineModule.cs index 679a558d2..5fc470665 100644 --- a/Source/Tools/Flax.Build/Build/EngineModule.cs +++ b/Source/Tools/Flax.Build/Build/EngineModule.cs @@ -42,7 +42,8 @@ namespace Flax.Build BinaryModuleName = "FlaxEngine"; options.ScriptingAPI.Defines.Add("FLAX"); options.ScriptingAPI.Defines.Add("FLAX_ASSERTIONS"); - options.ScriptingAPI.FileReferences.Add(Utilities.RemovePathRelativeParts(Path.Combine(Globals.EngineRoot, "Source", "Platforms", "DotNet", "Newtonsoft.Json.dll"))); + var newtonsoftJsonPath = options.Platform?.HasDynamicCodeExecutionSupport ?? true ? "Newtonsoft.Json.dll" : "AOT/Newtonsoft.Json.dll"; + options.ScriptingAPI.FileReferences.Add(Utilities.RemovePathRelativeParts(Path.Combine(Globals.EngineRoot, "Source", "Platforms", "DotNet", newtonsoftJsonPath))); options.ScriptingAPI.SystemReferences.Add("System.ComponentModel.TypeConverter"); } } diff --git a/Source/Tools/Flax.Build/Utilities/Utilities.cs b/Source/Tools/Flax.Build/Utilities/Utilities.cs index 0ce5c8b7b..917b8aa77 100644 --- a/Source/Tools/Flax.Build/Utilities/Utilities.cs +++ b/Source/Tools/Flax.Build/Utilities/Utilities.cs @@ -144,24 +144,51 @@ namespace Flax.Build return new TwoWayEnumerator(source.GetEnumerator()); } + /// + /// File copy modes. + /// + public enum CopyMode + { + /// + /// Copies the file to the destination, fails if it already exists. + /// + New, + + /// + /// If destination file exists, it will be overriden. + /// + OverrideIfExists, + + /// + /// If destination file exists, has the same size and is newer than source file, it won't be overriden (avoids unnecessary copies). + /// + OverrideIfNewer, + } + /// /// Copies the file. /// /// The source file path. /// The destination file path. - /// if the destination file can be overwritten; otherwise, . - public static void FileCopy(string srcFilePath, string dstFilePath, bool overwrite = true) + /// Copy operation modes. + public static void FileCopy(string srcFilePath, string dstFilePath, CopyMode mode = CopyMode.OverrideIfExists) { if (string.IsNullOrEmpty(srcFilePath)) throw new ArgumentNullException(nameof(srcFilePath)); if (string.IsNullOrEmpty(dstFilePath)) throw new ArgumentNullException(nameof(dstFilePath)); + if (mode == CopyMode.OverrideIfNewer && + File.Exists(dstFilePath) && + File.GetLastWriteTime(srcFilePath) <= File.GetLastWriteTime(dstFilePath) && + new FileInfo(dstFilePath).Length == new FileInfo(srcFilePath).Length) + return; + Log.Verbose(srcFilePath + " -> " + dstFilePath); try { - File.Copy(srcFilePath, dstFilePath, overwrite); + File.Copy(srcFilePath, dstFilePath, mode != CopyMode.New); } catch (Exception ex) { @@ -173,6 +200,17 @@ namespace Flax.Build } } + /// + /// Copies the file. + /// + /// The source file path. + /// The destination file path. + /// if the destination file can be overwritten; otherwise, . + public static void FileCopy(string srcFilePath, string dstFilePath, bool overwrite) + { + FileCopy(srcFilePath, dstFilePath, overwrite ? CopyMode.OverrideIfExists : CopyMode.New); + } + /// /// Copies the directories. /// From cf048c98042a85323ab75a7bac8e858f368093d3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 26 Nov 2025 00:07:00 -0800 Subject: [PATCH 3/7] Fix path filter query warning --- Source/Engine/Platform/Base/FileSystemBase.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Platform/Base/FileSystemBase.cpp b/Source/Engine/Platform/Base/FileSystemBase.cpp index 36b4182de..f414bbd01 100644 --- a/Source/Engine/Platform/Base/FileSystemBase.cpp +++ b/Source/Engine/Platform/Base/FileSystemBase.cpp @@ -327,10 +327,10 @@ bool FileSystemBase::PathFilterHelper(const char* path, const char* searchPatter // All files return true; } - else if (searchPattern[0] == '*' && searchPatternLength < pathLength && StringUtils::Compare(path + pathLength - searchPatternLength + 1, searchPattern + 1, searchPatternLength - 1) == 0) + else if (searchPattern[0] == '*' && StringUtils::Find(searchPattern + 1, "*") == nullptr) { // Path ending - return true; + return searchPatternLength < pathLength && StringUtils::Compare(path + pathLength - searchPatternLength + 1, searchPattern + 1, searchPatternLength - 1) == 0; } else if (searchPattern[0] == '*' && searchPatternLength > 2 && searchPattern[searchPatternLength - 1] == '*') { @@ -346,7 +346,7 @@ bool FileSystemBase::PathFilterHelper(const char* path, const char* searchPatter else { // TODO: implement all cases in a generic way - LOG(Warning, "DirectoryGetFiles: Wildcard filter is not implemented"); + LOG(Warning, "DirectoryGetFiles: Wildcard filter is not implemented ({})", String(searchPattern)); } return false; } From c8839b8587f01b8451d7f990caaa294a2fa6f2dd Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 26 Nov 2025 00:22:48 -0800 Subject: [PATCH 4/7] Add support for Cooperative Suspend when running on Mono Informs mono runtime that Job System, Thread Pool or Content Load threads can wait when they are going idle between tasks. --- Source/Engine/Content/Content.cpp | 5 ++ .../Engine/Scripting/Internal/InternalCalls.h | 48 ++++++++++++++++++- Source/Engine/Scripting/Runtime/DotNet.cpp | 6 +++ Source/Engine/Threading/JobSystem.cpp | 5 ++ Source/Engine/Threading/ThreadPool.cpp | 5 ++ 5 files changed, 68 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Content/Content.cpp b/Source/Engine/Content/Content.cpp index 66e18a231..4a9ea500b 100644 --- a/Source/Engine/Content/Content.cpp +++ b/Source/Engine/Content/Content.cpp @@ -30,6 +30,7 @@ #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerMemory.h" #include "Engine/Scripting/ManagedCLR/MClass.h" +#include "Engine/Scripting/Internal/InternalCalls.h" #include "Engine/Scripting/Scripting.h" #if USE_EDITOR #include "Editor/Editor.h" @@ -346,17 +347,21 @@ int32 LoadingThread::Run() ContentLoadTask* task; ThisLoadThread = this; + MONO_THREAD_INFO_TYPE* monoThreadInfo = nullptr; while (Platform::AtomicRead(&_exitFlag) == 0) { if (LoadTasks.try_dequeue(task)) { Run(task); + MONO_THREAD_INFO_GET(monoThreadInfo); } else { + MONO_ENTER_GC_SAFE_WITH_INFO(monoThreadInfo); LoadTasksMutex.Lock(); LoadTasksSignal.Wait(LoadTasksMutex); LoadTasksMutex.Unlock(); + MONO_EXIT_GC_SAFE_WITH_INFO; } } diff --git a/Source/Engine/Scripting/Internal/InternalCalls.h b/Source/Engine/Scripting/Internal/InternalCalls.h index b72993234..81d86ea34 100644 --- a/Source/Engine/Scripting/Internal/InternalCalls.h +++ b/Source/Engine/Scripting/Internal/InternalCalls.h @@ -9,7 +9,7 @@ #if defined(__clang__) // Helper utility to override vtable entry with automatic restore -// See BindingsGenerator.Cpp.cs that generates virtuall method wrappers for scripting to properly call overriden base method +// See BindingsGenerator.Cpp.cs that generates virtual method wrappers for scripting to properly call overriden base method struct FLAXENGINE_API VTableFunctionInjector { void** VTableAddr; @@ -100,3 +100,49 @@ T& InternalGetReference(T* obj) DebugLog::ThrowNullReference(); return *obj; } + +#ifdef USE_MONO_AOT_COOP + +// Cooperative Suspend - where threads suspend themselves when the runtime requests it. +// https://www.mono-project.com/docs/advanced/runtime/docs/coop-suspend/ +typedef struct _MonoStackData { + void* stackpointer; + const char* function_name; +} MonoStackData; +#if BUILD_DEBUG +#define MONO_STACKDATA(x) MonoStackData x = { &x, __func__ } +#else +#define MONO_STACKDATA(x) MonoStackData x = { &x, NULL } +#endif +#define MONO_THREAD_INFO_TYPE struct MonoThreadInfo +DLLIMPORT extern "C" MONO_THREAD_INFO_TYPE* mono_thread_info_attach(void); +DLLIMPORT extern "C" void* mono_threads_enter_gc_safe_region_with_info(MONO_THREAD_INFO_TYPE* info, MonoStackData* stackdata); +DLLIMPORT extern "C" void mono_threads_exit_gc_safe_region_internal(void* cookie, MonoStackData* stackdata); +#ifndef _MONO_UTILS_FORWARD_ +typedef struct _MonoDomain MonoDomain; +DLLIMPORT extern "C" MonoDomain* mono_domain_get(void); +#endif +#define MONO_ENTER_GC_SAFE \ + do { \ + MONO_STACKDATA(__gc_safe_dummy); \ + void* __gc_safe_cookie = mono_threads_enter_gc_safe_region_internal(&__gc_safe_dummy) +#define MONO_EXIT_GC_SAFE \ + mono_threads_exit_gc_safe_region_internal(__gc_safe_cookie, &__gc_safe_dummy); \ + } while (0) +#define MONO_ENTER_GC_SAFE_WITH_INFO(info) \ + do { \ + MONO_STACKDATA(__gc_safe_dummy); \ + void* __gc_safe_cookie = mono_threads_enter_gc_safe_region_with_info((info), &__gc_safe_dummy) +#define MONO_EXIT_GC_SAFE_WITH_INFO MONO_EXIT_GC_SAFE +#define MONO_THREAD_INFO_GET(info) if (!info && mono_domain_get()) info = mono_thread_info_attach() + +#else + +#define MONO_ENTER_GC_SAFE +#define MONO_EXIT_GC_SAFE +#define MONO_ENTER_GC_SAFE_WITH_INFO(info) +#define MONO_EXIT_GC_SAFE_WITH_INFO +#define MONO_THREAD_INFO_GET(info) +#define mono_thread_info_attach() nullptr + +#endif diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index 2e98094b2..ed662801e 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -2147,7 +2147,13 @@ bool InitHostfxr() #endif // Adjust GC threads suspending mode to not block attached native threads (eg. Job System) + // https://www.mono-project.com/docs/advanced/runtime/docs/coop-suspend/ +#if USE_MONO_AOT_COOP + Platform::SetEnvironmentVariable(TEXT("MONO_THREADS_SUSPEND"), TEXT("coop")); + Platform::SetEnvironmentVariable(TEXT("MONO_SLEEP_ABORT_LIMIT"), TEXT("5000")); // in ms +#else Platform::SetEnvironmentVariable(TEXT("MONO_THREADS_SUSPEND"), TEXT("preemptive")); +#endif #if defined(USE_MONO_AOT_MODE) // Enable AOT mode (per-platform) diff --git a/Source/Engine/Threading/JobSystem.cpp b/Source/Engine/Threading/JobSystem.cpp index e90a2e847..692a088b7 100644 --- a/Source/Engine/Threading/JobSystem.cpp +++ b/Source/Engine/Threading/JobSystem.cpp @@ -15,6 +15,7 @@ #include "Engine/Profiler/ProfilerMemory.h" #if USE_CSHARP #include "Engine/Scripting/ManagedCLR/MCore.h" +#include "Engine/Scripting/Internal/InternalCalls.h" #endif #define JOB_SYSTEM_ENABLED 1 @@ -184,6 +185,7 @@ int32 JobSystemThread::Run() JobData data; Function job; bool attachCSharpThread = true; + MONO_THREAD_INFO_TYPE* monoThreadInfo = nullptr; while (Platform::AtomicRead(&ExitFlag) == 0) { // Try to get a job @@ -205,6 +207,7 @@ int32 JobSystemThread::Run() { MCore::Thread::Attach(); attachCSharpThread = false; + monoThreadInfo = mono_thread_info_attach(); } #endif @@ -244,9 +247,11 @@ int32 JobSystemThread::Run() else { // Wait for signal + MONO_ENTER_GC_SAFE_WITH_INFO(monoThreadInfo); JobsMutex.Lock(); JobsSignal.Wait(JobsMutex); JobsMutex.Unlock(); + MONO_EXIT_GC_SAFE_WITH_INFO; } } return 0; diff --git a/Source/Engine/Threading/ThreadPool.cpp b/Source/Engine/Threading/ThreadPool.cpp index 4943469a5..d7410d0fc 100644 --- a/Source/Engine/Threading/ThreadPool.cpp +++ b/Source/Engine/Threading/ThreadPool.cpp @@ -15,6 +15,7 @@ #include "Engine/Platform/CPUInfo.h" #include "Engine/Platform/Thread.h" #include "Engine/Profiler/ProfilerMemory.h" +#include "Engine/Scripting/Internal/InternalCalls.h" FLAXENGINE_API bool IsInMainThread() { @@ -117,6 +118,7 @@ int32 ThreadPool::ThreadProc() Platform::SetThreadAffinityMask(THREAD_POOL_AFFINITY_MASK((int32)index)); #endif ThreadPoolTask* task; + MONO_THREAD_INFO_TYPE* monoThreadInfo = nullptr; // Work until end while (Platform::AtomicRead(&ThreadPoolImpl::ExitFlag) == 0) @@ -125,12 +127,15 @@ int32 ThreadPool::ThreadProc() if (ThreadPoolImpl::Jobs.try_dequeue(task)) { task->Execute(); + MONO_THREAD_INFO_GET(monoThreadInfo); } else { + MONO_ENTER_GC_SAFE_WITH_INFO(monoThreadInfo); ThreadPoolImpl::JobsMutex.Lock(); ThreadPoolImpl::JobsSignal.Wait(ThreadPoolImpl::JobsMutex); ThreadPoolImpl::JobsMutex.Unlock(); + MONO_EXIT_GC_SAFE_WITH_INFO; } } From 403d2cedc0a8665328a7efeb3817f6544b642978 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 26 Nov 2025 06:28:54 -0800 Subject: [PATCH 5/7] Updates to engine for porting to blue platform --- Source/Engine/Scripting/Scripting.cs | 4 +- Source/Tools/Flax.Build/Build/EngineTarget.cs | 2 +- .../Platforms/Unix/UnixToolchain.cs | 46 +++++++------------ 3 files changed, 19 insertions(+), 33 deletions(-) diff --git a/Source/Engine/Scripting/Scripting.cs b/Source/Engine/Scripting/Scripting.cs index b308f5dae..7f9f2980c 100644 --- a/Source/Engine/Scripting/Scripting.cs +++ b/Source/Engine/Scripting/Scripting.cs @@ -198,7 +198,7 @@ namespace FlaxEngine private static void OnLocalizationChanged() { // Invariant-globalization only (see InitHostfxr with Mono) -#if !(PLATFORM_IOS || PLATFORM_SWITCH) +#if !(PLATFORM_IOS || PLATFORM_SWITCH || PLATFORM_PS4 || PLATFORM_PS5) var currentThread = Thread.CurrentThread; var language = Localization.CurrentLanguage; if (language != null) @@ -261,7 +261,7 @@ namespace FlaxEngine internal static ManagedHandle CultureInfoToManaged(int lcid) { -#if PLATFORM_IOS || PLATFORM_SWITCH +#if PLATFORM_IOS || PLATFORM_SWITCH || PLATFORM_PS4 || PLATFORM_PS5 // Invariant-globalization only (see InitHostfxr with Mono) lcid = 0; #endif diff --git a/Source/Tools/Flax.Build/Build/EngineTarget.cs b/Source/Tools/Flax.Build/Build/EngineTarget.cs index 827205e91..039f9fb16 100644 --- a/Source/Tools/Flax.Build/Build/EngineTarget.cs +++ b/Source/Tools/Flax.Build/Build/EngineTarget.cs @@ -142,7 +142,7 @@ namespace Flax.Build } /// - /// Returns true if this build target should use separate (aka main-only) executable file and separate runtime (in shared library). Used on platforms that don't support linking again executable file but only shared library (see HasExecutableFileReferenceSupport). + /// Returns true if this build target should use separate (aka main-only) executable file and separate runtime (in shared library). Used on platforms that don't support linking (or symbol lookup) against executable file but only shared library (see HasExecutableFileReferenceSupport). /// public virtual bool UseSeparateMainExecutable(BuildOptions buildOptions) { diff --git a/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs b/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs index 6d5aacdde..fcb4397a1 100644 --- a/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs @@ -320,6 +320,17 @@ namespace Flax.Build.Platforms { } + /// + /// Gets linker argument to reference a specific shared library file. + /// + /// The graph. + /// The options. + /// The shared library file path. + protected virtual string GetSharedLibraryLinkArg(TaskGraph graph, BuildOptions options, string library) + { + return string.Format("\"-l{0}\"", GetLibName(library)); + } + /// public override CompileOutput CompileCppFiles(TaskGraph graph, BuildOptions options, List sourceFiles, string outputPath) { @@ -527,7 +538,8 @@ namespace Flax.Build.Platforms // Input libraries var libraryPaths = new HashSet(); - foreach (var library in linkEnvironment.InputLibraries) + var dynamicLibExt = Platform.SharedLibraryFileExtension; + foreach (var library in linkEnvironment.InputLibraries.Concat(options.Libraries)) { var dir = Path.GetDirectoryName(library); var ext = Path.GetExtension(library); @@ -539,37 +551,12 @@ namespace Flax.Build.Platforms { // Skip executable } - else if (ext == ".so") + else if (ext == dynamicLibExt) { // Link against dynamic library task.PrerequisiteFiles.Add(library); libraryPaths.Add(dir); - args.Add(string.Format("\"-l{0}\"", GetLibName(library))); - } - else - { - task.PrerequisiteFiles.Add(library); - args.Add(string.Format("\"{0}\"", GetLibName(library))); - } - } - foreach (var library in options.Libraries) - { - var dir = Path.GetDirectoryName(library); - var ext = Path.GetExtension(library); - if (string.IsNullOrEmpty(dir)) - { - args.Add(string.Format("\"-l{0}\"", library)); - } - else if (string.IsNullOrEmpty(ext)) - { - // Skip executable - } - else if (ext == ".so") - { - // Link against dynamic library - task.PrerequisiteFiles.Add(library); - libraryPaths.Add(dir); - args.Add(string.Format("\"-l{0}\"", GetLibName(library))); + args.Add(GetSharedLibraryLinkArg(graph, options, library)); } else { @@ -580,8 +567,7 @@ namespace Flax.Build.Platforms // Input files (link static libraries last) task.PrerequisiteFiles.AddRange(linkEnvironment.InputFiles); - foreach (var file in linkEnvironment.InputFiles.Where(x => !x.EndsWith(".a")) - .Concat(linkEnvironment.InputFiles.Where(x => x.EndsWith(".a")))) + foreach (var file in linkEnvironment.InputFiles.Where(x => !x.EndsWith(".a")).Concat(linkEnvironment.InputFiles.Where(x => x.EndsWith(".a")))) { args.Add(string.Format("\"{0}\"", file.Replace('\\', '/'))); } From 1e3ce48024efbaba333caa92451762d5b3f2a1a9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 26 Nov 2025 23:43:20 -0800 Subject: [PATCH 6/7] Fix compilation regression --- Source/Engine/Scripting/Internal/InternalCalls.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Engine/Scripting/Internal/InternalCalls.h b/Source/Engine/Scripting/Internal/InternalCalls.h index 81d86ea34..4535952ea 100644 --- a/Source/Engine/Scripting/Internal/InternalCalls.h +++ b/Source/Engine/Scripting/Internal/InternalCalls.h @@ -138,6 +138,7 @@ DLLIMPORT extern "C" MonoDomain* mono_domain_get(void); #else +#define MONO_THREAD_INFO_TYPE void #define MONO_ENTER_GC_SAFE #define MONO_EXIT_GC_SAFE #define MONO_ENTER_GC_SAFE_WITH_INFO(info) From a1999183f21855aa591b377607b6fac5596f32f2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 27 Nov 2025 09:13:14 +0100 Subject: [PATCH 7/7] Fix compilation regression --- Source/Engine/Platform/Unix/UnixFileSystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Platform/Unix/UnixFileSystem.cpp b/Source/Engine/Platform/Unix/UnixFileSystem.cpp index d85f60563..df854c138 100644 --- a/Source/Engine/Platform/Unix/UnixFileSystem.cpp +++ b/Source/Engine/Platform/Unix/UnixFileSystem.cpp @@ -367,7 +367,7 @@ bool UnixFileSystem::getFilesFromDirectoryTop(Array& results, const char if (S_ISREG(statEntry.st_mode) != 0) { // Validate with filter - if (FileSystem::PathFilterHelper(fullPath, searchPattern)) + if (FileSystemBase::PathFilterHelper(fullPath, searchPattern)) results.Add(String(fullPath)); } }