diff --git a/Content/Shaders/SDF.flax b/Content/Shaders/SDF.flax index 5141c14dd..709cc20f2 100644 --- a/Content/Shaders/SDF.flax +++ b/Content/Shaders/SDF.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d2b1dc1523cb2140db7ce5fed6e97b09d7fcebbe6cc19fca7708b5b882267040 -size 4175 +oid sha256:d3922811f0eb56cbb515c93cd53d80316740ea78219aa81118d2c9dee4a9d230 +size 4142 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/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/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..f414bbd01 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] == '*' && StringUtils::Find(searchPattern + 1, "*") == nullptr) + { + // Path ending + return searchPatternLength < pathLength && StringUtils::Compare(path + pathLength - searchPatternLength + 1, searchPattern + 1, searchPatternLength - 1) == 0; + } + 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 ({})", String(searchPattern)); + } + 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..df854c138 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 (FileSystemBase::PathFilterHelper(fullPath, searchPattern)) + results.Add(String(fullPath)); } } diff --git a/Source/Engine/Renderer/DepthOfFieldPass.h b/Source/Engine/Renderer/DepthOfFieldPass.h index b7351c4b1..098c4061e 100644 --- a/Source/Engine/Renderer/DepthOfFieldPass.h +++ b/Source/Engine/Renderer/DepthOfFieldPass.h @@ -52,9 +52,12 @@ private: void OnShaderReloading(Asset* obj) { _psDofDepthBlurGeneration->ReleaseGPU(); - _psBokehGeneration->ReleaseGPU(); - _psBokeh->ReleaseGPU(); - _psBokehComposite->ReleaseGPU(); + if (_psBokehGeneration) + _psBokehGeneration->ReleaseGPU(); + if (_psBokeh) + _psBokeh->ReleaseGPU(); + if (_psBokehComposite) + _psBokehComposite->ReleaseGPU(); invalidateResources(); } #endif diff --git a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp index 1c1b9e4e3..ce0ec1881 100644 --- a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp +++ b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp @@ -551,9 +551,6 @@ bool GlobalSurfaceAtlasPass::Init() // Check platform support const auto device = GPUDevice::Instance; _supported = device->GetFeatureLevel() >= FeatureLevel::SM5 && device->Limits.HasCompute && device->Limits.HasTypedUAVLoad; -#if PLATFORM_APPLE_FAMILY - _supported = false; // Vulkan over Metal has some issues in complex scenes with DDGI -#endif return false; } diff --git a/Source/Engine/Scripting/Internal/InternalCalls.h b/Source/Engine/Scripting/Internal/InternalCalls.h index b72993234..4535952ea 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,50 @@ 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_THREAD_INFO_TYPE void +#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 d8e5acaff..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) @@ -2155,9 +2161,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 diff --git a/Source/Engine/Scripting/Scripting.cpp b/Source/Engine/Scripting/Scripting.cpp index 2f7b9f9d6..4e17bb80a 100644 --- a/Source/Engine/Scripting/Scripting.cpp +++ b/Source/Engine/Scripting/Scripting.cpp @@ -568,10 +568,14 @@ bool Scripting::Load() #endif // Load FlaxEngine - const String flaxEnginePath = Globals::BinariesFolder / TEXT("FlaxEngine.CSharp.dll"); auto* flaxEngineModule = (NativeBinaryModule*)GetBinaryModuleFlaxEngine(); if (!flaxEngineModule->Assembly->IsLoaded()) { + String flaxEnginePath = Globals::BinariesFolder / TEXT("FlaxEngine.CSharp.dll"); +#if USE_MONO_AOT + if (!FileSystem::FileExists(flaxEnginePath)) + flaxEnginePath = Globals::BinariesFolder / TEXT("Dotnet") / TEXT("FlaxEngine.CSharp.dll"); +#endif if (flaxEngineModule->Assembly->Load(flaxEnginePath)) { LOG(Error, "Failed to load FlaxEngine C# assembly."); 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/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; } } diff --git a/Source/Shaders/AtmosphereFog.hlsl b/Source/Shaders/AtmosphereFog.hlsl index 81bb2e1fd..527260d5a 100644 --- a/Source/Shaders/AtmosphereFog.hlsl +++ b/Source/Shaders/AtmosphereFog.hlsl @@ -329,7 +329,7 @@ float3 GetInscatteredLight(AtmosphericFogData atmosphericFog, in float3 viewPosi if (intersectAtmosphere(viewPosition, viewDir, offset, maxPathLength)) { return float3(offset / 10, 0, 0); - +#if 0 float pathLength = distance(viewPosition, surfacePos); //return pathLength.xxx; @@ -413,6 +413,7 @@ float3 GetInscatteredLight(AtmosphericFogData atmosphericFog, in float3 viewPosi float sunIntensity = 10; inscatteredLight *= sunIntensity; } +#endif } return inscatteredLight; @@ -420,8 +421,6 @@ float3 GetInscatteredLight(AtmosphericFogData atmosphericFog, in float3 viewPosi float4 GetAtmosphericFog(AtmosphericFogData atmosphericFog, float viewFar, float3 viewPosition, float3 viewVector, float sceneDepth, float3 sceneColor) { - float4 result = float4(1, 0, 0, 1); - #if 0 float scale = 0.00001f * atmosphericFog.AtmosphericFogDistanceScale;// convert cm to km @@ -536,8 +535,6 @@ float4 GetAtmosphericFog(AtmosphericFogData atmosphericFog, float viewFar, float //return float4(sun + groundColor + inscatterColor, 1); #endif - - return result; } float4 GetAtmosphericFog(AtmosphericFogData atmosphericFog, float viewFar, float3 worldPosition, float3 cameraPosition) diff --git a/Source/Shaders/Common.hlsl b/Source/Shaders/Common.hlsl index 10b2855f4..66303546a 100644 --- a/Source/Shaders/Common.hlsl +++ b/Source/Shaders/Common.hlsl @@ -96,34 +96,9 @@ // Compiler support for HLSL 2021 that is stricter (need to use or/and/select for vector-based logical operators) #if !defined(__DXC_VERSION_MAJOR) || (__DXC_VERSION_MAJOR <= 1 && __DXC_VERSION_MINOR < 7) -bool InternalAnd(bool a, bool b) { return bool(a && b); } -bool2 InternalAnd(bool2 a, bool2 b) { return bool2(a.x && b.x, a.y && b.y); } -bool3 InternalAnd(bool3 a, bool3 b) { return bool3(a.x && b.x, a.y && b.y, a.z && b.z); } -bool4 InternalAnd(bool4 a, bool4 b) { return bool4(a.x && b.x, a.y && b.y, a.z && b.z, a.w && b.w); } - -bool InternalOr(bool a, bool b) { return bool(a || b); } -bool2 InternalOr(bool2 a, bool2 b) { return bool2(a.x || b.x, a.y || b.y); } -bool3 InternalOr(bool3 a, bool3 b) { return bool3(a.x || b.x, a.y || b.y, a.z || b.z); } -bool4 InternalOr(bool4 a, bool4 b) { return bool4(a.x || b.x, a.y || b.y, a.z || b.z, a.w || b.w); } - -#define SELECT_INTERNAL(type) \ - type InternalSelect(bool c, type a, type b) { return type (c ? a.x : b.x); } \ - type##2 InternalSelect(bool c, type##2 a, type##2 b) { return type##2(c ? a.x : b.x, c ? a.y : b.y); } \ - type##2 InternalSelect(bool2 c, type a, type b) { return type##2(c.x ? a : b, c.y ? a : b); } \ - type##2 InternalSelect(bool2 c, type##2 a, type##2 b) { return type##2(c.x ? a.x : b.x, c.y ? a.y : b.y); } \ - type##3 InternalSelect(bool c, type##3 a, type##3 b) { return type##3(c ? a.x : b.x, c ? a.y : b.y, c ? a.z : b.z); } \ - type##3 InternalSelect(bool3 c, type a, type b) { return type##3(c.x ? a : b, c.y ? a : b, c.z ? a : b); } \ - type##3 InternalSelect(bool3 c, type##3 a, type##3 b) { return type##3(c.x ? a.x : b.x, c.y ? a.y : b.y, c.z ? a.z : b.z); } \ - type##4 InternalSelect(bool c, type##4 a, type##4 b) { return type##4(c ? a.x : b.x, c ? a.y : b.y, c ? a.z : b.z, c ? a.w : b.w); } \ - type##4 InternalSelect(bool4 c, type a, type b) { return type##4(c.x ? a : b, c.y ? a : b, c.z ? a : b, c.w ? a : b); } \ - type##4 InternalSelect(bool4 c, type##4 a, type##4 b) { return type##4(c.x ? a.x : b.x, c.y ? a.y : b.y, c.z ? a.z : b.z, c.w ? a.w : b.w); } -SELECT_INTERNAL(uint) -SELECT_INTERNAL(float) -#undef SELECT_INTERNAL - -#define and(a, b) InternalAnd(a, b) -#define or(a, b) InternalOr(a, b) -#define select(c, a, b) InternalSelect(c, a, b) +#define and(a, b) (a) && (b) +#define or(a, b) (a) || (b) +#define select(c, a, b) (c) ? (a) : (b) #endif diff --git a/Source/Shaders/MaterialCommon.hlsl b/Source/Shaders/MaterialCommon.hlsl index 02f754c94..8c9567c29 100644 --- a/Source/Shaders/MaterialCommon.hlsl +++ b/Source/Shaders/MaterialCommon.hlsl @@ -311,7 +311,7 @@ float3 AOMultiBounce(float visibility, float3 albedo) float2 Flipbook(float2 uv, float frame, float2 sizeXY, float2 flipXY = 0.0f) { - float tile = (int)fmod(frame, sizeXY.x * sizeXY.y); + float tile = (float)(int)fmod(frame, sizeXY.x * sizeXY.y); float2 tileCount = float2(1.0, 1.0) / sizeXY; float tileY = abs(flipXY.y * sizeXY.y - (floor(tile * tileCount.x) + flipXY.y * 1)); float tileX = abs(flipXY.x * sizeXY.x - ((tile - sizeXY.x * floor(tile * tileCount.x)) + flipXY.x * 1)); diff --git a/Source/Shaders/MeshAccelerationStructure.hlsl b/Source/Shaders/MeshAccelerationStructure.hlsl index d98d25404..0559e5f55 100644 --- a/Source/Shaders/MeshAccelerationStructure.hlsl +++ b/Source/Shaders/MeshAccelerationStructure.hlsl @@ -16,13 +16,10 @@ struct BVHNode int Count; // Negative for non-leaf nodes }; -struct BVHBuffers -{ - StructuredBuffer BVHBuffer; - ByteAddressBuffer VertexBuffer; - ByteAddressBuffer IndexBuffer; - uint VertexStride; -}; +// Pass all data via separate params (SPIR-V doesn't support buffers in structures) +#define BVHBuffers_Param StructuredBuffer BVHBuffer, ByteAddressBuffer VertexBuffer, ByteAddressBuffer IndexBuffer, uint VertexStride +#define BVHBuffers_Init(BVHBuffer, VertexBuffer, IndexBuffer, VertexStride) BVHBuffer, VertexBuffer, IndexBuffer, VertexStride +#define BVHBuffers_Pass BVHBuffers_Init(BVHBuffer, VertexBuffer, IndexBuffer, VertexStride) struct BVHHit { @@ -30,11 +27,11 @@ struct BVHHit bool IsBackface; }; -float3 LoadVertexBVH(BVHBuffers bvh, uint index) +float3 LoadVertexBVH(BVHBuffers_Param, uint index) { int addr = index << 2u; - uint vertexIndex = bvh.IndexBuffer.Load(addr); - return asfloat(bvh.VertexBuffer.Load3(vertexIndex * bvh.VertexStride)); + uint vertexIndex = IndexBuffer.Load(addr); + return asfloat(VertexBuffer.Load3(vertexIndex * VertexStride)); } // [https://tavianator.com/2011/ray_box.html] @@ -52,7 +49,7 @@ float RayTestBoxBVH(float3 rayPos, float3 rayDir, float3 boxMin, float3 boxMax) } // Performs raytracing against the BVH acceleration structure to find the closest intersection with a triangle. -bool RayCastBVH(BVHBuffers bvh, float3 rayPos, float3 rayDir, out BVHHit hit, float maxDistance = 1000000.0f) +bool RayCastBVH(BVHBuffers_Param, float3 rayPos, float3 rayDir, out BVHHit hit, float maxDistance = 1000000.0f) { hit = (BVHHit)0; hit.Distance = maxDistance; @@ -66,7 +63,7 @@ bool RayCastBVH(BVHBuffers bvh, float3 rayPos, float3 rayDir, out BVHHit hit, fl LOOP while (stackCount > 0) { - BVHNode node = bvh.BVHBuffer[stack[--stackCount]]; + BVHNode node = BVHBuffer[stack[--stackCount]]; // Raytrace bounds float boundsHit = RayTestBoxBVH(rayPos, rayDir, node.BoundsMin, node.BoundsMax); @@ -82,9 +79,9 @@ bool RayCastBVH(BVHBuffers bvh, float3 rayPos, float3 rayDir, out BVHHit hit, fl for (uint i = indexStart; i < indexEnd;) { // Load triangle - float3 v0 = LoadVertexBVH(bvh, i++); - float3 v1 = LoadVertexBVH(bvh, i++); - float3 v2 = LoadVertexBVH(bvh, i++); + float3 v0 = LoadVertexBVH(BVHBuffers_Pass, i++); + float3 v1 = LoadVertexBVH(BVHBuffers_Pass, i++); + float3 v2 = LoadVertexBVH(BVHBuffers_Pass, i++); // Raytrace triangle float distance; @@ -109,7 +106,7 @@ bool RayCastBVH(BVHBuffers bvh, float3 rayPos, float3 rayDir, out BVHHit hit, fl } // Performs a query against the BVH acceleration structure to find the closest distance to a triangle from a given point. -bool PointQueryBVH(BVHBuffers bvh, float3 pos, out BVHHit hit, float maxDistance = 1000000.0f) +bool PointQueryBVH(BVHBuffers_Param, float3 pos, out BVHHit hit, float maxDistance = 1000000.0f) { hit = (BVHHit)0; hit.Distance = maxDistance; @@ -123,7 +120,7 @@ bool PointQueryBVH(BVHBuffers bvh, float3 pos, out BVHHit hit, float maxDistance LOOP while (stackCount > 0) { - BVHNode node = bvh.BVHBuffer[stack[--stackCount]]; + BVHNode node = BVHBuffer[stack[--stackCount]]; // Skip too far nodes if (PointDistanceBox(node.BoundsMin, node.BoundsMax, pos) >= hit.Distance) @@ -138,9 +135,9 @@ bool PointQueryBVH(BVHBuffers bvh, float3 pos, out BVHHit hit, float maxDistance for (uint i = indexStart; i < indexEnd;) { // Load triangle - float3 v0 = LoadVertexBVH(bvh, i++); - float3 v1 = LoadVertexBVH(bvh, i++); - float3 v2 = LoadVertexBVH(bvh, i++); + float3 v0 = LoadVertexBVH(BVHBuffers_Pass, i++); + float3 v1 = LoadVertexBVH(BVHBuffers_Pass, i++); + float3 v2 = LoadVertexBVH(BVHBuffers_Pass, i++); // Check triangle float distance = sqrt(DistancePointToTriangle2(pos, v0, v1, v2)); diff --git a/Source/Shaders/SDF.shader b/Source/Shaders/SDF.shader index b2ae928f7..77e27d8f9 100644 --- a/Source/Shaders/SDF.shader +++ b/Source/Shaders/SDF.shader @@ -77,15 +77,9 @@ void CS_RasterizeTriangles(uint3 GroupId : SV_GroupID, uint3 GroupThreadID : SV_ int3 voxelCoord = GetVoxelCoord(voxelIndex); float3 voxelPos = GetVoxelPos(voxelCoord); - BVHBuffers bvh; - bvh.BVHBuffer = BVHBuffer; - bvh.VertexBuffer = VertexBuffer; - bvh.IndexBuffer = IndexBuffer; - bvh.VertexStride = VertexStride; - // Point query to find the distance to the closest surface BVHHit hit; - PointQueryBVH(bvh, voxelPos, hit, MaxDistance); + PointQueryBVH(BVHBuffers_Init(BVHBuffer, VertexBuffer, IndexBuffer, VertexStride), voxelPos, hit, MaxDistance); float sdf = hit.Distance; // Raycast triangles around voxel to count triangle backfaces hit @@ -104,7 +98,7 @@ void CS_RasterizeTriangles(uint3 GroupId : SV_GroupID, uint3 GroupThreadID : SV_ for (uint i = 0; i < CLOSEST_CACHE_SIZE; i++) { float3 rayDir = closestDirections[i]; - if (RayCastBVH(bvh, voxelPos, rayDir, hit, MaxDistance)) + if (RayCastBVH(BVHBuffers_Init(BVHBuffer, VertexBuffer, IndexBuffer, VertexStride), voxelPos, rayDir, hit, MaxDistance)) { sdf = min(sdf, hit.Distance); if (hit.IsBackface) diff --git a/Source/Shaders/ShadowsSampling.hlsl b/Source/Shaders/ShadowsSampling.hlsl index 071b6f723..ac181732b 100644 --- a/Source/Shaders/ShadowsSampling.hlsl +++ b/Source/Shaders/ShadowsSampling.hlsl @@ -227,7 +227,8 @@ ShadowSample SampleDirectionalLightShadowCascade(LightData light, Buffer // Increase the sharpness for higher cascades to match the filter radius const float SharpnessScale[MaxNumCascades] = { 1.0f, 1.5f, 3.0f, 3.5f }; shadow.Sharpness *= SharpnessScale[cascadeIndex]; - + + result.TransmissionShadow = 1; #if defined(USE_GBUFFER_CUSTOM_DATA) // Subsurface shadowing BRANCH @@ -239,8 +240,6 @@ ShadowSample SampleDirectionalLightShadowCascade(LightData light, Buffer result.TransmissionShadow = CalculateSubsurfaceOcclusion(opacity, shadowPosition.z, shadowMapDepth); result.TransmissionShadow = PostProcessShadow(shadow, result.TransmissionShadow); } -#else - result.TransmissionShadow = 1; #endif result.SurfaceShadow = PostProcessShadow(shadow, result.SurfaceShadow); 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/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 1c978ea55..446771675 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) { @@ -534,7 +545,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); @@ -546,37 +558,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 { @@ -587,8 +574,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('\\', '/'))); } 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. ///