Merge remote-tracking branch 'origin/master' into sdl_platform

This commit is contained in:
2025-11-28 11:58:44 +02:00
28 changed files with 226 additions and 178 deletions

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d2b1dc1523cb2140db7ce5fed6e97b09d7fcebbe6cc19fca7708b5b882267040
size 4175
oid sha256:d3922811f0eb56cbb515c93cd53d80316740ea78219aa81118d2c9dee4a9d230
size 4142

View File

@@ -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 <sys/stat.h>
#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;

View File

@@ -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"));

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -422,24 +422,8 @@ bool AndroidFileSystem::getFilesFromDirectoryTop(Array<String>& 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));
}
}

View File

@@ -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<String, HeapAllocation>& 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;
}

View File

@@ -284,6 +284,6 @@ public:
/// <returns>Relative path</returns>
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);
};

View File

@@ -367,43 +367,8 @@ bool UnixFileSystem::getFilesFromDirectoryTop(Array<String>& 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));
}
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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.");

View File

@@ -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

View File

@@ -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<void(int32)> 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;

View File

@@ -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;
}
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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));

View File

@@ -16,13 +16,10 @@ struct BVHNode
int Count; // Negative for non-leaf nodes
};
struct BVHBuffers
{
StructuredBuffer<BVHNode> 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<BVHNode> 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));

View File

@@ -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)

View File

@@ -227,7 +227,8 @@ ShadowSample SampleDirectionalLightShadowCascade(LightData light, Buffer<float4>
// 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<float4>
result.TransmissionShadow = CalculateSubsurfaceOcclusion(opacity, shadowPosition.z, shadowMapDepth);
result.TransmissionShadow = PostProcessShadow(shadow, result.TransmissionShadow);
}
#else
result.TransmissionShadow = 1;
#endif
result.SurfaceShadow = PostProcessShadow(shadow, result.SurfaceShadow);

View File

@@ -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)

View File

@@ -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");
}
}

View File

@@ -142,7 +142,7 @@ namespace Flax.Build
}
/// <summary>
/// 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).
/// </summary>
public virtual bool UseSeparateMainExecutable(BuildOptions buildOptions)
{

View File

@@ -320,6 +320,17 @@ namespace Flax.Build.Platforms
{
}
/// <summary>
/// Gets linker argument to reference a specific shared library file.
/// </summary>
/// <param name="graph">The graph.</param>
/// <param name="options">The options.</param>
/// <param name="library">The shared library file path.</param>
protected virtual string GetSharedLibraryLinkArg(TaskGraph graph, BuildOptions options, string library)
{
return string.Format("\"-l{0}\"", GetLibName(library));
}
/// <inheritdoc />
public override CompileOutput CompileCppFiles(TaskGraph graph, BuildOptions options, List<string> sourceFiles, string outputPath)
{
@@ -534,7 +545,8 @@ namespace Flax.Build.Platforms
// Input libraries
var libraryPaths = new HashSet<string>();
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('\\', '/')));
}

View File

@@ -144,24 +144,51 @@ namespace Flax.Build
return new TwoWayEnumerator<T>(source.GetEnumerator());
}
/// <summary>
/// File copy modes.
/// </summary>
public enum CopyMode
{
/// <summary>
/// Copies the file to the destination, fails if it already exists.
/// </summary>
New,
/// <summary>
/// If destination file exists, it will be overriden.
/// </summary>
OverrideIfExists,
/// <summary>
/// If destination file exists, has the same size and is newer than source file, it won't be overriden (avoids unnecessary copies).
/// </summary>
OverrideIfNewer,
}
/// <summary>
/// Copies the file.
/// </summary>
/// <param name="srcFilePath">The source file path.</param>
/// <param name="dstFilePath">The destination file path.</param>
/// <param name="overwrite"><see langword="true" /> if the destination file can be overwritten; otherwise, <see langword="false" />.</param>
public static void FileCopy(string srcFilePath, string dstFilePath, bool overwrite = true)
/// <param name="mode">Copy operation modes.</param>
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
}
}
/// <summary>
/// Copies the file.
/// </summary>
/// <param name="srcFilePath">The source file path.</param>
/// <param name="dstFilePath">The destination file path.</param>
/// <param name="overwrite"><see langword="true" /> if the destination file can be overwritten; otherwise, <see langword="false" />.</param>
public static void FileCopy(string srcFilePath, string dstFilePath, bool overwrite)
{
FileCopy(srcFilePath, dstFilePath, overwrite ? CopyMode.OverrideIfExists : CopyMode.New);
}
/// <summary>
/// Copies the directories.
/// </summary>