Compare commits
83 Commits
0ac3ab2329
...
master_fix
| Author | SHA1 | Date | |
|---|---|---|---|
| 824ff7050e | |||
| 042843fe42 | |||
| 8631b389c1 | |||
| e3f5af530b | |||
|
|
e257f9e4a0 | ||
|
|
056de752ed | ||
|
|
76700c0b24 | ||
|
|
9fdcff657d | ||
|
|
2b6339c05c | ||
|
|
bb91202439 | ||
|
|
f25e9f262a | ||
|
|
ee51077f49 | ||
|
|
950e958a58 | ||
|
|
5fdbed2b56 | ||
|
|
0e627577fc | ||
|
|
4846d4b024 | ||
|
|
5e5293bf7b | ||
|
|
d88477dcae | ||
|
|
bd58bd91b4 | ||
|
|
7ce0d88bdc | ||
|
|
98bb2d40d6 | ||
|
|
f4bc620bbd | ||
|
|
0313bf32c9 | ||
|
|
0c887cd29e | ||
|
|
5bd9bce634 | ||
|
|
2a53d0a462 | ||
| 82bd915274 | |||
|
|
71391cf1cc | ||
|
|
b5286af526 | ||
|
|
9f07a2a54e | ||
|
|
c39c642b60 | ||
|
|
02cff3973a | ||
|
|
a63b97d31d | ||
|
|
ca52122656 | ||
|
|
20a7fcf6a0 | ||
|
|
43665aa7eb | ||
|
|
3b9b49950c | ||
|
|
0a8752ec0a | ||
|
|
47685dc2be | ||
|
|
517ee5bb25 | ||
|
|
3ab01d3576 | ||
|
|
31b6d4d658 | ||
|
|
08f840d642 | ||
|
|
776b6259cd | ||
|
|
5c81c71116 | ||
|
|
188b635ea0 | ||
|
|
56066a3212 | ||
|
|
ed50ce9c90 | ||
|
|
a7e77f6e21 | ||
|
|
56278b17ee | ||
|
|
bd78db72b9 | ||
|
|
32bd72fecd | ||
|
|
3a798a70fa | ||
|
|
02429266b1 | ||
|
|
77aea0c69c | ||
|
|
6a3ce862cb | ||
|
|
93217da619 | ||
|
|
63def54dad | ||
|
|
00f9a28729 | ||
|
|
56beca0db4 | ||
|
|
64cd898a65 | ||
|
|
90472a4b31 | ||
|
|
a1999183f2 | ||
|
|
1e3ce48024 | ||
|
|
0007185b5f | ||
|
|
403d2cedc0 | ||
|
|
c8839b8587 | ||
|
|
cf048c9804 | ||
|
|
bea75f51bd | ||
|
|
1bf6612002 | ||
|
|
d9a18b1d31 | ||
|
|
465f30661f | ||
|
|
a62ca5452e | ||
|
|
92254eefcc | ||
|
|
2d56411e5f | ||
|
|
f8dc8ab903 | ||
|
|
2a55cda583 | ||
|
|
7c91c03adf | ||
|
|
caa902ea9b | ||
|
|
fb07071e24 | ||
|
|
a1cb7dcbe7 | ||
|
|
032f698c7b | ||
|
|
e2aaef9b88 |
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d2b1dc1523cb2140db7ce5fed6e97b09d7fcebbe6cc19fca7708b5b882267040
|
||||
size 4175
|
||||
oid sha256:d3922811f0eb56cbb515c93cd53d80316740ea78219aa81118d2c9dee4a9d230
|
||||
size 4142
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"Major": 1,
|
||||
"Minor": 11,
|
||||
"Revision": 0,
|
||||
"Build": 6804
|
||||
"Build": 6805
|
||||
},
|
||||
"Company": "Flax",
|
||||
"Copyright": "Copyright (c) 2012-2025 Wojciech Figat. All rights reserved.",
|
||||
|
||||
@@ -281,6 +281,13 @@ namespace FlaxEditor.Content
|
||||
|
||||
private void CacheData()
|
||||
{
|
||||
if (!_asset)
|
||||
{
|
||||
_parameters = Utils.GetEmptyArray<ScriptMemberInfo>();
|
||||
_methods = Utils.GetEmptyArray<ScriptMemberInfo>();
|
||||
_attributes = Utils.GetEmptyArray<Attribute>();
|
||||
return;
|
||||
}
|
||||
if (_parameters != null)
|
||||
return;
|
||||
if (_asset.WaitForLoaded())
|
||||
@@ -344,13 +351,13 @@ namespace FlaxEditor.Content
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => Path.GetFileNameWithoutExtension(_asset.Path);
|
||||
public string Name => _asset ? Path.GetFileNameWithoutExtension(_asset.Path) : null;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Namespace => string.Empty;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string TypeName => JsonSerializer.GetStringID(_asset.ID);
|
||||
public string TypeName => _asset ? JsonSerializer.GetStringID(_asset.ID) : null;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsPublic => true;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1368,7 +1368,10 @@ bool CookAssetsStep::Perform(CookingData& data)
|
||||
{
|
||||
typeName = e.TypeName;
|
||||
}
|
||||
LOG(Info, "{0}: {1:>4} assets of total size {2}", typeName, e.Count, Utilities::BytesToText(e.ContentSize));
|
||||
if (e.Count == 1)
|
||||
LOG(Info, "{0}: 1 asset of total size {1}", typeName, Utilities::BytesToText(e.ContentSize));
|
||||
else
|
||||
LOG(Info, "{0}: {1:>4} assets of total size {2}", typeName, e.Count, Utilities::BytesToText(e.ContentSize));
|
||||
}
|
||||
LOG(Info, "");
|
||||
}
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -896,9 +896,11 @@ namespace FlaxEditor.Modules
|
||||
|
||||
if (type.IsAssignableTo(typeof(AssetEditorWindow)))
|
||||
{
|
||||
var ctor = type.GetConstructor(new Type[] { typeof(Editor), typeof(AssetItem) });
|
||||
var assetItem = Editor.ContentDatabase.FindAsset(winData.AssetItemID);
|
||||
var assetType = assetItem.GetType();
|
||||
var ctor = type.GetConstructor(new Type[] { typeof(Editor), assetType });
|
||||
var win = (AssetEditorWindow)ctor.Invoke(new object[] { Editor.Instance, assetItem });
|
||||
|
||||
win.Show(winData.DockState, winData.DockState != DockState.Float ? winData.DockedTo : null, winData.SelectOnShow, winData.SplitterValue);
|
||||
if (winData.DockState == DockState.Float)
|
||||
{
|
||||
|
||||
@@ -406,6 +406,8 @@ namespace FlaxEngine.Utilities
|
||||
{
|
||||
if (type == ScriptType.Null)
|
||||
return null;
|
||||
if (type.BaseType == null)
|
||||
return type.Type;
|
||||
while (type.Type == null)
|
||||
type = type.BaseType;
|
||||
return type.Type;
|
||||
|
||||
@@ -400,7 +400,7 @@ namespace FlaxEditor.Surface
|
||||
return scriptType.GetGenericTypeDefinition() == typeof(Dictionary<,>);
|
||||
}
|
||||
var managedType = TypeUtils.GetType(scriptType);
|
||||
return !TypeUtils.IsDelegate(managedType);
|
||||
return managedType != null && !TypeUtils.IsDelegate(managedType);
|
||||
}
|
||||
|
||||
internal static bool IsValidVisualScriptFunctionType(ScriptType scriptType)
|
||||
@@ -408,7 +408,7 @@ namespace FlaxEditor.Surface
|
||||
if (scriptType.IsGenericType || scriptType.IsStatic || !scriptType.IsPublic || scriptType.HasAttribute(typeof(HideInEditorAttribute), true))
|
||||
return false;
|
||||
var managedType = TypeUtils.GetType(scriptType);
|
||||
return !TypeUtils.IsDelegate(managedType);
|
||||
return managedType != null && !TypeUtils.IsDelegate(managedType);
|
||||
}
|
||||
|
||||
internal static string GetVisualScriptTypeDescription(ScriptType type)
|
||||
|
||||
@@ -2441,10 +2441,14 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
{
|
||||
if (bucket.LoopsLeft == 0)
|
||||
{
|
||||
// End playing animation
|
||||
// End playing animation and reset bucket params
|
||||
value = tryGetValue(node->GetBox(1), Value::Null);
|
||||
bucket.Index = -1;
|
||||
slot.Animation = nullptr;
|
||||
bucket.TimePosition = 0.0f;
|
||||
bucket.BlendInPosition = 0.0f;
|
||||
bucket.BlendOutPosition = 0.0f;
|
||||
bucket.LoopsDone = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2553,9 +2557,15 @@ void AnimGraphExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value& va
|
||||
// Function Input
|
||||
case 1:
|
||||
{
|
||||
// Skip when graph is too small (eg. preview) and fallback with default value from the function graph
|
||||
if (context.GraphStack.Count() < 2)
|
||||
{
|
||||
value = tryGetValue(node->TryGetBox(1), Value::Zero);
|
||||
break;
|
||||
}
|
||||
|
||||
// Find the function call
|
||||
AnimGraphNode* functionCallNode = nullptr;
|
||||
ASSERT(context.GraphStack.Count() >= 2);
|
||||
Graph* graph;
|
||||
for (int32 i = context.CallStack.Count() - 1; i >= 0; i--)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -658,7 +658,10 @@ public:
|
||||
--_count;
|
||||
T* data = _allocation.Get();
|
||||
if (index < _count)
|
||||
Memory::MoveAssignItems(data + index, data + (index + 1), _count - index);
|
||||
{
|
||||
for (int32 i = index; i < _count; i++)
|
||||
data[i] = MoveTemp(data[i + 1]);
|
||||
}
|
||||
Memory::DestructItems(data + _count, 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -409,27 +409,36 @@ protected:
|
||||
else
|
||||
{
|
||||
// Rebuild entire table completely
|
||||
const int32 elementsCount = _elementsCount;
|
||||
const int32 oldSize = _size;
|
||||
AllocationData oldAllocation;
|
||||
AllocationUtils::MoveToEmpty<BucketType, AllocationType>(oldAllocation, _allocation, _size, _size);
|
||||
AllocationUtils::MoveToEmpty<BucketType, AllocationType>(oldAllocation, _allocation, oldSize, oldSize);
|
||||
_allocation.Allocate(_size);
|
||||
BucketType* data = _allocation.Get();
|
||||
for (int32 i = 0; i < _size; ++i)
|
||||
for (int32 i = 0; i < oldSize; ++i)
|
||||
data[i]._state = HashSetBucketState::Empty;
|
||||
BucketType* oldData = oldAllocation.Get();
|
||||
FindPositionResult pos;
|
||||
for (int32 i = 0; i < _size; ++i)
|
||||
for (int32 i = 0; i < oldSize; ++i)
|
||||
{
|
||||
BucketType& oldBucket = oldData[i];
|
||||
if (oldBucket.IsOccupied())
|
||||
{
|
||||
FindPosition(oldBucket.GetKey(), pos);
|
||||
ASSERT(pos.FreeSlotIndex != -1);
|
||||
if (pos.FreeSlotIndex == -1)
|
||||
{
|
||||
// Grow and retry to handle pathological cases (eg. heavy collisions)
|
||||
EnsureCapacity(_size + 1, true);
|
||||
FindPosition(oldBucket.GetKey(), pos);
|
||||
ASSERT(pos.FreeSlotIndex != -1);
|
||||
}
|
||||
BucketType& bucket = _allocation.Get()[pos.FreeSlotIndex];
|
||||
bucket = MoveTemp(oldBucket);
|
||||
}
|
||||
}
|
||||
for (int32 i = 0; i < _size; ++i)
|
||||
for (int32 i = 0; i < oldSize; ++i)
|
||||
oldData[i].Free();
|
||||
_elementsCount = elementsCount;
|
||||
}
|
||||
_deletedCount = 0;
|
||||
}
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
|
||||
#if defined(__clang__)
|
||||
|
||||
#define DLLEXPORT __attribute__ ((__visibility__ ("default")))
|
||||
#define DLLEXPORT __attribute__((__visibility__("default")))
|
||||
#define DLLIMPORT
|
||||
#define USED __attribute__((used))
|
||||
#define THREADLOCAL __thread
|
||||
#define STDCALL __attribute__((stdcall))
|
||||
#define CDECL __attribute__((cdecl))
|
||||
@@ -19,7 +20,7 @@
|
||||
#define PACK_BEGIN()
|
||||
#define PACK_END() __attribute__((__packed__))
|
||||
#define ALIGN_BEGIN(_align)
|
||||
#define ALIGN_END(_align) __attribute__( (aligned(_align) ) )
|
||||
#define ALIGN_END(_align) __attribute__((aligned(_align)))
|
||||
#define OFFSET_OF(X, Y) __builtin_offsetof(X, Y)
|
||||
#define PRAGMA_DISABLE_DEPRECATION_WARNINGS \
|
||||
_Pragma("clang diagnostic push") \
|
||||
@@ -37,8 +38,9 @@
|
||||
|
||||
#elif defined(__GNUC__)
|
||||
|
||||
#define DLLEXPORT __attribute__ ((__visibility__ ("default")))
|
||||
#define DLLEXPORT __attribute__((__visibility__("default")))
|
||||
#define DLLIMPORT
|
||||
#define USED __attribute__((used))
|
||||
#define THREADLOCAL __thread
|
||||
#define STDCALL __attribute__((stdcall))
|
||||
#define CDECL __attribute__((cdecl))
|
||||
@@ -52,7 +54,7 @@
|
||||
#define PACK_BEGIN()
|
||||
#define PACK_END() __attribute__((__packed__))
|
||||
#define ALIGN_BEGIN(_align)
|
||||
#define ALIGN_END(_align) __attribute__( (aligned(_align) ) )
|
||||
#define ALIGN_END(_align) __attribute__((aligned(_align)))
|
||||
#define OFFSET_OF(X, Y) __builtin_offsetof(X, Y)
|
||||
#define PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
||||
#define PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
||||
@@ -67,6 +69,7 @@
|
||||
|
||||
#define DLLEXPORT __declspec(dllexport)
|
||||
#define DLLIMPORT __declspec(dllimport)
|
||||
#define USED
|
||||
#define THREADLOCAL __declspec(thread)
|
||||
#define STDCALL __stdcall
|
||||
#define CDECL __cdecl
|
||||
|
||||
@@ -18,9 +18,7 @@ namespace AllocationUtils
|
||||
capacity |= capacity >> 8;
|
||||
capacity |= capacity >> 16;
|
||||
uint64 capacity64 = (uint64)(capacity + 1) * 2;
|
||||
if (capacity64 > MAX_int32)
|
||||
capacity64 = MAX_int32;
|
||||
return (int32)capacity64;
|
||||
return capacity64 >= MAX_int32 ? MAX_int32 : (int32)capacity64 / 2;
|
||||
}
|
||||
|
||||
// Aligns the input value to the next power of 2 to be used as bigger memory allocation block.
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ void GPUContext::FrameBegin()
|
||||
|
||||
void GPUContext::FrameEnd()
|
||||
{
|
||||
ClearState();
|
||||
ResetState();
|
||||
FlushState();
|
||||
}
|
||||
|
||||
|
||||
@@ -189,7 +189,7 @@ public:
|
||||
/// [Deprecated in v1.10]
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> if depth buffer is binded; otherwise, <c>false</c>.</returns>
|
||||
DEPRECATED("IsDepthBufferBinded has been deprecated and will be removed in ")
|
||||
DEPRECATED("IsDepthBufferBinded has been deprecated and will be removed in future")
|
||||
virtual bool IsDepthBufferBinded() = 0;
|
||||
|
||||
public:
|
||||
@@ -617,8 +617,17 @@ public:
|
||||
|
||||
/// <summary>
|
||||
/// Clears the context state.
|
||||
/// [Deprecated in v1.12]
|
||||
/// </summary>
|
||||
API_FUNCTION() virtual void ClearState() = 0;
|
||||
API_FUNCTION() DEPRECATED("Use ResetState instead") void ClearState()
|
||||
{
|
||||
ResetState();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the context state.
|
||||
/// </summary>
|
||||
API_FUNCTION() virtual void ResetState() = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Flushes the internal cached context state with a command buffer.
|
||||
|
||||
@@ -201,6 +201,7 @@ bool DeferredMaterialShader::Load()
|
||||
psDesc.DepthWriteEnable = true;
|
||||
psDesc.DepthEnable = true;
|
||||
psDesc.DepthFunc = ComparisonFunc::Less;
|
||||
psDesc.BlendMode.RenderTargetWriteMask = BlendingMode::ColorWrite::None;
|
||||
psDesc.HS = nullptr;
|
||||
psDesc.DS = nullptr;
|
||||
GPUShaderProgramVS* instancedDepthPassVS;
|
||||
|
||||
@@ -195,5 +195,10 @@ bool ForwardMaterialShader::Load()
|
||||
psDesc.VS = _shader->GetVS("VS_Skinned");
|
||||
_cache.DepthSkinned.Init(psDesc);
|
||||
|
||||
#if PLATFORM_PS5
|
||||
// Fix shader binding issues on forward shading materials on PS5
|
||||
_drawModes = DrawPass::None;
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -264,5 +264,10 @@ bool ParticleMaterialShader::Load()
|
||||
// Lazy initialization
|
||||
_cacheVolumetricFog.Desc.PS = nullptr;
|
||||
|
||||
#if PLATFORM_PS5
|
||||
// Fix shader binding issues on forward shading materials on PS5
|
||||
_drawModes = DrawPass::None;
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -113,7 +113,8 @@ GPUTexture* RenderBuffers::RequestHalfResDepth(GPUContext* context)
|
||||
|
||||
PixelFormat RenderBuffers::GetOutputFormat() const
|
||||
{
|
||||
return _useAlpha ? PixelFormat::R16G16B16A16_Float : PixelFormat::R11G11B10_Float;
|
||||
// TODO: fix incorrect alpha leaking into reflections on PS5 with R11G11B10_Float
|
||||
return _useAlpha || PLATFORM_PS5 ? PixelFormat::R16G16B16A16_Float : PixelFormat::R11G11B10_Float;
|
||||
}
|
||||
|
||||
bool RenderBuffers::GetUseAlpha() const
|
||||
|
||||
@@ -450,7 +450,7 @@ public:
|
||||
/// <summary>
|
||||
/// The high-level renderer context. Used to collect the draw calls for the scene rendering. Can be used to perform a custom rendering.
|
||||
/// </summary>
|
||||
API_STRUCT(NoDefault) struct RenderContext
|
||||
API_STRUCT(NoDefault) struct FLAXENGINE_API RenderContext
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(RenderContext);
|
||||
|
||||
@@ -491,7 +491,7 @@ API_STRUCT(NoDefault) struct RenderContext
|
||||
/// <summary>
|
||||
/// The high-level renderer context batch that encapsulates multiple rendering requests within a single task (eg. optimize main view scene rendering and shadow projections at once).
|
||||
/// </summary>
|
||||
API_STRUCT(NoDefault) struct RenderContextBatch
|
||||
API_STRUCT(NoDefault) struct FLAXENGINE_API RenderContextBatch
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(RenderContextBatch);
|
||||
|
||||
|
||||
@@ -216,20 +216,21 @@ GPUVertexLayout* GPUVertexLayout::Get(const Span<GPUVertexLayout*>& layouts)
|
||||
return result;
|
||||
}
|
||||
|
||||
GPUVertexLayout* GPUVertexLayout::Merge(GPUVertexLayout* base, GPUVertexLayout* reference, bool removeUnused, bool addMissing, int32 missingSlotOverride)
|
||||
GPUVertexLayout* GPUVertexLayout::Merge(GPUVertexLayout* base, GPUVertexLayout* reference, bool removeUnused, bool addMissing, int32 missingSlotOverride, bool referenceOrder)
|
||||
{
|
||||
GPUVertexLayout* result = base ? base : reference;
|
||||
if (base && reference && base != reference)
|
||||
{
|
||||
bool elementsModified = false;
|
||||
Elements newElements = base->GetElements();
|
||||
const Elements& refElements = reference->GetElements();
|
||||
if (removeUnused)
|
||||
{
|
||||
for (int32 i = newElements.Count() - 1; i >= 0; i--)
|
||||
{
|
||||
bool missing = true;
|
||||
const VertexElement& e = newElements.Get()[i];
|
||||
for (const VertexElement& ee : reference->GetElements())
|
||||
for (const VertexElement& ee : refElements)
|
||||
{
|
||||
if (ee.Type == e.Type)
|
||||
{
|
||||
@@ -247,7 +248,7 @@ GPUVertexLayout* GPUVertexLayout::Merge(GPUVertexLayout* base, GPUVertexLayout*
|
||||
}
|
||||
if (addMissing)
|
||||
{
|
||||
for (const VertexElement& e : reference->GetElements())
|
||||
for (const VertexElement& e : refElements)
|
||||
{
|
||||
bool missing = true;
|
||||
for (const VertexElement& ee : base->GetElements())
|
||||
@@ -282,6 +283,32 @@ GPUVertexLayout* GPUVertexLayout::Merge(GPUVertexLayout* base, GPUVertexLayout*
|
||||
}
|
||||
}
|
||||
}
|
||||
if (referenceOrder)
|
||||
{
|
||||
for (int32 i = 0, j = 0; i < newElements.Count() && j < refElements.Count(); j++)
|
||||
{
|
||||
if (newElements[i].Type == refElements[j].Type)
|
||||
{
|
||||
// Elements match so move forward
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find reference element in a new list
|
||||
for (int32 k = i + 1; k < newElements.Count(); k++)
|
||||
{
|
||||
if (newElements[k].Type == refElements[j].Type)
|
||||
{
|
||||
// Move matching element to the reference position
|
||||
VertexElement e = newElements[k];
|
||||
newElements.RemoveAt(k);
|
||||
newElements.Insert(i, e);
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (elementsModified)
|
||||
result = Get(newElements, true);
|
||||
}
|
||||
|
||||
@@ -84,8 +84,9 @@ public:
|
||||
/// <param name="removeUnused">True to remove elements from base layout that don't exist in a reference layout.</param>
|
||||
/// <param name="addMissing">True to add missing elements to base layout that exist in a reference layout.</param>
|
||||
/// <param name="missingSlotOverride">Allows to override the input slot for missing elements. Use value -1 to inherit slot from the reference layout.</param>
|
||||
/// <param name="referenceOrder">True to reorder result elements to match the reference layout. For example, if input vertex buffer layout is different than vertex shader then it can match those.</param>
|
||||
/// <returns>Vertex layout object. Doesn't need to be cleared as it's cached for an application lifetime.</returns>
|
||||
static GPUVertexLayout* Merge(GPUVertexLayout* base, GPUVertexLayout* reference, bool removeUnused = false, bool addMissing = true, int32 missingSlotOverride = -1);
|
||||
static GPUVertexLayout* Merge(GPUVertexLayout* base, GPUVertexLayout* reference, bool removeUnused = false, bool addMissing = true, int32 missingSlotOverride = -1, bool referenceOrder = false);
|
||||
|
||||
public:
|
||||
// [GPUResource]
|
||||
|
||||
@@ -724,7 +724,7 @@ void GPUContextDX11::SetState(GPUPipelineState* state)
|
||||
}
|
||||
}
|
||||
|
||||
void GPUContextDX11::ClearState()
|
||||
void GPUContextDX11::ResetState()
|
||||
{
|
||||
if (!_context)
|
||||
return;
|
||||
|
||||
@@ -158,7 +158,7 @@ public:
|
||||
void SetScissor(const Rectangle& scissorRect) override;
|
||||
GPUPipelineState* GetState() const override;
|
||||
void SetState(GPUPipelineState* state) override;
|
||||
void ClearState() override;
|
||||
void ResetState() override;
|
||||
void FlushState() override;
|
||||
void Flush() override;
|
||||
void UpdateBuffer(GPUBuffer* buffer, const void* data, uint32 size, uint32 offset) override;
|
||||
|
||||
@@ -143,6 +143,8 @@ void CommandQueueDX12::WaitForFence(uint64 fenceValue)
|
||||
|
||||
void CommandQueueDX12::WaitForGPU()
|
||||
{
|
||||
PROFILE_CPU();
|
||||
ZoneColor(TracyWaitZoneColor);
|
||||
const uint64 value = _fence.Signal(this);
|
||||
_fence.WaitCPU(value);
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ bool GPUBufferDX12::OnInit()
|
||||
// Create resource
|
||||
ID3D12Resource* resource;
|
||||
#if PLATFORM_WINDOWS
|
||||
D3D12_HEAP_FLAGS heapFlags = D3D12_HEAP_FLAG_CREATE_NOT_ZEROED;
|
||||
D3D12_HEAP_FLAGS heapFlags = EnumHasAnyFlags(_desc.Flags, GPUBufferFlags::VertexBuffer | GPUBufferFlags::IndexBuffer) || _desc.InitData ? D3D12_HEAP_FLAG_CREATE_NOT_ZEROED : D3D12_HEAP_FLAG_NONE;
|
||||
#else
|
||||
D3D12_HEAP_FLAGS heapFlags = D3D12_HEAP_FLAG_NONE;
|
||||
#endif
|
||||
|
||||
@@ -1304,7 +1304,7 @@ void GPUContextDX12::SetState(GPUPipelineState* state)
|
||||
}
|
||||
}
|
||||
|
||||
void GPUContextDX12::ClearState()
|
||||
void GPUContextDX12::ResetState()
|
||||
{
|
||||
if (!_commandList)
|
||||
return;
|
||||
|
||||
@@ -201,7 +201,7 @@ public:
|
||||
void SetScissor(const Rectangle& scissorRect) override;
|
||||
GPUPipelineState* GetState() const override;
|
||||
void SetState(GPUPipelineState* state) override;
|
||||
void ClearState() override;
|
||||
void ResetState() override;
|
||||
void FlushState() override;
|
||||
void Flush() override;
|
||||
void UpdateBuffer(GPUBuffer* buffer, const void* data, uint32 size, uint32 offset) override;
|
||||
|
||||
@@ -628,6 +628,7 @@ bool GPUDeviceDX12::Init()
|
||||
VALIDATE_DIRECTX_CALL(dxgiAdapter->EnumOutputs(0, dxgiOutput.GetAddressOf()));
|
||||
DXGI_FORMAT backbufferFormat = RenderToolsDX::ToDxgiFormat(GPU_BACK_BUFFER_PIXEL_FORMAT);
|
||||
UINT modesCount = 0;
|
||||
#ifdef _GAMING_XBOX_SCARLETT
|
||||
VALIDATE_DIRECTX_CALL(dxgiOutput->GetDisplayModeList(backbufferFormat, 0, &modesCount, NULL));
|
||||
Array<DXGIXBOX_MODE_DESC> modes;
|
||||
modes.Resize((int32)modesCount);
|
||||
@@ -642,6 +643,11 @@ bool GPUDeviceDX12::Init()
|
||||
videoOutput.RefreshRate = Math::Max(videoOutput.RefreshRate, mode.RefreshRate.Numerator / (float)mode.RefreshRate.Denominator);
|
||||
}
|
||||
modes.Resize(0);
|
||||
#else
|
||||
videoOutput.Width = 1920;
|
||||
videoOutput.Height = 1080;
|
||||
videoOutput.RefreshRate = 60;
|
||||
#endif
|
||||
|
||||
#if PLATFORM_GDK
|
||||
GDKPlatform::Suspended.Bind<GPUDeviceDX12, &GPUDeviceDX12::OnSuspended>(this);
|
||||
|
||||
@@ -159,7 +159,7 @@ bool GPUTextureDX12::OnInit()
|
||||
initialState = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
|
||||
|
||||
// Create texture
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_WINDOWS && 0
|
||||
D3D12_HEAP_FLAGS heapFlags = useRTV || useDSV ? D3D12_HEAP_FLAG_CREATE_NOT_ZEROED : D3D12_HEAP_FLAG_NONE;
|
||||
#else
|
||||
D3D12_HEAP_FLAGS heapFlags = D3D12_HEAP_FLAG_NONE;
|
||||
|
||||
@@ -177,7 +177,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
void ClearState() override
|
||||
void ResetState() override
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -1329,7 +1329,7 @@ void GPUContextVulkan::SetState(GPUPipelineState* state)
|
||||
}
|
||||
}
|
||||
|
||||
void GPUContextVulkan::ClearState()
|
||||
void GPUContextVulkan::ResetState()
|
||||
{
|
||||
ResetRenderTarget();
|
||||
ResetSR();
|
||||
|
||||
@@ -193,7 +193,7 @@ public:
|
||||
void SetScissor(const Rectangle& scissorRect) override;
|
||||
GPUPipelineState* GetState() const override;
|
||||
void SetState(GPUPipelineState* state) override;
|
||||
void ClearState() override;
|
||||
void ResetState() override;
|
||||
void FlushState() override;
|
||||
void Flush() override;
|
||||
void UpdateBuffer(GPUBuffer* buffer, const void* data, uint32 size, uint32 offset) override;
|
||||
|
||||
@@ -554,10 +554,11 @@ void AnimatedModel::StopSlotAnimation(const StringView& slotName, Animation* ani
|
||||
{
|
||||
for (auto& slot : GraphInstance.Slots)
|
||||
{
|
||||
if (slot.Animation == anim && slot.Name == slotName)
|
||||
if ((slot.Animation == anim || anim == nullptr) && slot.Name == slotName)
|
||||
{
|
||||
//slot.Animation = nullptr; // TODO: make an immediate version of this method and set the animation to nullptr.
|
||||
slot.Reset = true;
|
||||
if (slot.Animation != nullptr)
|
||||
slot.Reset = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -573,7 +574,7 @@ void AnimatedModel::PauseSlotAnimation(const StringView& slotName, Animation* an
|
||||
{
|
||||
for (auto& slot : GraphInstance.Slots)
|
||||
{
|
||||
if (slot.Animation == anim && slot.Name == slotName)
|
||||
if ((slot.Animation == anim || anim == nullptr) && slot.Name == slotName)
|
||||
{
|
||||
slot.Pause = true;
|
||||
break;
|
||||
@@ -595,7 +596,7 @@ bool AnimatedModel::IsPlayingSlotAnimation(const StringView& slotName, Animation
|
||||
{
|
||||
for (auto& slot : GraphInstance.Slots)
|
||||
{
|
||||
if (slot.Animation == anim && slot.Name == slotName && !slot.Pause)
|
||||
if ((slot.Animation == anim || anim == nullptr) && slot.Name == slotName && !slot.Pause)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -412,8 +412,8 @@ public:
|
||||
/// Stops the animation playback on the slot in Anim Graph.
|
||||
/// </summary>
|
||||
/// <param name="slotName">The name of the slot.</param>
|
||||
/// <param name="anim">The animation to stop.</param>
|
||||
API_FUNCTION() void StopSlotAnimation(const StringView& slotName, Animation* anim);
|
||||
/// <param name="anim">The animation to check. Null to use slot name only.</param>
|
||||
API_FUNCTION() void StopSlotAnimation(const StringView& slotName, Animation* anim = nullptr);
|
||||
|
||||
/// <summary>
|
||||
/// Pauses all the animations playback on the all slots in Anim Graph.
|
||||
@@ -424,8 +424,8 @@ public:
|
||||
/// Pauses the animation playback on the slot in Anim Graph.
|
||||
/// </summary>
|
||||
/// <param name="slotName">The name of the slot.</param>
|
||||
/// <param name="anim">The animation to pause.</param>
|
||||
API_FUNCTION() void PauseSlotAnimation(const StringView& slotName, Animation* anim);
|
||||
/// <param name="anim">The animation to check. Null to use slot name only.</param>
|
||||
API_FUNCTION() void PauseSlotAnimation(const StringView& slotName, Animation* anim = nullptr);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if any animation playback is active on any slot in Anim Graph (not paused).
|
||||
@@ -436,8 +436,8 @@ public:
|
||||
/// Checks if the animation playback is active on the slot in Anim Graph (not paused).
|
||||
/// </summary>
|
||||
/// <param name="slotName">The name of the slot.</param>
|
||||
/// <param name="anim">The animation to check.</param>
|
||||
API_FUNCTION() bool IsPlayingSlotAnimation(const StringView& slotName, Animation* anim);
|
||||
/// <param name="anim">The animation to check. Null to use slot name only.</param>
|
||||
API_FUNCTION() bool IsPlayingSlotAnimation(const StringView& slotName, Animation* anim = nullptr);
|
||||
|
||||
private:
|
||||
void ApplyRootMotion(const Transform& rootMotionDelta);
|
||||
|
||||
@@ -16,7 +16,7 @@ const Char* GetCommandLine(int argc, char* argv[])
|
||||
const Char* cmdLine;
|
||||
if (length != 0)
|
||||
{
|
||||
Char* str = (Char*)malloc(length * sizeof(Char));
|
||||
Char* str = (Char*)malloc((length + 1) * sizeof(Char));
|
||||
cmdLine = str;
|
||||
for (int i = 1; i < argc; i++)
|
||||
{
|
||||
|
||||
@@ -100,6 +100,35 @@ API_STRUCT(NoDefault, Namespace="FlaxEngine.Networking") struct FLAXENGINE_API N
|
||||
return Word0 + Word1 != 0;
|
||||
}
|
||||
|
||||
NetworkClientsMask operator&(const NetworkClientsMask& other) const
|
||||
{
|
||||
return { Word0 & other.Word0, Word1 & other.Word1 };
|
||||
}
|
||||
|
||||
NetworkClientsMask operator|(const NetworkClientsMask& other) const
|
||||
{
|
||||
return { Word0 | other.Word0, Word1 | other.Word1 };
|
||||
}
|
||||
|
||||
NetworkClientsMask operator~() const
|
||||
{
|
||||
return { ~Word0, ~Word1 };
|
||||
}
|
||||
|
||||
NetworkClientsMask& operator|=(const NetworkClientsMask& other)
|
||||
{
|
||||
Word0 |= other.Word0;
|
||||
Word1 |= other.Word1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NetworkClientsMask& operator&=(const NetworkClientsMask& other)
|
||||
{
|
||||
Word0 &= other.Word0;
|
||||
Word1 &= other.Word1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const NetworkClientsMask& other) const
|
||||
{
|
||||
return Word0 == other.Word0 && Word1 == other.Word1;
|
||||
|
||||
@@ -116,17 +116,6 @@ PACK_STRUCT(struct NetworkMessageObjectRpc
|
||||
|
||||
struct NetworkReplicatedObject
|
||||
{
|
||||
ScriptingObjectReference<ScriptingObject> Object;
|
||||
Guid ObjectId;
|
||||
Guid ParentId;
|
||||
uint32 OwnerClientId;
|
||||
uint32 LastOwnerFrame = 0;
|
||||
NetworkObjectRole Role;
|
||||
uint8 Spawned : 1;
|
||||
uint8 Synced : 1;
|
||||
DataContainer<uint32> TargetClientIds;
|
||||
INetworkObject* AsNetworkObject;
|
||||
|
||||
struct
|
||||
{
|
||||
NetworkClientsMask Mask;
|
||||
@@ -139,6 +128,17 @@ struct NetworkReplicatedObject
|
||||
}
|
||||
} RepCache;
|
||||
|
||||
ScriptingObjectReference<ScriptingObject> Object;
|
||||
Guid ObjectId;
|
||||
Guid ParentId;
|
||||
DataContainer<uint32> TargetClientIds;
|
||||
INetworkObject* AsNetworkObject;
|
||||
uint32 OwnerClientId;
|
||||
uint32 LastOwnerFrame = 0;
|
||||
NetworkObjectRole Role;
|
||||
uint8 Spawned : 1;
|
||||
uint8 Synced : 1;
|
||||
|
||||
NetworkReplicatedObject()
|
||||
{
|
||||
Spawned = 0;
|
||||
@@ -152,12 +152,12 @@ struct NetworkReplicatedObject
|
||||
|
||||
bool operator==(const NetworkReplicatedObject& other) const
|
||||
{
|
||||
return Object == other.Object;
|
||||
return ObjectId == other.ObjectId;
|
||||
}
|
||||
|
||||
bool operator==(const ScriptingObject* other) const
|
||||
{
|
||||
return Object == other;
|
||||
return other && ObjectId == other->GetID();
|
||||
}
|
||||
|
||||
bool operator==(const Guid& other) const
|
||||
@@ -176,6 +176,11 @@ inline uint32 GetHash(const NetworkReplicatedObject& key)
|
||||
return GetHash(key.ObjectId);
|
||||
}
|
||||
|
||||
inline uint32 GetHash(const ScriptingObject* key)
|
||||
{
|
||||
return key ? GetHash(key->GetID()) : 0;
|
||||
}
|
||||
|
||||
struct Serializer
|
||||
{
|
||||
NetworkReplicator::SerializeFunc Methods[2];
|
||||
@@ -698,14 +703,11 @@ void SendReplication(ScriptingObject* obj, NetworkClientsMask targetClients)
|
||||
return;
|
||||
auto& item = it->Item;
|
||||
const bool isClient = NetworkManager::IsClient();
|
||||
const NetworkClientsMask fullTargetClients = targetClients;
|
||||
|
||||
// Skip serialization of objects that none will receive
|
||||
if (!isClient)
|
||||
{
|
||||
BuildCachedTargets(item, targetClients);
|
||||
if (CachedTargets.Count() == 0)
|
||||
return;
|
||||
}
|
||||
// If server has no recipients, skip early.
|
||||
if (!isClient && !targetClients)
|
||||
return;
|
||||
|
||||
if (item.AsNetworkObject)
|
||||
item.AsNetworkObject->OnNetworkSerialize();
|
||||
@@ -728,18 +730,30 @@ void SendReplication(ScriptingObject* obj, NetworkClientsMask targetClients)
|
||||
|
||||
#if USE_NETWORK_REPLICATOR_CACHE
|
||||
// Process replication cache to skip sending object data if it didn't change
|
||||
if (item.RepCache.Data.Length() == size &&
|
||||
item.RepCache.Mask == targetClients &&
|
||||
Platform::MemoryCompare(item.RepCache.Data.Get(), stream->GetBuffer(), size) == 0)
|
||||
if (item.RepCache.Data.Length() == size && Platform::MemoryCompare(item.RepCache.Data.Get(), stream->GetBuffer(), size) == 0)
|
||||
{
|
||||
return;
|
||||
// Check if only newly joined clients are missing this data to avoid resending it to everyone
|
||||
NetworkClientsMask missingClients = targetClients & ~item.RepCache.Mask;
|
||||
|
||||
// If data is the same and only the client set changed, replicate to missing clients only
|
||||
if (!missingClients)
|
||||
return;
|
||||
targetClients = missingClients;
|
||||
}
|
||||
item.RepCache.Mask = targetClients;
|
||||
item.RepCache.Mask = fullTargetClients;
|
||||
item.RepCache.Data.Copy(stream->GetBuffer(), size);
|
||||
#endif
|
||||
// TODO: use Unreliable for dynamic objects that are replicated every frame? (eg. player state)
|
||||
constexpr NetworkChannelType repChannel = NetworkChannelType::Reliable;
|
||||
|
||||
// Skip serialization of objects that none will receive
|
||||
if (!isClient)
|
||||
{
|
||||
BuildCachedTargets(item, targetClients);
|
||||
if (CachedTargets.Count() == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
// Send object to clients
|
||||
NetworkMessageObjectReplicate msgData;
|
||||
msgData.OwnerFrame = NetworkManager::Frame;
|
||||
@@ -1530,7 +1544,21 @@ void NetworkReplicator::DespawnObject(ScriptingObject* obj)
|
||||
// Register for despawning (batched during update)
|
||||
auto& despawn = DespawnQueue.AddOne();
|
||||
despawn.Id = obj->GetID();
|
||||
despawn.Targets = item.TargetClientIds;
|
||||
if (item.TargetClientIds.IsValid())
|
||||
{
|
||||
despawn.Targets = item.TargetClientIds;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Snapshot current recipients to avoid sending despawn to clients that connect later (and never got the spawn)
|
||||
Array<uint32, InlinedAllocation<8>> clientIds;
|
||||
for (const NetworkClient* client : NetworkManager::Clients)
|
||||
{
|
||||
if (client->State == NetworkConnectionState::Connected && client->ClientId != item.OwnerClientId)
|
||||
clientIds.Add(client->ClientId);
|
||||
}
|
||||
despawn.Targets.Copy(clientIds);
|
||||
}
|
||||
|
||||
// Prevent spawning
|
||||
for (int32 i = 0; i < SpawnQueue.Count(); i++)
|
||||
@@ -1823,6 +1851,31 @@ void NetworkInternal::NetworkReplicatorClientConnected(NetworkClient* client)
|
||||
{
|
||||
ScopeLock lock(ObjectsLock);
|
||||
NewClients.Add(client);
|
||||
|
||||
// Ensure cached replication acknowledges the new client without resending to others.
|
||||
// Clear the new client's bit in RepCache and schedule a near-term replication.
|
||||
const int32 clientIndex = NetworkManager::Clients.Find(client);
|
||||
if (clientIndex != -1)
|
||||
{
|
||||
const uint64 bitMask = 1ull << (uint64)(clientIndex % 64);
|
||||
const int32 wordIndex = clientIndex / 64;
|
||||
for (auto it = Objects.Begin(); it.IsNotEnd(); ++it)
|
||||
{
|
||||
auto& item = it->Item;
|
||||
ScriptingObject* obj = item.Object.Get();
|
||||
if (!obj || !item.Spawned || item.Role != NetworkObjectRole::OwnedAuthoritative)
|
||||
continue;
|
||||
|
||||
// Mark this client as missing cached data
|
||||
uint64* word = wordIndex == 0 ? &item.RepCache.Mask.Word0 : &item.RepCache.Mask.Word1;
|
||||
*word &= ~bitMask;
|
||||
|
||||
// Force next replication tick for this object so the new client gets data promptly
|
||||
if (Hierarchy)
|
||||
Hierarchy->DirtyObject(obj);
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT(sizeof(NetworkClientsMask) * 8 >= (uint32)NetworkManager::Clients.Count()); // Ensure that clients mask can hold all of clients
|
||||
}
|
||||
|
||||
@@ -2275,7 +2328,9 @@ void NetworkInternal::OnNetworkMessageObjectDespawn(NetworkEvent& event, Network
|
||||
}
|
||||
else
|
||||
{
|
||||
NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Failed to despawn object {}", objectId);
|
||||
// If this client never had the object (eg. it was targeted to other clients only), drop the message quietly
|
||||
DespawnedObjects.Add(objectId);
|
||||
NETWORK_REPLICATOR_LOG(Warning, "[NetworkReplicator] Failed to despawn object {}", objectId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -482,9 +482,15 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupFunction(Box* box, Node* node,
|
||||
// Function Input
|
||||
case 1:
|
||||
{
|
||||
// Skip when graph is too small (eg. preview) and fallback with default value from the function graph
|
||||
if (context.GraphStack.Count() < 2)
|
||||
{
|
||||
value = tryGetValue(node->TryGetBox(1), Value::Zero);
|
||||
break;
|
||||
}
|
||||
|
||||
// Find the function call
|
||||
Node* functionCallNode = nullptr;
|
||||
ASSERT(context.GraphStack.Count() >= 2);
|
||||
ParticleEmitterGraphCPU* graph;
|
||||
for (int32 i = context.CallStackSize - 1; i >= 0; i--)
|
||||
{
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -51,6 +51,7 @@ Array<User*, FixedAllocation<8>> PlatformBase::Users;
|
||||
Delegate<User*> PlatformBase::UserAdded;
|
||||
Delegate<User*> PlatformBase::UserRemoved;
|
||||
void* OutOfMemoryBuffer = nullptr;
|
||||
volatile int64 FatalReporting = 0;
|
||||
|
||||
const Char* ToString(NetworkConnectionType value)
|
||||
{
|
||||
@@ -306,11 +307,20 @@ int32 PlatformBase::GetCacheLineSize()
|
||||
|
||||
void PlatformBase::Fatal(const StringView& msg, void* context, FatalErrorType error)
|
||||
{
|
||||
// Let only one thread to report the error (and wait for it to end to have valid log before crash)
|
||||
RETRY:
|
||||
if (Platform::InterlockedCompareExchange(&FatalReporting, 1, 0) != 0)
|
||||
{
|
||||
Platform::Sleep(1);
|
||||
goto RETRY;
|
||||
}
|
||||
|
||||
// Check if is already during fatal state
|
||||
if (Engine::FatalError != FatalErrorType::None)
|
||||
{
|
||||
// Just send one more error to the log and back
|
||||
LOG(Error, "Error after fatal error: {0}", msg);
|
||||
Platform::AtomicStore(&FatalReporting, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -429,6 +439,8 @@ void PlatformBase::Fatal(const StringView& msg, void* context, FatalErrorType er
|
||||
}
|
||||
#endif
|
||||
|
||||
Platform::AtomicStore(&FatalReporting, 0);
|
||||
|
||||
// Show error message
|
||||
if (Engine::ReportCrash.IsBinded())
|
||||
Engine::ReportCrash(msg, context);
|
||||
|
||||
@@ -112,7 +112,7 @@ void LinuxInput::UpdateState()
|
||||
if (time - lastUpdateTime > 1.0f)
|
||||
{
|
||||
PROFILE_CPU_NAMED("Input.ScanGamepads");
|
||||
DetectGamePads();
|
||||
//DetectGamePads();
|
||||
lastUpdateTime = time;
|
||||
for (int i = 0; i < foundGamepads; i++)
|
||||
{
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "Engine/Graphics/PixelFormatSampler.h"
|
||||
#include "Engine/Graphics/Textures/TextureData.h"
|
||||
#include "IncludeX11.h"
|
||||
#include "ThirdParty/X11/Xutil.h"
|
||||
|
||||
// ICCCM
|
||||
#define WM_NormalState 1L // window normal state
|
||||
@@ -178,6 +179,20 @@ LinuxWindow::LinuxWindow(const CreateWindowSettings& settings)
|
||||
X11::XSetTransientForHint(display, window, (X11::Window)((LinuxWindow*)settings.Parent)->GetNativePtr());
|
||||
}
|
||||
|
||||
// Provides class hint for WMs like Hyprland to hook onto and apply window rules
|
||||
X11::XClassHint* classHint = X11::XAllocClassHint();
|
||||
if (classHint)
|
||||
{
|
||||
const char* className = settings.IsRegularWindow ? "FlexEditor" : "FlaxPopup";
|
||||
|
||||
classHint->res_name = const_cast<char*>(className);
|
||||
classHint->res_class = const_cast<char*>(className);
|
||||
|
||||
X11::XSetClassHint(display, window, classHint);
|
||||
|
||||
XFree(classHint);
|
||||
}
|
||||
|
||||
_dpi = Platform::GetDpi();
|
||||
_dpiScale = (float)_dpi / (float)DefaultDPI;
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -375,6 +375,7 @@ void PostProcessingPass::Render(RenderContext& renderContext, GPUTexture* input,
|
||||
RENDER_TARGET_POOL_SET_NAME(bloomBuffer1, "PostProcessing.Bloom");
|
||||
RENDER_TARGET_POOL_SET_NAME(bloomBuffer2, "PostProcessing.Bloom");
|
||||
|
||||
// TODO: skip this clear? or do it at once for the whole textures (2 calls instead of per-mip)
|
||||
for (int32 mip = 0; mip < bloomMipCount; mip++)
|
||||
{
|
||||
context->Clear(bloomBuffer1->View(0, mip), Color::Transparent);
|
||||
|
||||
@@ -509,7 +509,7 @@ void ProbesRendererService::OnRender(RenderTask* task, GPUContext* context)
|
||||
|
||||
// Render frame
|
||||
Renderer::Render(_task);
|
||||
context->ClearState();
|
||||
context->ResetState();
|
||||
|
||||
// Copy frame to cube face
|
||||
{
|
||||
@@ -568,7 +568,7 @@ void ProbesRendererService::OnRender(RenderTask* task, GPUContext* context)
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
context->ClearState();
|
||||
context->ResetState();
|
||||
|
||||
if (_workStep < 7)
|
||||
return; // Continue rendering next frame
|
||||
|
||||
@@ -920,6 +920,7 @@ void RenderList::ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsL
|
||||
constexpr int32 vbMax = ARRAY_COUNT(DrawCall::Geometry.VertexBuffers);
|
||||
if (useInstancing)
|
||||
{
|
||||
context->UpdateCB(perDrawCB, &perDraw);
|
||||
GPUBuffer* vb[vbMax + 1];
|
||||
uint32 vbOffsets[vbMax + 1];
|
||||
vb[3] = _instanceBuffer.GetBuffer(); // Pass object index in a vertex stream at slot 3 (used by VS in Surface.shader)
|
||||
@@ -1057,7 +1058,7 @@ void RenderList::ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsL
|
||||
materialBinds += list.PreBatchedDrawCalls.Count();
|
||||
if (list.Batches.IsEmpty() && list.Indices.Count() != 0)
|
||||
{
|
||||
// Draw calls list has bot been batched so execute draw calls separately
|
||||
// Draw calls list has not been batched so execute draw calls separately
|
||||
for (int32 j = 0; j < list.Indices.Count(); j++)
|
||||
{
|
||||
perDraw.DrawObjectIndex = listData[j];
|
||||
|
||||
@@ -273,7 +273,7 @@ struct DrawCallsList
|
||||
/// <summary>
|
||||
/// True if draw calls batches list can be rendered using hardware instancing, otherwise false.
|
||||
/// </summary>
|
||||
bool CanUseInstancing;
|
||||
bool CanUseInstancing = true;
|
||||
|
||||
void Clear();
|
||||
bool IsEmpty() const;
|
||||
|
||||
@@ -200,7 +200,7 @@ void Renderer::Render(SceneRenderTask* task)
|
||||
|
||||
// Prepare GPU context
|
||||
auto context = GPUDevice::Instance->GetMainContext();
|
||||
context->ClearState();
|
||||
context->ResetState();
|
||||
context->FlushState();
|
||||
const Viewport viewport = task->GetViewport();
|
||||
context->SetViewportAndScissors(viewport);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -2137,6 +2137,53 @@ static void* OnMonoDlFallbackClose(void* handle, void* user_data)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef USE_MONO_AOT_MODULE
|
||||
|
||||
#include "Engine/Threading/ThreadPoolTask.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
|
||||
class MonoAotPreloadTask : public ThreadPoolTask
|
||||
{
|
||||
public:
|
||||
bool Run() override;
|
||||
};
|
||||
|
||||
// Preloads in-build AOT dynamic module in async
|
||||
class MonoAotPreloadService : public EngineService
|
||||
{
|
||||
public:
|
||||
volatile int64 Ready = 0;
|
||||
void* Library = nullptr;
|
||||
|
||||
MonoAotPreloadService()
|
||||
: EngineService(TEXT("AOT Preload"), -800)
|
||||
{
|
||||
}
|
||||
|
||||
bool Init() override
|
||||
{
|
||||
New<MonoAotPreloadTask>()->Start();
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
MonoAotPreloadService MonoAotPreloadServiceInstance;
|
||||
|
||||
bool MonoAotPreloadTask::Run()
|
||||
{
|
||||
// Load AOT module
|
||||
Stopwatch aotModuleLoadStopwatch;
|
||||
//LOG(Info, "Loading Mono AOT module...");
|
||||
MonoAotPreloadServiceInstance.Library = Platform::LoadLibrary(TEXT(USE_MONO_AOT_MODULE));
|
||||
aotModuleLoadStopwatch.Stop();
|
||||
LOG(Info, "Mono AOT module loaded in {0}ms", aotModuleLoadStopwatch.GetMilliseconds());
|
||||
|
||||
Platform::AtomicStore(&MonoAotPreloadServiceInstance.Ready, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool InitHostfxr()
|
||||
{
|
||||
#if DOTNET_HOST_MONO_DEBUG
|
||||
@@ -2147,7 +2194,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,16 +2208,18 @@ 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
|
||||
// Load AOT module
|
||||
Stopwatch aotModuleLoadStopwatch;
|
||||
LOG(Info, "Loading Mono AOT module...");
|
||||
void* libAotModule = Platform::LoadLibrary(TEXT(USE_MONO_AOT_MODULE));
|
||||
// Wait for AOT module preloading
|
||||
while (Platform::AtomicRead(&MonoAotPreloadServiceInstance.Ready) == 0)
|
||||
Platform::Yield();
|
||||
|
||||
// Initialize AOT module
|
||||
void* libAotModule = MonoAotPreloadServiceInstance.Library;
|
||||
if (libAotModule == nullptr)
|
||||
{
|
||||
LOG(Error, "Failed to laod Mono AOT module (" TEXT(USE_MONO_AOT_MODULE) ")");
|
||||
@@ -2187,8 +2242,6 @@ bool InitHostfxr()
|
||||
mono_aot_register_module((void**)modules[i]);
|
||||
}
|
||||
Allocator::Free(modules);
|
||||
aotModuleLoadStopwatch.Stop();
|
||||
LOG(Info, "Mono AOT module loaded in {0}ms", aotModuleLoadStopwatch.GetMilliseconds());
|
||||
#endif
|
||||
|
||||
// Setup debugger
|
||||
|
||||
@@ -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.");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -33,6 +33,13 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
ScriptingObjectReferenceBase(ScriptingObjectReferenceBase&& other) noexcept
|
||||
: _object(nullptr)
|
||||
{
|
||||
OnSet(other._object);
|
||||
other.OnSet(nullptr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ScriptingObjectReferenceBase"/> class.
|
||||
/// </summary>
|
||||
@@ -96,6 +103,16 @@ protected:
|
||||
void OnSet(ScriptingObject* object);
|
||||
|
||||
void OnDeleted(ScriptingObject* obj);
|
||||
|
||||
ScriptingObjectReferenceBase& operator=(ScriptingObjectReferenceBase&& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
OnSet(other._object);
|
||||
other.OnSet(nullptr);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -133,6 +150,11 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
ScriptingObjectReference(ScriptingObjectReference&& other) noexcept
|
||||
: ScriptingObjectReferenceBase(MoveTemp(other))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="ScriptingObjectReference"/> class.
|
||||
/// </summary>
|
||||
@@ -173,6 +195,12 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
ScriptingObjectReference& operator=(ScriptingObjectReference&& other) noexcept
|
||||
{
|
||||
ScriptingObjectReferenceBase::operator=(MoveTemp(other));
|
||||
return *this;
|
||||
}
|
||||
|
||||
FORCE_INLINE ScriptingObjectReference& operator=(const Guid& id)
|
||||
{
|
||||
OnSet(static_cast<ScriptingObject*>(FindObject(id, T::GetStaticClass())));
|
||||
|
||||
@@ -76,16 +76,19 @@ ShaderCompilerDX::ShaderCompilerDX(ShaderProfile profile, PlatformType platform,
|
||||
{
|
||||
IDxcCompiler3* compiler = nullptr;
|
||||
IDxcLibrary* library = nullptr;
|
||||
IDxcContainerBuilder* builder = nullptr;
|
||||
IDxcContainerReflection* containerReflection = nullptr;
|
||||
DxcCreateInstanceProc createInstance = dxcCreateInstanceProc ? (DxcCreateInstanceProc)dxcCreateInstanceProc : &DxcCreateInstance;
|
||||
if (FAILED(createInstance(CLSID_DxcCompiler, __uuidof(compiler), reinterpret_cast<void**>(&compiler))) ||
|
||||
FAILED(createInstance(CLSID_DxcLibrary, __uuidof(library), reinterpret_cast<void**>(&library))) ||
|
||||
FAILED(createInstance(CLSID_DxcContainerBuilder, __uuidof(builder), reinterpret_cast<void**>(&builder))) ||
|
||||
FAILED(createInstance(CLSID_DxcContainerReflection, __uuidof(containerReflection), reinterpret_cast<void**>(&containerReflection))))
|
||||
{
|
||||
LOG(Error, "DxcCreateInstance failed");
|
||||
}
|
||||
_compiler = compiler;
|
||||
_library = library;
|
||||
_builder = builder;
|
||||
_containerReflection = containerReflection;
|
||||
static HashSet<void*> PrintVersions;
|
||||
if (PrintVersions.Add(createInstance))
|
||||
@@ -103,14 +106,13 @@ ShaderCompilerDX::ShaderCompilerDX(ShaderProfile profile, PlatformType platform,
|
||||
|
||||
ShaderCompilerDX::~ShaderCompilerDX()
|
||||
{
|
||||
auto compiler = (IDxcCompiler2*)_compiler;
|
||||
if (compiler)
|
||||
if (auto compiler = (IDxcCompiler2*)_compiler)
|
||||
compiler->Release();
|
||||
auto library = (IDxcLibrary*)_library;
|
||||
if (library)
|
||||
if (auto library = (IDxcLibrary*)_library)
|
||||
library->Release();
|
||||
auto containerReflection = (IDxcContainerReflection*)_containerReflection;
|
||||
if (containerReflection)
|
||||
if (auto builder = (IDxcContainerBuilder*)_builder)
|
||||
builder->Release();
|
||||
if (auto containerReflection = (IDxcContainerReflection*)_containerReflection)
|
||||
containerReflection->Release();
|
||||
}
|
||||
|
||||
@@ -254,7 +256,7 @@ bool ShaderCompilerDX::CompileShader(ShaderFunctionMeta& meta, WritePermutationD
|
||||
}
|
||||
|
||||
// Get the output
|
||||
ComPtr<IDxcBlob> shaderBuffer = nullptr;
|
||||
ComPtr<IDxcBlob> shaderBuffer;
|
||||
if (FAILED(results->GetResult(&shaderBuffer)))
|
||||
{
|
||||
LOG(Error, "IDxcOperationResult::GetResult failed.");
|
||||
@@ -460,6 +462,28 @@ bool ShaderCompilerDX::CompileShader(ShaderFunctionMeta& meta, WritePermutationD
|
||||
}
|
||||
}
|
||||
|
||||
// Strip reflection data
|
||||
if (!options->GenerateDebugData)
|
||||
{
|
||||
if (auto builder = (IDxcContainerBuilder*)_builder)
|
||||
{
|
||||
if (builder->Load(shaderBuffer) == S_OK)
|
||||
{
|
||||
builder->RemovePart(DXC_PART_PDB);
|
||||
builder->RemovePart(DXC_PART_REFLECTION_DATA);
|
||||
ComPtr<IDxcOperationResult> serializeResult;
|
||||
if (builder->SerializeContainer(&serializeResult) == S_OK)
|
||||
{
|
||||
ComPtr<IDxcBlob> optimizedShaderBuffer;
|
||||
if (SUCCEEDED(serializeResult->GetResult(&optimizedShaderBuffer)))
|
||||
{
|
||||
shaderBuffer = optimizedShaderBuffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (WriteShaderFunctionPermutation(_context, meta, permutationIndex, bindings, &header, sizeof(header), shaderBuffer->GetBufferPointer(), (int32)shaderBuffer->GetBufferSize()))
|
||||
return true;
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ private:
|
||||
Array<char> _funcNameDefineBuffer;
|
||||
void* _compiler;
|
||||
void* _library;
|
||||
void* _builder;
|
||||
void* _containerReflection;
|
||||
|
||||
public:
|
||||
|
||||
@@ -278,6 +278,17 @@ bool ShaderCompiler::WriteShaderFunctionPermutation(ShaderCompilationContext* co
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ShaderCompiler::WriteShaderFunctionPermutation(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const ShaderBindings& bindings, const void* header, int32 headerSize, const void* cache1, int32 cache1Size, const void* cache2, int32 cache2Size)
|
||||
{
|
||||
auto output = context->Output;
|
||||
output->Write((uint32)(cache1Size + cache2Size + headerSize));
|
||||
output->WriteBytes(header, headerSize);
|
||||
output->WriteBytes(cache1, cache1Size);
|
||||
output->WriteBytes(cache2, cache2Size);
|
||||
output->Write(bindings);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ShaderCompiler::WriteShaderFunctionPermutation(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const ShaderBindings& bindings, const void* cache, int32 cacheSize)
|
||||
{
|
||||
auto output = context->Output;
|
||||
|
||||
@@ -108,6 +108,7 @@ protected:
|
||||
|
||||
static bool WriteShaderFunctionBegin(ShaderCompilationContext* context, ShaderFunctionMeta& meta);
|
||||
static bool WriteShaderFunctionPermutation(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const ShaderBindings& bindings, const void* header, int32 headerSize, const void* cache, int32 cacheSize);
|
||||
static bool WriteShaderFunctionPermutation(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const ShaderBindings& bindings, const void* header, int32 headerSize, const void* cache1, int32 cache1Size, const void* cache2, int32 cache2Size);
|
||||
static bool WriteShaderFunctionPermutation(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const ShaderBindings& bindings, const void* cache, int32 cacheSize);
|
||||
static bool WriteShaderFunctionEnd(ShaderCompilationContext* context, ShaderFunctionMeta& meta);
|
||||
static bool WriteCustomDataVS(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const Array<ShaderMacro>& macros, void* additionalData);
|
||||
|
||||
@@ -376,7 +376,7 @@ void ShadowsOfMordor::Builder::onJobRender(GPUContext* context)
|
||||
EnableLightmapsUsage = _giBounceRunningIndex != 0;
|
||||
//
|
||||
Renderer::Render(_task);
|
||||
context->ClearState();
|
||||
context->ResetState();
|
||||
//
|
||||
IsRunningRadiancePass = false;
|
||||
EnableLightmapsUsage = true;
|
||||
@@ -515,7 +515,7 @@ void ShadowsOfMordor::Builder::onJobRender(GPUContext* context)
|
||||
}
|
||||
|
||||
// Cleanup after rendering
|
||||
context->ClearState();
|
||||
context->ResetState();
|
||||
|
||||
// Mark job as done
|
||||
Platform::AtomicStore(&_wasJobDone, 1);
|
||||
|
||||
@@ -27,6 +27,19 @@ void CheckBitArray(const BitArray<AllocationType>& array)
|
||||
|
||||
TEST_CASE("Array")
|
||||
{
|
||||
SECTION("Test Capacity")
|
||||
{
|
||||
// Ensure correct collections capacity growing to meet proper memory usage vs safe slack
|
||||
CHECK(AllocationUtils::CalculateCapacityGrow(1, 0) == 8);
|
||||
CHECK(AllocationUtils::CalculateCapacityGrow(7, 0) == 8);
|
||||
CHECK(AllocationUtils::CalculateCapacityGrow(1, 16) == 16);
|
||||
CHECK(AllocationUtils::CalculateCapacityGrow(31, 0) == 32);
|
||||
CHECK(AllocationUtils::CalculateCapacityGrow(32, 0) == 32);
|
||||
CHECK(AllocationUtils::CalculateCapacityGrow(1000, 0) == 1024);
|
||||
CHECK(AllocationUtils::CalculateCapacityGrow(1024, 0) == 1024);
|
||||
CHECK(AllocationUtils::CalculateCapacityGrow(1025, 0) == 2048);
|
||||
}
|
||||
|
||||
SECTION("Test Allocators")
|
||||
{
|
||||
Array<int32> a1;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -790,9 +790,18 @@ void MaterialGenerator::ProcessGroupFunction(Box* box, Node* node, Value& value)
|
||||
// Function Input
|
||||
case 1:
|
||||
{
|
||||
// Check the stack count. If only 1 graph is present,
|
||||
// we are processing the graph in isolation (e.g., in the Editor Preview).
|
||||
// In this case, we skip the caller-finding logic and use the node's default value.
|
||||
if (_graphStack.Count() < 2)
|
||||
{
|
||||
// Use the default value from the function input node's box (usually box 1)
|
||||
value = tryGetValue(node->TryGetBox(1), Value::Zero);
|
||||
break;
|
||||
}
|
||||
|
||||
// Find the function call
|
||||
Node* functionCallNode = nullptr;
|
||||
ASSERT(_graphStack.Count() >= 2);
|
||||
Graph* graph;
|
||||
for (int32 i = _callStack.Count() - 1; i >= 0; i--)
|
||||
{
|
||||
|
||||
@@ -303,6 +303,12 @@ namespace FlaxEngine.GUI
|
||||
[EditorDisplay("Text Style"), EditorOrder(2023), ExpandGroups]
|
||||
public Color TextColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color used to display highlighted text.
|
||||
/// </summary>
|
||||
[EditorDisplay("Text Style"), EditorOrder(2024), ExpandGroups]
|
||||
public Color TextColorHighlighted { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the horizontal text alignment within the control bounds.
|
||||
/// </summary>
|
||||
@@ -386,6 +392,7 @@ namespace FlaxEngine.GUI
|
||||
var style = Style.Current;
|
||||
Font = new FontReference(style.FontMedium);
|
||||
TextColor = style.Foreground;
|
||||
TextColorHighlighted = style.Foreground;
|
||||
BackgroundColor = style.BackgroundNormal;
|
||||
BackgroundColorHighlighted = BackgroundColor;
|
||||
BackgroundColorSelected = BackgroundColor;
|
||||
@@ -587,8 +594,8 @@ namespace FlaxEngine.GUI
|
||||
X = margin,
|
||||
Size = new Float2(size.X - margin, size.Y),
|
||||
Font = Font,
|
||||
TextColor = Color.White * 0.9f,
|
||||
TextColorHighlighted = Color.White,
|
||||
TextColor = TextColor * 0.9f,
|
||||
TextColorHighlighted = TextColorHighlighted,
|
||||
HorizontalAlignment = HorizontalAlignment,
|
||||
VerticalAlignment = VerticalAlignment,
|
||||
Text = _items[i],
|
||||
@@ -749,7 +756,7 @@ namespace FlaxEngine.GUI
|
||||
// Draw text of the selected item
|
||||
var textRect = new Rectangle(margin, 0, clientRect.Width - boxSize - 2.0f * margin, clientRect.Height);
|
||||
Render2D.PushClip(textRect);
|
||||
var textColor = TextColor;
|
||||
var textColor = (IsMouseOver || IsNavFocused) ? TextColorHighlighted : TextColor;
|
||||
string text = _items[_selectedIndex];
|
||||
string format = TextFormat != null ? TextFormat : null;
|
||||
if (!string.IsNullOrEmpty(format))
|
||||
|
||||
@@ -146,6 +146,8 @@ void ShaderGenerator::ProcessGroupMath(Box* box, Node* node, Value& value)
|
||||
Box* b2 = node->GetBox(1);
|
||||
Value v1 = tryGetValue(b1, 0, Value::Zero);
|
||||
Value v2 = tryGetValue(b2, 1, Value::Zero);
|
||||
if (SanitizeMathValue(v1, node, b1, &value))
|
||||
break;
|
||||
if (b1->HasConnection())
|
||||
v2 = v2.Cast(v1.Type);
|
||||
else
|
||||
@@ -251,7 +253,10 @@ void ShaderGenerator::ProcessGroupMath(Box* box, Node* node, Value& value)
|
||||
// Lerp
|
||||
case 25:
|
||||
{
|
||||
Value a = tryGetValue(node->GetBox(0), 0, Value::Zero);
|
||||
auto boxA = node->GetBox(0);
|
||||
Value a = tryGetValue(boxA, 0, Value::Zero);
|
||||
if (SanitizeMathValue(a, node, boxA, &value))
|
||||
break;
|
||||
Value b = tryGetValue(node->GetBox(1), 1, Value::One).Cast(a.Type);
|
||||
Value alpha = tryGetValue(node->GetBox(2), 2, Value::Zero).Cast(ValueType::Float);
|
||||
String text = String::Format(TEXT("lerp({0}, {1}, {2})"), a.Value, b.Value, alpha.Value);
|
||||
@@ -1364,6 +1369,20 @@ SerializedMaterialParam& ShaderGenerator::findOrAddGlobalSDF()
|
||||
return param;
|
||||
}
|
||||
|
||||
bool ShaderGenerator::SanitizeMathValue(Value& value, Node* node, Box* box, Value* resultOnInvalid)
|
||||
{
|
||||
bool invalid = value.Type == VariantType::Object;
|
||||
if (invalid)
|
||||
{
|
||||
OnError(node, box, TEXT("Invalid input type for math operation"));
|
||||
if (resultOnInvalid)
|
||||
*resultOnInvalid = Value::Zero;
|
||||
else
|
||||
value = Value::Zero;
|
||||
}
|
||||
return invalid;
|
||||
}
|
||||
|
||||
String ShaderGenerator::getLocalName(int32 index)
|
||||
{
|
||||
return TEXT("local") + StringUtils::ToString(index);
|
||||
|
||||
@@ -255,6 +255,8 @@ protected:
|
||||
SerializedMaterialParam& findOrAddTextureGroupSampler(int32 index);
|
||||
SerializedMaterialParam& findOrAddGlobalSDF();
|
||||
|
||||
bool SanitizeMathValue(Value& value, Node* node, Box* box, Value* resultOnInvalid = nullptr);
|
||||
|
||||
static String getLocalName(int32 index);
|
||||
static String getParamName(int32 index);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
package com.flaxengine;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -387,17 +387,7 @@ namespace Flax.Build.Bindings
|
||||
|
||||
// Find namespace for this type to build a fullname
|
||||
if (apiType != null)
|
||||
{
|
||||
var e = apiType.Parent;
|
||||
while (!(e is FileInfo))
|
||||
{
|
||||
e = e.Parent;
|
||||
}
|
||||
if (e is FileInfo fileInfo && !managedType.StartsWith(fileInfo.Namespace))
|
||||
{
|
||||
managedType = fileInfo.Namespace + '.' + managedType.Replace(".", "+");
|
||||
}
|
||||
}
|
||||
managedType = apiType.Namespace + '.' + managedType.Replace(".", "+");
|
||||
|
||||
// Use runtime lookup from fullname of the C# class
|
||||
return "Scripting::FindClass(\"" + managedType + "\")";
|
||||
@@ -770,6 +760,11 @@ namespace Flax.Build.Bindings
|
||||
genericArgs += ", " + typeInfo.GenericArgs[1];
|
||||
result = $"Array<{genericArgs}>({result})";
|
||||
}
|
||||
else if (arrayApiType?.Name == "bool")
|
||||
{
|
||||
type = "bool*";
|
||||
result = "Array<bool>({0}, {1})";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -925,7 +920,7 @@ namespace Flax.Build.Bindings
|
||||
|
||||
// BytesContainer
|
||||
if (typeInfo.Type == "BytesContainer" && typeInfo.GenericArgs == null)
|
||||
return "MUtils::ToArray({0})";
|
||||
return $"MUtils::ToArray({value})";
|
||||
|
||||
// Construct native typename for MUtils template argument
|
||||
var nativeType = new StringBuilder(64);
|
||||
@@ -1244,8 +1239,12 @@ namespace Flax.Build.Bindings
|
||||
callParams += ", ";
|
||||
separator = true;
|
||||
var name = parameterInfo.Name;
|
||||
var countParamName = $"__{parameterInfo.Name}Count";
|
||||
if (CppParamsThatNeedConversion[i] && (!FindApiTypeInfo(buildData, parameterInfo.Type, caller)?.IsStruct ?? false))
|
||||
{
|
||||
name = '*' + name;
|
||||
countParamName = '*' + countParamName;
|
||||
}
|
||||
|
||||
string param = string.Empty;
|
||||
if (string.IsNullOrWhiteSpace(CppParamsWrappersCache[i]))
|
||||
@@ -1258,7 +1257,7 @@ namespace Flax.Build.Bindings
|
||||
else
|
||||
{
|
||||
// Convert value
|
||||
param += string.Format(CppParamsWrappersCache[i], name);
|
||||
param += string.Format(CppParamsWrappersCache[i], name, countParamName);
|
||||
}
|
||||
|
||||
// Special case for output result parameters that needs additional converting from native to managed format (such as non-POD structures or output array parameter)
|
||||
@@ -1293,11 +1292,21 @@ namespace Flax.Build.Bindings
|
||||
callParams += parameterInfo.Name;
|
||||
callParams += "Temp";
|
||||
}
|
||||
// Instruct for more optoimized value move operation
|
||||
// Instruct for more optimized value move operation
|
||||
else if (parameterInfo.Type.IsMoveRef)
|
||||
{
|
||||
callParams += $"MoveTemp({param})";
|
||||
}
|
||||
else if (parameterInfo.Type.IsRef && !parameterInfo.Type.IsConst)
|
||||
{
|
||||
// Non-const lvalue reference parameters needs to be passed via temporary value
|
||||
if (parameterInfo.IsOut || parameterInfo.IsRef)
|
||||
contents.Append(indent).AppendFormat("{2}& {0}Temp = {1};", parameterInfo.Name, param, parameterInfo.Type.ToString(false)).AppendLine();
|
||||
else
|
||||
contents.Append(indent).AppendFormat("{2} {0}Temp = {1};", parameterInfo.Name, param, parameterInfo.Type.ToString(false)).AppendLine();
|
||||
callParams += parameterInfo.Name;
|
||||
callParams += "Temp";
|
||||
}
|
||||
else
|
||||
{
|
||||
callParams += param;
|
||||
@@ -2997,16 +3006,19 @@ namespace Flax.Build.Bindings
|
||||
header.Append("template<>").AppendLine();
|
||||
header.AppendFormat("struct MConverter<{0}>", fullName).AppendLine();
|
||||
header.Append('{').AppendLine();
|
||||
header.AppendFormat(" MObject* Box(const {0}& data, const MClass* klass)", fullName).AppendLine();
|
||||
|
||||
header.AppendFormat(" DLLEXPORT USED MObject* Box(const {0}& data, const MClass* klass)", fullName).AppendLine();
|
||||
header.Append(" {").AppendLine();
|
||||
header.Append(" auto managed = ToManaged(data);").AppendLine();
|
||||
header.Append(" return MCore::Object::Box((void*)&managed, klass);").AppendLine();
|
||||
header.Append(" }").AppendLine();
|
||||
header.AppendFormat(" void Unbox({0}& result, MObject* data)", fullName).AppendLine();
|
||||
|
||||
header.AppendFormat(" DLLEXPORT USED void Unbox({0}& result, MObject* data)", fullName).AppendLine();
|
||||
header.Append(" {").AppendLine();
|
||||
header.AppendFormat(" result = ToNative(*reinterpret_cast<{0}*>(MCore::Object::Unbox(data)));", wrapperName).AppendLine();
|
||||
header.Append(" }").AppendLine();
|
||||
header.AppendFormat(" void ToManagedArray(MArray* result, const Span<{0}>& data)", fullName).AppendLine();
|
||||
|
||||
header.AppendFormat(" DLLEXPORT USED void ToManagedArray(MArray* result, const Span<{0}>& data)", fullName).AppendLine();
|
||||
header.Append(" {").AppendLine();
|
||||
header.AppendFormat(" MClass* klass = {0}::TypeInitializer.GetClass();", fullName).AppendLine();
|
||||
header.AppendFormat(" {0}* resultPtr = ({0}*)MCore::Array::GetAddress(result);", wrapperName).AppendLine();
|
||||
@@ -3016,7 +3028,8 @@ namespace Flax.Build.Bindings
|
||||
header.Append(" MCore::GC::WriteValue(&resultPtr[i], &managed, 1, klass);").AppendLine();
|
||||
header.Append(" }").AppendLine();
|
||||
header.Append(" }").AppendLine();
|
||||
header.AppendFormat(" void ToNativeArray(Span<{0}>& result, const MArray* data)", fullName).AppendLine();
|
||||
|
||||
header.AppendFormat(" DLLEXPORT USED void ToNativeArray(Span<{0}>& result, const MArray* data)", fullName).AppendLine();
|
||||
header.Append(" {").AppendLine();
|
||||
header.AppendFormat(" {0}* dataPtr = ({0}*)MCore::Array::GetAddress(data);", wrapperName).AppendLine();
|
||||
header.Append(" for (int32 i = 0; i < result.Length(); i++)").AppendLine();
|
||||
@@ -3108,7 +3121,7 @@ namespace Flax.Build.Bindings
|
||||
header.AppendFormat("struct MConverter<{0}>", fullName).AppendLine();
|
||||
header.Append('{').AppendLine();
|
||||
|
||||
header.AppendFormat(" static MObject* Box(const {0}& data, const MClass* klass)", fullName).AppendLine();
|
||||
header.AppendFormat(" DLLEXPORT USED static MObject* Box(const {0}& data, const MClass* klass)", fullName).AppendLine();
|
||||
header.Append(" {").AppendLine();
|
||||
header.Append(" MObject* obj = MCore::Object::New(klass);").AppendLine();
|
||||
for (var i = 0; i < fields.Count; i++)
|
||||
@@ -3128,13 +3141,13 @@ namespace Flax.Build.Bindings
|
||||
header.Append(" return obj;").AppendLine();
|
||||
header.Append(" }").AppendLine();
|
||||
|
||||
header.AppendFormat(" static MObject* Box(const {0}& data)", fullName).AppendLine();
|
||||
header.AppendFormat(" DLLEXPORT USED static MObject* Box(const {0}& data)", fullName).AppendLine();
|
||||
header.Append(" {").AppendLine();
|
||||
header.AppendFormat(" MClass* klass = {0}::TypeInitializer.GetClass();", fullName).AppendLine();
|
||||
header.Append(" return Box(data, klass);").AppendLine();
|
||||
header.Append(" }").AppendLine();
|
||||
|
||||
header.AppendFormat(" static void Unbox({0}& result, MObject* obj)", fullName).AppendLine();
|
||||
header.AppendFormat(" DLLEXPORT USED static void Unbox({0}& result, MObject* obj)", fullName).AppendLine();
|
||||
header.Append(" {").AppendLine();
|
||||
header.Append(" MClass* klass = MCore::Object::GetClass(obj);").AppendLine();
|
||||
header.Append(" void* v = nullptr;").AppendLine();
|
||||
@@ -3156,20 +3169,20 @@ namespace Flax.Build.Bindings
|
||||
}
|
||||
header.Append(" }").AppendLine();
|
||||
|
||||
header.AppendFormat(" static {0} Unbox(MObject* data)", fullName).AppendLine();
|
||||
header.AppendFormat(" DLLEXPORT USED static {0} Unbox(MObject* data)", fullName).AppendLine();
|
||||
header.Append(" {").AppendLine();
|
||||
header.AppendFormat(" {0} result;", fullName).AppendLine();
|
||||
header.Append(" Unbox(result, data);").AppendLine();
|
||||
header.Append(" return result;").AppendLine();
|
||||
header.Append(" }").AppendLine();
|
||||
|
||||
header.AppendFormat(" void ToManagedArray(MArray* result, const Span<{0}>& data)", fullName).AppendLine();
|
||||
header.AppendFormat(" DLLEXPORT USED void ToManagedArray(MArray* result, const Span<{0}>& data)", fullName).AppendLine();
|
||||
header.Append(" {").AppendLine();
|
||||
header.Append(" for (int32 i = 0; i < data.Length(); i++)").AppendLine();
|
||||
header.Append(" MCore::GC::WriteArrayRef(result, Box(data[i]), i);").AppendLine();
|
||||
header.Append(" }").AppendLine();
|
||||
|
||||
header.AppendFormat(" void ToNativeArray(Span<{0}>& result, const MArray* data)", fullName).AppendLine();
|
||||
header.AppendFormat(" DLLEXPORT USED void ToNativeArray(Span<{0}>& result, const MArray* data)", fullName).AppendLine();
|
||||
header.Append(" {").AppendLine();
|
||||
header.Append(" MObject** dataPtr = (MObject**)MCore::Array::GetAddress(data);").AppendLine();
|
||||
header.Append(" for (int32 i = 0; i < result.Length(); i++)").AppendLine();
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -282,7 +282,7 @@ namespace Flax.Build
|
||||
dotnetRuntimeVersions = MergeVersions(dotnetRuntimeVersions, GetVersions(Path.Combine(dotnetPath, "shared", "Microsoft.NETCore.App")));
|
||||
|
||||
dotnetSdkVersions = dotnetSdkVersions.Where(x => File.Exists(Path.Combine(dotnetPath, "sdk", x, ".version")));
|
||||
dotnetRuntimeVersions = dotnetRuntimeVersions.Where(x => File.Exists(Path.Combine(dotnetPath, "shared", "Microsoft.NETCore.App", x, ".version")));
|
||||
dotnetRuntimeVersions = dotnetRuntimeVersions.Where(x => File.Exists(Path.Combine(dotnetPath, "shared", "Microsoft.NETCore.App", x, "System.dll")));
|
||||
dotnetRuntimeVersions = dotnetRuntimeVersions.Where(x => Directory.Exists(Path.Combine(dotnetPath, "packs", "Microsoft.NETCore.App.Ref", x)));
|
||||
|
||||
dotnetSdkVersions = dotnetSdkVersions.OrderByDescending(ParseVersion);
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
@@ -217,7 +217,8 @@ namespace Flax.Build
|
||||
var engineLibraryType = LinkerOutput.SharedLibrary;
|
||||
if (buildOptions.Toolchain?.Compiler == TargetCompiler.MSVC)
|
||||
engineLibraryType = LinkerOutput.ImportLibrary; // MSVC links DLL against import library
|
||||
exeBuildOptions.LinkEnv.InputLibraries.Add(Path.Combine(buildOptions.OutputFolder, buildOptions.Platform.GetLinkOutputFileName(LibraryName, engineLibraryType)));
|
||||
var engineLibraryPath = Utilities.NormalizePath(Path.Combine(buildOptions.OutputFolder, buildOptions.Platform.GetLinkOutputFileName(LibraryName, engineLibraryType)));
|
||||
exeBuildOptions.LinkEnv.InputLibraries.Add(engineLibraryPath);
|
||||
exeBuildOptions.LinkEnv.InputFiles.AddRange(mainModuleOptions.OutputFiles);
|
||||
exeBuildOptions.DependencyFiles.AddRange(mainModuleOptions.DependencyFiles);
|
||||
exeBuildOptions.NugetPackageReferences.AddRange(mainModuleOptions.NugetPackageReferences);
|
||||
|
||||
@@ -134,7 +134,7 @@ namespace Flax.Build.Graph
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs tasks list sorting based on task dependencies and cost heuristics to to improve parallelism of the graph execution.
|
||||
/// Performs tasks list sorting based on task dependencies and cost heuristics to improve parallelism of the graph execution.
|
||||
/// </summary>
|
||||
public void SortTasks()
|
||||
{
|
||||
@@ -149,12 +149,7 @@ namespace Flax.Build.Graph
|
||||
{
|
||||
if (FileToProducingTaskMap.TryGetValue(prerequisiteFile, out var prerequisiteTask))
|
||||
{
|
||||
HashSet<Task> dependentTasks;
|
||||
if (taskToDependentActionsMap.ContainsKey(prerequisiteTask))
|
||||
{
|
||||
dependentTasks = taskToDependentActionsMap[prerequisiteTask];
|
||||
}
|
||||
else
|
||||
if (!taskToDependentActionsMap.TryGetValue(prerequisiteTask, out var dependentTasks))
|
||||
{
|
||||
dependentTasks = new HashSet<Task>();
|
||||
taskToDependentActionsMap[prerequisiteTask] = dependentTasks;
|
||||
|
||||
@@ -94,6 +94,7 @@ namespace Flax.Deps.Dependencies
|
||||
defines += "-DDISABLE_EXECUTABLES=1-DDISABLE_SHARED_LIBS=1";
|
||||
buildArgs = $" -subset mono+libs -cmakeargs \"{defines}\" /p:FeaturePerfTracing=false /p:FeatureWin32Registry=false /p:FeatureCominteropApartmentSupport=false /p:FeatureManagedEtw=false /p:FeatureManagedEtwChannels=false /p:FeatureEtw=false /p:ApiCompatValidateAssemblies=false";
|
||||
envVars.Add("_GAMING_XBOX", "1");
|
||||
envVars.Add(targetPlatform == TargetPlatform.XboxScarlett ? "_GAMING_XBOX_SCARLETT" : "_GAMING_XBOX_XBOXONE", "1");
|
||||
break;
|
||||
case TargetPlatform.Linux:
|
||||
os = "linux";
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
@@ -507,7 +518,7 @@ namespace Flax.Build.Platforms
|
||||
var args = new List<string>();
|
||||
args.AddRange(options.LinkEnv.CustomArgs);
|
||||
{
|
||||
args.Add(string.Format("-o \"{0}\"", outputFilePath));
|
||||
args.Add(string.Format("-o \"{0}\"", outputFilePath.Replace('\\', '/')));
|
||||
|
||||
if (!options.LinkEnv.DebugInformation)
|
||||
{
|
||||
@@ -527,7 +538,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);
|
||||
@@ -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('\\', '/')));
|
||||
}
|
||||
@@ -619,7 +605,7 @@ namespace Flax.Build.Platforms
|
||||
/// <inheritdoc />
|
||||
public override void LinkFiles(TaskGraph graph, BuildOptions options, string outputFilePath)
|
||||
{
|
||||
outputFilePath = outputFilePath.Replace('\\', '/');
|
||||
outputFilePath = Utilities.NormalizePath(outputFilePath);
|
||||
|
||||
Task linkTask;
|
||||
switch (options.LinkEnv.Output)
|
||||
|
||||
@@ -778,6 +778,7 @@ namespace Flax.Build.Platforms
|
||||
/// <inheritdoc />
|
||||
public override void LinkFiles(TaskGraph graph, BuildOptions options, string outputFilePath)
|
||||
{
|
||||
outputFilePath = Utilities.NormalizePath(outputFilePath);
|
||||
var linkEnvironment = options.LinkEnv;
|
||||
var task = graph.Add<LinkTask>();
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user