From e598746980bdd9d1d6a71d2a92c25b69d35f05ab Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sun, 30 Jul 2023 15:26:29 +0300 Subject: [PATCH 1/5] Fix build issues in Visual Studio 2022 Preview 5 --- Source/ThirdParty/fmt/format.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/ThirdParty/fmt/format.h b/Source/ThirdParty/fmt/format.h index 0550af1b5..f2151d7b0 100644 --- a/Source/ThirdParty/fmt/format.h +++ b/Source/ThirdParty/fmt/format.h @@ -36,6 +36,7 @@ #include #include #include +#include #include "core.h" From db56284ca4195840a207efcaa8ee44f6ccde7432 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Tue, 1 Aug 2023 19:32:40 +0300 Subject: [PATCH 2/5] Fix WindowsPlatform::LoadLibrary to not modify the string parameter --- Source/Engine/Platform/Windows/WindowsPlatform.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Source/Engine/Platform/Windows/WindowsPlatform.cpp b/Source/Engine/Platform/Windows/WindowsPlatform.cpp index 4d43272d1..970ccfa10 100644 --- a/Source/Engine/Platform/Windows/WindowsPlatform.cpp +++ b/Source/Engine/Platform/Windows/WindowsPlatform.cpp @@ -1191,11 +1191,8 @@ void* WindowsPlatform::LoadLibrary(const Char* filename) folder = StringView::Empty; if (folder.HasChars()) { - Char& end = ((Char*)folder.Get())[folder.Length()]; - const Char c = end; - end = 0; - SetDllDirectoryW(*folder); - end = c; + String folderNullTerminated(folder); + SetDllDirectoryW(folderNullTerminated.Get()); } // Avoiding windows dialog boxes if missing From fe87eb96e619482dd254917866d536763788af14 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sun, 21 May 2023 00:53:03 +0300 Subject: [PATCH 3/5] Skip post processing when tonemapping and camera artifacts are disabled --- Source/Engine/Renderer/PostProcessingPass.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Renderer/PostProcessingPass.cpp b/Source/Engine/Renderer/PostProcessingPass.cpp index 760b12ea0..345147b61 100644 --- a/Source/Engine/Renderer/PostProcessingPass.cpp +++ b/Source/Engine/Renderer/PostProcessingPass.cpp @@ -190,8 +190,8 @@ void PostProcessingPass::Render(RenderContext& renderContext, GPUTexture* input, PostProcessSettings& settings = renderContext.List->Settings; bool useBloom = EnumHasAnyFlags(view.Flags, ViewFlags::Bloom) && settings.Bloom.Enabled && settings.Bloom.Intensity > 0.0f; - bool useToneMapping = EnumHasAnyFlags(view.Flags, ViewFlags::ToneMapping); - bool useCameraArtifacts = EnumHasAnyFlags(view.Flags, ViewFlags::CameraArtifacts); + bool useToneMapping = EnumHasAnyFlags(view.Flags, ViewFlags::ToneMapping) && settings.ToneMapping.Mode != ToneMappingMode::None; + bool useCameraArtifacts = EnumHasAnyFlags(view.Flags, ViewFlags::CameraArtifacts) && (settings.CameraArtifacts.VignetteIntensity > 0.0f || settings.CameraArtifacts.GrainAmount > 0.0f || settings.CameraArtifacts.ChromaticDistortion > 0.0f || settings.CameraArtifacts.ScreenFadeColor.A > 0.0f); bool useLensFlares = EnumHasAnyFlags(view.Flags, ViewFlags::LensFlares) && settings.LensFlares.Intensity > 0.0f && useBloom; // Ensure to have valid data and if at least one effect should be applied From 47c6e544068831a511f141ed642489a5f1ac2a96 Mon Sep 17 00:00:00 2001 From: Wiktor Kocielski Date: Tue, 8 Aug 2023 06:40:28 +0300 Subject: [PATCH 4/5] Fix exposed public CommonValue inclusion --- .../Graph/CPU/ParticleEmitterGraph.CPU.ParticleModules.cpp | 1 + Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.cpp | 1 + Source/Engine/Particles/Graph/ParticleEmitterGraph.h | 1 + Source/Engine/Particles/ParticleEffect.cpp | 1 + Source/Engine/Particles/ParticleEmitter.h | 1 + Source/Engine/Particles/ParticleSystem.cpp | 1 + Source/Engine/Particles/ParticlesSimulation.cpp | 1 + Source/Engine/Particles/ParticlesSimulation.h | 1 - 8 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.ParticleModules.cpp b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.ParticleModules.cpp index 5d8297452..6ea4b8b4e 100644 --- a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.ParticleModules.cpp +++ b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.ParticleModules.cpp @@ -3,6 +3,7 @@ #include "ParticleEmitterGraph.CPU.h" #include "Engine/Core/Random.h" #include "Engine/Utilities/Noise.h" +#include "Engine/Core/Types/CommonValue.h" // ReSharper disable CppCStyleCast // ReSharper disable CppClangTidyClangDiagnosticCastAlign diff --git a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.cpp b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.cpp index 60df4a6d6..513252e80 100644 --- a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.cpp +++ b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.cpp @@ -6,6 +6,7 @@ #include "Engine/Serialization/FileReadStream.h" #include "Engine/Visject/ShaderGraphUtilities.h" #include "Engine/Engine/Globals.h" +#include "Engine/Core/Types/CommonValue.h" /// /// GPU particles shader source code template has special marks for generated code. diff --git a/Source/Engine/Particles/Graph/ParticleEmitterGraph.h b/Source/Engine/Particles/Graph/ParticleEmitterGraph.h index 7b0b53eb7..e184b0660 100644 --- a/Source/Engine/Particles/Graph/ParticleEmitterGraph.h +++ b/Source/Engine/Particles/Graph/ParticleEmitterGraph.h @@ -8,6 +8,7 @@ #include "Engine/Particles/Types.h" #include "Engine/Particles/ParticlesSimulation.h" #include "Engine/Particles/ParticlesData.h" +#include "Engine/Core/Types/CommonValue.h" class ParticleEffect; diff --git a/Source/Engine/Particles/ParticleEffect.cpp b/Source/Engine/Particles/ParticleEffect.cpp index 1d7fa18a1..8a767ba22 100644 --- a/Source/Engine/Particles/ParticleEffect.cpp +++ b/Source/Engine/Particles/ParticleEffect.cpp @@ -2,6 +2,7 @@ #include "ParticleEffect.h" #include "Particles.h" +#include "Engine/Core/Types/CommonValue.h" #include "Engine/Serialization/JsonTools.h" #include "Engine/Serialization/Serialization.h" #include "Engine/Level/Scene/SceneRendering.h" diff --git a/Source/Engine/Particles/ParticleEmitter.h b/Source/Engine/Particles/ParticleEmitter.h index 93279290f..4d502bcf5 100644 --- a/Source/Engine/Particles/ParticleEmitter.h +++ b/Source/Engine/Particles/ParticleEmitter.h @@ -3,6 +3,7 @@ #pragma once #include "Engine/Content/BinaryAsset.h" +#include "Engine/Core/Math/BoundingBox.h" #include "Engine/Graphics/Shaders/Cache/ShaderAssetBase.h" #include "Graph/CPU/ParticleEmitterGraph.CPU.h" #if COMPILE_WITH_GPU_PARTICLES diff --git a/Source/Engine/Particles/ParticleSystem.cpp b/Source/Engine/Particles/ParticleSystem.cpp index 6f63101b2..0ef193cdc 100644 --- a/Source/Engine/Particles/ParticleSystem.cpp +++ b/Source/Engine/Particles/ParticleSystem.cpp @@ -2,6 +2,7 @@ #include "ParticleSystem.h" #include "ParticleEffect.h" +#include "Engine/Core/Types/CommonValue.h" #include "Engine/Level/Level.h" #include "Engine/Content/Factories/BinaryAssetFactory.h" #include "Engine/Serialization/MemoryReadStream.h" diff --git a/Source/Engine/Particles/ParticlesSimulation.cpp b/Source/Engine/Particles/ParticlesSimulation.cpp index c68862925..cfdb51432 100644 --- a/Source/Engine/Particles/ParticlesSimulation.cpp +++ b/Source/Engine/Particles/ParticlesSimulation.cpp @@ -6,6 +6,7 @@ #include "Particles.h" #include "Engine/Graphics/GPUBuffer.h" #include "Engine/Graphics/GPUDevice.h" +#include "Engine/Core/Types/CommonValue.h" ParticleEmitterInstance::ParticleEmitterInstance() { diff --git a/Source/Engine/Particles/ParticlesSimulation.h b/Source/Engine/Particles/ParticlesSimulation.h index a7d16d3d1..d7b4cd1d1 100644 --- a/Source/Engine/Particles/ParticlesSimulation.h +++ b/Source/Engine/Particles/ParticlesSimulation.h @@ -2,7 +2,6 @@ #pragma once -#include "Engine/Core/Types/CommonValue.h" #include "Engine/Visject/GraphParameter.h" class ParticleSystemInstance; From 9d8105e3f3bbf61d8b5082c5c5d8ec48e9414e83 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Tue, 8 Aug 2023 17:35:13 +0300 Subject: [PATCH 5/5] Separate managed assembly unloading and scripting ALC reinitialization Fixes an issue with multiple managed assemblies unloading and releasing all cached data before native resources were fully released in other assemblies. --- .../Engine/Engine/NativeInterop.Unmanaged.cs | 31 +++++++++++-------- Source/Engine/Scripting/ManagedCLR/MCore.h | 2 +- Source/Engine/Scripting/Runtime/DotNet.cpp | 18 +++++------ Source/Engine/Scripting/Runtime/Mono.cpp | 2 +- Source/Engine/Scripting/Runtime/None.cpp | 2 +- Source/Engine/Scripting/Scripting.cpp | 4 ++- 6 files changed, 33 insertions(+), 26 deletions(-) diff --git a/Source/Engine/Engine/NativeInterop.Unmanaged.cs b/Source/Engine/Engine/NativeInterop.Unmanaged.cs index 57454ad1e..d2a8ca55e 100644 --- a/Source/Engine/Engine/NativeInterop.Unmanaged.cs +++ b/Source/Engine/Engine/NativeInterop.Unmanaged.cs @@ -903,11 +903,25 @@ namespace FlaxEngine.Interop AssemblyLocations.Remove(assembly.FullName); - // Clear all caches which might hold references to closing assembly + // Unload native library handles associated for this assembly + string nativeLibraryName = assemblyOwnedNativeLibraries.GetValueOrDefault(assembly); + if (nativeLibraryName != null && loadedNativeLibraries.TryGetValue(nativeLibraryName, out IntPtr nativeLibrary)) + { + NativeLibrary.Free(nativeLibrary); + loadedNativeLibraries.Remove(nativeLibraryName); + } + if (nativeLibraryName != null) + nativeLibraryPaths.Remove(nativeLibraryName); + } + + [UnmanagedCallersOnly] + internal static void ReloadScriptingAssemblyLoadContext() + { +#if FLAX_EDITOR + // Clear all caches which might hold references to assemblies in collectible ALC typeCache.Clear(); // Release all references in collectible ALC -#if FLAX_EDITOR cachedDelegatesCollectible.Clear(); foreach (var pair in typeHandleCacheCollectible) pair.Value.Free(); @@ -918,23 +932,13 @@ namespace FlaxEngine.Interop foreach (var handle in fieldHandleCacheCollectible) handle.Free(); fieldHandleCacheCollectible.Clear(); -#endif + _typeSizeCache.Clear(); foreach (var pair in classAttributesCacheCollectible) pair.Value.Free(); classAttributesCacheCollectible.Clear(); - // Unload native library handles associated for this assembly - string nativeLibraryName = assemblyOwnedNativeLibraries.GetValueOrDefault(assembly); - if (nativeLibraryName != null && loadedNativeLibraries.TryGetValue(nativeLibraryName, out IntPtr nativeLibrary)) - { - NativeLibrary.Free(nativeLibrary); - loadedNativeLibraries.Remove(nativeLibraryName); - } - if (nativeLibraryName != null) - nativeLibraryPaths.Remove(nativeLibraryName); - // Unload the ALC bool unloading = true; scriptingAssemblyLoadContext.Unloading += (alc) => { unloading = false; }; @@ -945,6 +949,7 @@ namespace FlaxEngine.Interop InitScriptingAssemblyLoadContext(); DelegateHelpers.InitMethods(); +#endif } [UnmanagedCallersOnly] diff --git a/Source/Engine/Scripting/ManagedCLR/MCore.h b/Source/Engine/Scripting/ManagedCLR/MCore.h index 38b58403d..e1de3c207 100644 --- a/Source/Engine/Scripting/ManagedCLR/MCore.h +++ b/Source/Engine/Scripting/ManagedCLR/MCore.h @@ -47,7 +47,7 @@ public: #if USE_EDITOR // Called by Scripting in a middle of hot-reload (after unloading modules but before loading them again). - static void OnMidHotReload(); + static void ReloadScriptingAssemblyLoadContext(); #endif public: diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index 8d75d7a95..36f1f16af 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -160,6 +160,7 @@ DECLARE_ENUM_OPERATORS(MTypeAttributes); DECLARE_ENUM_OPERATORS(MMethodAttributes); DECLARE_ENUM_OPERATORS(MFieldAttributes); +// Multiple AppDomains are superseded by AssemblyLoadContext in .NET extern MDomain* MRootDomain; extern MDomain* MActiveDomain; extern Array> MDomains; @@ -301,11 +302,14 @@ void MCore::UnloadEngine() #if USE_EDITOR -void MCore::OnMidHotReload() +void MCore::ReloadScriptingAssemblyLoadContext() { // Clear any cached class attributes (see https://github.com/FlaxEngine/FlaxEngine/issues/1108) for (auto e : CachedClassHandles) e.Value->_attributes.Clear(); + + static void* ReloadScriptingAssemblyLoadContextPtr = GetStaticMethodPointer(TEXT("ReloadScriptingAssemblyLoadContext")); + CallStaticMethod(ReloadScriptingAssemblyLoadContextPtr); } #endif @@ -723,16 +727,12 @@ bool MAssembly::LoadImage(const String& assemblyPath, const StringView& nativePa bool MAssembly::UnloadImage(bool isReloading) { - if (_handle) + if (_handle && isReloading) { - // TODO: closing assembly on reload only is copy-paste from mono, do we need do this on .NET too? - if (isReloading) - { - LOG(Info, "Unloading managed assembly \'{0}\' (is reloading)", String(_name)); + LOG(Info, "Unloading managed assembly \'{0}\' (is reloading)", String(_name)); - static void* CloseAssemblyPtr = GetStaticMethodPointer(TEXT("CloseAssembly")); - CallStaticMethod(CloseAssemblyPtr, _handle); - } + static void* CloseAssemblyPtr = GetStaticMethodPointer(TEXT("CloseAssembly")); + CallStaticMethod(CloseAssemblyPtr, _handle); CachedAssemblyHandles.Remove(_handle); _handle = nullptr; diff --git a/Source/Engine/Scripting/Runtime/Mono.cpp b/Source/Engine/Scripting/Runtime/Mono.cpp index ca4ef367b..0a60db42f 100644 --- a/Source/Engine/Scripting/Runtime/Mono.cpp +++ b/Source/Engine/Scripting/Runtime/Mono.cpp @@ -716,7 +716,7 @@ void MCore::UnloadEngine() #if USE_EDITOR -void MCore::OnMidHotReload() +void MCore::ReloadScriptingAssemblyLoadContext() { } diff --git a/Source/Engine/Scripting/Runtime/None.cpp b/Source/Engine/Scripting/Runtime/None.cpp index c8f8926e5..ef9c118cf 100644 --- a/Source/Engine/Scripting/Runtime/None.cpp +++ b/Source/Engine/Scripting/Runtime/None.cpp @@ -61,7 +61,7 @@ void MCore::UnloadEngine() #if USE_EDITOR -void MCore::OnMidHotReload() +void MCore::ReloadScriptingAssemblyLoadContext() { } diff --git a/Source/Engine/Scripting/Scripting.cpp b/Source/Engine/Scripting/Scripting.cpp index bbfa802f1..6210063b8 100644 --- a/Source/Engine/Scripting/Scripting.cpp +++ b/Source/Engine/Scripting/Scripting.cpp @@ -706,7 +706,9 @@ void Scripting::Reload(bool canTriggerSceneReload) modules.Clear(); _nonNativeModules.ClearDelete(); _hasGameModulesLoaded = false; - MCore::OnMidHotReload(); + + // Release and create a new assembly load context for user assemblies + MCore::ReloadScriptingAssemblyLoadContext(); // Give GC a try to cleanup old user objects and the other mess MCore::GC::Collect();