83 Commits

Author SHA1 Message Date
824ff7050e Fix managed wrapper function parameter handling for BytesContainer
Some checks failed
Build Android / Game (Android, Release ARM64) (push) Has been cancelled
Build iOS / Game (iOS, Release ARM64) (push) Has been cancelled
Build Linux / Editor (Linux, Development x64) (push) Has been cancelled
Build Linux / Game (Linux, Release x64) (push) Has been cancelled
Build macOS / Editor (Mac, Development ARM64) (push) Has been cancelled
Build macOS / Game (Mac, Release ARM64) (push) Has been cancelled
Build Windows / Editor (Windows, Development x64) (push) Has been cancelled
Build Windows / Game (Windows, Release x64) (push) Has been cancelled
Cooker / Cook (Mac) (push) Has been cancelled
Tests / Tests (Linux) (push) Has been cancelled
Tests / Tests (Windows) (push) Has been cancelled
2025-12-23 01:41:01 +02:00
042843fe42 Fix clang bindings code generation for non-const ref parameters
Some checks failed
Build Android / Game (Android, Release ARM64) (push) Has been cancelled
Build iOS / Game (iOS, Release ARM64) (push) Has been cancelled
Build Linux / Editor (Linux, Development x64) (push) Has been cancelled
Build Linux / Game (Linux, Release x64) (push) Has been cancelled
Build macOS / Editor (Mac, Development ARM64) (push) Has been cancelled
Build macOS / Game (Mac, Release ARM64) (push) Has been cancelled
Build Windows / Editor (Windows, Development x64) (push) Has been cancelled
Build Windows / Game (Windows, Release x64) (push) Has been cancelled
Cooker / Cook (Mac) (push) Has been cancelled
Tests / Tests (Linux) (push) Has been cancelled
Tests / Tests (Windows) (push) Has been cancelled
2025-12-20 23:04:08 +02:00
8631b389c1 asdf 2025-12-20 23:02:42 +02:00
e3f5af530b Fix incorrect class namespace in bindings class name lookups 2025-12-19 23:57:41 +02:00
Wojtek Figat
e257f9e4a0 Merge branch 'Tryibion-fix-anim-slot-replay' 2025-12-14 23:03:45 +01:00
Wojtek Figat
056de752ed Add docs 2025-12-14 23:03:34 +01:00
Wojtek Figat
76700c0b24 Merge branch 'fix-anim-slot-replay' of https://github.com/Tryibion/FlaxEngine into Tryibion-fix-anim-slot-replay 2025-12-14 23:01:21 +01:00
Wojtek Figat
9fdcff657d Merge branch 'VitaminCpp-late_join_fix' 2025-12-14 22:58:55 +01:00
Wojtek Figat
2b6339c05c Minor code cleanup 2025-12-14 22:58:53 +01:00
Wojtek Figat
bb91202439 Merge branch 'late_join_fix' of https://github.com/VitaminCpp/FlaxEngine into VitaminCpp-late_join_fix 2025-12-14 22:49:49 +01:00
Wojtek Figat
f25e9f262a Merge branch 'VitaminCpp-replication_hashing_fix' 2025-12-14 22:48:09 +01:00
Wojtek Figat
ee51077f49 Merge branch 'replication_hashing_fix' of https://github.com/VitaminCpp/FlaxEngine into VitaminCpp-replication_hashing_fix 2025-12-14 22:43:58 +01:00
Wojtek Figat
950e958a58 Merge branch 'VitaminCpp-hash_set_crash_fix' 2025-12-14 22:41:11 +01:00
Wojtek Figat
5fdbed2b56 Minor codestyle adjustments 2025-12-14 22:41:00 +01:00
Chandler Cox
0e627577fc Simplify code. 2025-12-14 15:00:44 -06:00
Wojtek Figat
4846d4b024 Merge branch 'hash_set_crash_fix' of https://github.com/VitaminCpp/FlaxEngine into VitaminCpp-hash_set_crash_fix 2025-12-14 21:52:40 +01:00
Wojtek Figat
5e5293bf7b Merge branch 'GoaLitiuM-oob_write_fix' 2025-12-14 21:31:42 +01:00
Wojtek Figat
d88477dcae Merge branch 'oob_write_fix' of https://github.com/GoaLitiuM/FlaxEngine into GoaLitiuM-oob_write_fix 2025-12-14 21:31:37 +01:00
Wojtek Figat
bd58bd91b4 Merge branch 'ThePhantomMask-AddDropdownHighlightedColor' 2025-12-13 23:14:47 +01:00
Wojtek Figat
7ce0d88bdc Merge branch 'AddDropdownHighlightedColor' of https://github.com/ThePhantomMask/FlaxEngine into ThePhantomMask-AddDropdownHighlightedColor 2025-12-13 23:14:09 +01:00
Wojtek Figat
98bb2d40d6 Merge branch 'Inertia-Squared-hyprland-fix' 2025-12-13 23:13:30 +01:00
Wojtek Figat
f4bc620bbd Merge branch 'hyprland-fix' of https://github.com/Inertia-Squared/FlaxEngine into Inertia-Squared-hyprland-fix 2025-12-13 23:13:26 +01:00
Wojtek Figat
0313bf32c9 Merge branch 'AcidicVoid-master' 2025-12-13 23:11:05 +01:00
Wojtek Figat
0c887cd29e Use fix from #3830 in particle and anim graphs too 2025-12-13 23:11:01 +01:00
Wojtek Figat
5bd9bce634 Merge branch 'master' of https://github.com/AcidicVoid/FlaxEngine into AcidicVoid-master 2025-12-13 23:08:01 +01:00
Wojtek Figat
2a53d0a462 Fix crash on Visual Script missing asset ref after hot-reload in Editor
#3823
2025-12-13 02:10:41 +01:00
82bd915274 Fix out-of-bounds write while parsing command-line arguments 2025-12-12 14:47:15 +02:00
Wojtek Figat
71391cf1cc Fix deprecated tag placement 2025-12-11 16:38:28 +01:00
Wojtek Figat
b5286af526 Attempt to fix regression from 32bd72fecd 2025-12-11 14:48:18 +01:00
Wojtek Figat
9f07a2a54e Attempt to fix regression from 32bd72fecd 2025-12-10 18:58:43 +01:00
Wojtek Figat
c39c642b60 Add safety check for invalid math values in shader graph generation 2025-12-10 17:39:18 +01:00
Wojtek Figat
02cff3973a Bump up engine version 2025-12-10 15:01:53 +01:00
Wojtek Figat
a63b97d31d Add stripping DXIL debug data from the shader cache when not used 2025-12-10 14:58:12 +01:00
Wojtek Figat
ca52122656 Fix validation error on Windows for textures but optimize buffers instead 2025-12-10 14:53:51 +01:00
Wojtek Figat
20a7fcf6a0 Add profiler wait event for GPU wait on D3D12 2025-12-10 13:01:24 +01:00
Wojtek Figat
43665aa7eb Rename GPUContext::ClearState to ResetState for constentency 2025-12-10 13:00:59 +01:00
Wojtek Figat
3b9b49950c Fixes for Xbox One 2025-12-10 09:48:47 +01:00
Wojtek Figat
0a8752ec0a Fix cross-building building engine with separate executable and library for Unix platforms on Windows 2025-12-10 09:48:27 +01:00
Wojtek Figat
47685dc2be Merge branch 'VitaminCpp-missing_move_semantics_fix' 2025-12-09 10:00:55 +01:00
Wojtek Figat
517ee5bb25 Merge branch 'missing_move_semantics_fix' of https://github.com/VitaminCpp/FlaxEngine into VitaminCpp-missing_move_semantics_fix 2025-12-09 10:00:51 +01:00
Wojtek Figat
3ab01d3576 Merge branch 'VitaminCpp-minor_mem_layout_opt' 2025-12-09 10:00:20 +01:00
Wojtek Figat
31b6d4d658 Merge branch 'minor_mem_layout_opt' of https://github.com/VitaminCpp/FlaxEngine into VitaminCpp-minor_mem_layout_opt 2025-12-09 10:00:16 +01:00
Wojtek Figat
08f840d642 Merge branch 'Tryibion-fix-exception-reload' 2025-12-09 09:59:56 +01:00
Wojtek Figat
776b6259cd Merge branch 'fix-exception-reload' of https://github.com/Tryibion/FlaxEngine into Tryibion-fix-exception-reload 2025-12-09 09:59:34 +01:00
Wojtek Figat
5c81c71116 Move constant buffer init for instanced draws only, others do it in all paths 2025-12-09 09:51:53 +01:00
Wojtek Figat
188b635ea0 Merge remote-tracking branch 'origin/master' 2025-12-09 09:48:05 +01:00
Wojtek Figat
56066a3212 Porting to a famous blue platform 2025-12-08 14:41:55 -08:00
Phantom
ed50ce9c90 Change Dropdown's EditorOrder from 2023 to 2024 2025-12-07 18:48:16 +01:00
Phantom
a7e77f6e21 Update CreatePopupItem method
-Modify the `TextColour` property to use a dynamic value based on `TextColour` multiplied by `0.9f` instead of a fixed value (`Colour.White * 0.9f`).
-Modify the `TextColourHighlighted` property to use the dynamic value of `TextColourHighlighted` instead of a fixed value (`Colour.White`).
2025-12-07 18:23:38 +01:00
Phantom
56278b17ee Add Text Color Highlighted on Dropdown 2025-12-07 16:53:43 +01:00
Wojtek Figat
bd78db72b9 Add Mono AOT dynamic module preloading to speed up startup time 2025-12-05 03:46:28 -08:00
Wojtek Figat
32bd72fecd Minor fix to the game cooker assets summary log of a single asset 2025-12-04 23:51:07 +01:00
Wojtek Figat
3a798a70fa Fix collections capacity growing to use the closest power of two
Capacity was incorrectly 2x larger than needed.
Added unit test to ensure it stays correct.
2025-12-04 23:29:15 +01:00
Wojtek Figat
02429266b1 Fix Array::RemoveAtKeepOrder to avoid memory override with large mem copy 2025-12-03 05:03:21 -08:00
Wojtek Figat
77aea0c69c Fix fatal error reporting from multiple therads to sync and properly log (eg. out of memory) 2025-12-01 08:18:54 -08:00
Inertia
6a3ce862cb - Add X11 Class hints for easy hooking by WMs for window-specific rules (required to fix some bugs in WMs like Hyprland) 2025-12-01 11:19:35 +11:00
Wojtek Figat
93217da619 Add option to merge vertex layout with reference order maintained 2025-11-29 15:04:11 -08:00
VitaminCpp
63def54dad Merge branch 'FlaxEngine:master' into hash_set_crash_fix 2025-11-28 15:55:15 +01:00
Michael Herzog
00f9a28729 Fixed HashSet compaction count after mid-compact growth
Ensure HashSetBase::Compact() preserves _elementsCount even when EnsureCapacity() triggers during compaction. The growth path resets the counter; we now cache the original count and restore it after moving all buckets so Count() stays correct in heavy-collision scenarios.
2025-11-28 15:51:57 +01:00
Michael Herzog
56beca0db4 Fixed network replicated-object deduplication by hashing/equality on ObjectId
Aligned NetworkReplicatedObject equality with its hash (compare ObjectId, not pointer).
2025-11-27 23:28:17 +01:00
Alex Ray
64cd898a65 Bypassing Call Logic in Editor Preview 2025-11-27 18:09:11 +01:00
VitaminCpp
90472a4b31 Merge branch 'FlaxEngine:master' into late_join_fix 2025-11-27 10:33:53 +01:00
Wojtek Figat
a1999183f2 Fix compilation regression 2025-11-27 09:13:14 +01:00
Wojtek Figat
1e3ce48024 Fix compilation regression 2025-11-26 23:43:20 -08:00
Michael Herzog
0007185b5f Fixed late-join network replication
- Adjusted replication to resend unchanged state only to missing clients.
- Skip server serialization when no recipients, and downgrade unknown-despawn noise.
2025-11-26 17:54:49 +01:00
Wojtek Figat
403d2cedc0 Updates to engine for porting to blue platform 2025-11-26 06:28:54 -08:00
Wojtek Figat
c8839b8587 Add support for Cooperative Suspend when running on Mono
Informs mono runtime that Job System, Thread Pool or Content Load threads can wait when they are going idle between tasks.
2025-11-26 00:22:48 -08:00
Wojtek Figat
cf048c9804 Fix path filter query warning 2025-11-26 00:07:00 -08:00
Wojtek Figat
bea75f51bd Fix AOT libs cooking to avoid file dirtying for more accurate iterative cooking 2025-11-26 00:02:40 -08:00
Chandler Cox
1bf6612002 Fix exception thrown when reloading open windows. 2025-11-25 17:26:57 -06:00
Michael Herzog
d9a18b1d31 Fixed HashSet compact rehash under heavy collisions
- Compact now iterates over the old bucket array using the saved oldSize, and frees with that size, avoiding out-of-bounds when _size changes.
- If reinsertion finds no free slot during compaction (pathological collisions), the table grows once and retries, preventing AVs.
- This fix addresses problems with weak hash keys (like #3824).
2025-11-25 21:23:49 +01:00
Michael Herzog
465f30661f Minor memory layout optimization 2025-11-25 17:36:49 +01:00
Michael Herzog
a62ca5452e Fixed missing move semantics in script object reference 2025-11-25 17:33:11 +01:00
Wojtek Figat
92254eefcc SImplify some code and update code for platforms 2025-11-25 00:20:14 -08:00
Chandler Cox
2d56411e5f Add slot stop methods without anim param. 2025-11-23 14:19:37 -06:00
Chandler Cox
f8dc8ab903 Fix not being able to replay same animation in animation slot. 2025-11-23 14:19:11 -06:00
Wojtek Figat
2a55cda583 Add fallback location of engine managed library on AOT platforms 2025-11-20 14:58:30 -08:00
Wojtek Figat
7c91c03adf Merge remote-tracking branch 'origin/master' 2025-11-20 06:26:09 -08:00
Wojtek Figat
caa902ea9b Fix shader compilation without HLSL 2021 on Vulkan 2025-11-20 13:47:23 +01:00
Wojtek Figat
fb07071e24 Restore Global Surface Atlas and DDGI on Apple platforms
#3797
2025-11-20 13:47:18 +01:00
Wojtek Figat
a1cb7dcbe7 Fix GPU BVH shader compilation for macOS/iOS 2025-11-20 13:47:11 +01:00
Wojtek Figat
032f698c7b Fix shader warning 2025-11-19 23:29:28 -08:00
Wojtek Figat
e2aaef9b88 Fix shader warning 2025-11-19 08:27:33 -08:00
90 changed files with 755 additions and 320 deletions

View File

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

View File

@@ -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.",

View File

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

View File

@@ -10,9 +10,10 @@
#include "Engine/Serialization/JsonTools.h"
#include "Engine/Serialization/JsonWriters.h"
#include "Editor/Cooker/PlatformTools.h"
#include "Engine/Engine/Globals.h"
#include "Editor/Editor.h"
#include "Editor/ProjectInfo.h"
#include "Engine/Engine/Globals.h"
#include "Editor/Utilities/EditorUtilities.h"
#if PLATFORM_MAC
#include <sys/stat.h>
#endif
@@ -127,7 +128,7 @@ bool CompileScriptsStep::DeployBinaries(CookingData& data, const String& path, c
const String dst = dstPath / StringUtils::GetFileName(file);
if (dst == file)
continue;
if (FileSystem::CopyFile(dst, file))
if (EditorUtilities::CopyFileIfNewer(dst, file))
{
data.Error(String::Format(TEXT("Failed to copy file from {0} to {1}."), file, dst));
return true;

View File

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

View File

@@ -59,6 +59,7 @@ bool PrecompileAssembliesStep::Perform(CookingData& data)
data.StepProgress(infoMsg, 0);
// Override Newtonsoft.Json with AOT-version (one that doesn't use System.Reflection.Emit)
// TODO: remove it since EngineModule does properly reference AOT lib now
EditorUtilities::CopyFileIfNewer(data.ManagedCodeOutputPath / TEXT("Newtonsoft.Json.dll"), Globals::StartupFolder / TEXT("Source/Platforms/DotNet/AOT/Newtonsoft.Json.dll"));
FileSystem::DeleteFile(data.ManagedCodeOutputPath / TEXT("Newtonsoft.Json.xml"));
FileSystem::DeleteFile(data.ManagedCodeOutputPath / TEXT("Newtonsoft.Json.pdb"));

View File

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

View File

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

View File

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

View File

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

View File

@@ -30,6 +30,7 @@
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Scripting/ManagedCLR/MClass.h"
#include "Engine/Scripting/Internal/InternalCalls.h"
#include "Engine/Scripting/Scripting.h"
#if USE_EDITOR
#include "Editor/Editor.h"
@@ -346,17 +347,21 @@ int32 LoadingThread::Run()
ContentLoadTask* task;
ThisLoadThread = this;
MONO_THREAD_INFO_TYPE* monoThreadInfo = nullptr;
while (Platform::AtomicRead(&_exitFlag) == 0)
{
if (LoadTasks.try_dequeue(task))
{
Run(task);
MONO_THREAD_INFO_GET(monoThreadInfo);
}
else
{
MONO_ENTER_GC_SAFE_WITH_INFO(monoThreadInfo);
LoadTasksMutex.Lock();
LoadTasksSignal.Wait(LoadTasksMutex);
LoadTasksMutex.Unlock();
MONO_EXIT_GC_SAFE_WITH_INFO;
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -79,10 +79,11 @@ namespace FlaxEngine.Interop
NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), NativeLibraryImportResolver);
// Change default culture to match with Mono runtime default culture
CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture;
CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.InvariantCulture;
System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
System.Threading.Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
var culture = CultureInfo.InvariantCulture;
CultureInfo.DefaultThreadCurrentCulture = culture;
CultureInfo.DefaultThreadCurrentUICulture = culture;
System.Threading.Thread.CurrentThread.CurrentCulture = culture;
System.Threading.Thread.CurrentThread.CurrentUICulture = culture;
}

View File

@@ -67,7 +67,7 @@ void GPUContext::FrameBegin()
void GPUContext::FrameEnd()
{
ClearState();
ResetState();
FlushState();
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -724,7 +724,7 @@ void GPUContextDX11::SetState(GPUPipelineState* state)
}
}
void GPUContextDX11::ClearState()
void GPUContextDX11::ResetState()
{
if (!_context)
return;

View File

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

View File

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

View File

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

View File

@@ -1304,7 +1304,7 @@ void GPUContextDX12::SetState(GPUPipelineState* state)
}
}
void GPUContextDX12::ClearState()
void GPUContextDX12::ResetState()
{
if (!_commandList)
return;

View File

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

View File

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

View File

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

View File

@@ -177,7 +177,7 @@ public:
{
}
void ClearState() override
void ResetState() override
{
}

View File

@@ -1329,7 +1329,7 @@ void GPUContextVulkan::SetState(GPUPipelineState* state)
}
}
void GPUContextVulkan::ClearState()
void GPUContextVulkan::ResetState()
{
ResetRenderTarget();
ResetSR();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -422,24 +422,8 @@ bool AndroidFileSystem::getFilesFromDirectoryTop(Array<String>& results, const c
if (S_ISREG(statEntry.st_mode) != 0)
{
// Validate with filter
const int32 fullPathLength = StringUtils::Length(fullPath);
const int32 searchPatternLength = StringUtils::Length(searchPattern);
if (searchPatternLength == 0 || StringUtils::Compare(searchPattern, "*") == 0)
{
// All files
}
else if (searchPattern[0] == '*' && searchPatternLength < fullPathLength && StringUtils::Compare(fullPath + fullPathLength - searchPatternLength + 1, searchPattern + 1) == 0)
{
// Path ending
}
else
{
// TODO: implement all cases in a generic way
continue;
}
// Add file
results.Add(String(fullPath));
if (FileSystem::PathFilterHelper(fullPath, searchPattern))
results.Add(String(fullPath));
}
}

View File

@@ -7,6 +7,7 @@
#include "Engine/Core/Types/StringView.h"
#include "Engine/Core/Collections/Array.h"
#include "Engine/Core/Math/Math.h"
#include "Engine/Core/Log.h"
#include "Engine/Engine/Globals.h"
bool FileSystemBase::ShowOpenFileDialog(Window* parentWindow, const StringView& initialDirectory, const StringView& filter, bool multiSelect, const StringView& title, Array<String, HeapAllocation>& filenames)
@@ -313,3 +314,39 @@ bool FileSystemBase::DirectoryCopyHelper(const String& dst, const String& src, b
return false;
}
bool FileSystemBase::PathFilterHelper(const char* path, const char* searchPattern)
{
// Validate with filter
const int32 pathLength = StringUtils::Length(path);
const int32 searchPatternLength = StringUtils::Length(searchPattern);
if (searchPatternLength == 0 ||
StringUtils::Compare(searchPattern, "*") == 0 ||
StringUtils::Compare(searchPattern, "*.*") == 0)
{
// All files
return true;
}
else if (searchPattern[0] == '*' && StringUtils::Find(searchPattern + 1, "*") == nullptr)
{
// Path ending
return searchPatternLength < pathLength && StringUtils::Compare(path + pathLength - searchPatternLength + 1, searchPattern + 1, searchPatternLength - 1) == 0;
}
else if (searchPattern[0] == '*' && searchPatternLength > 2 && searchPattern[searchPatternLength - 1] == '*')
{
// Contains pattern
bool match = false;
for (int32 i = 0; i < pathLength - searchPatternLength - 1; i++)
{
int32 len = Math::Min(searchPatternLength - 2, pathLength - i);
if (StringUtils::Compare(&path[i], &searchPattern[1], len) == 0)
return true;
}
}
else
{
// TODO: implement all cases in a generic way
LOG(Warning, "DirectoryGetFiles: Wildcard filter is not implemented ({})", String(searchPattern));
}
return false;
}

View File

@@ -284,6 +284,6 @@ public:
/// <returns>Relative path</returns>
static String ConvertAbsolutePathToRelative(const String& basePath, const String& path);
private:
static bool DirectoryCopyHelper(const String& dst, const String& src, bool withSubDirectories);
static bool PathFilterHelper(const char* path, const char* searchPattern);
};

View File

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

View File

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

View File

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

View File

@@ -367,43 +367,8 @@ bool UnixFileSystem::getFilesFromDirectoryTop(Array<String>& results, const char
if (S_ISREG(statEntry.st_mode) != 0)
{
// Validate with filter
const int32 fullPathLength = StringUtils::Length(fullPath);
const int32 searchPatternLength = StringUtils::Length(searchPattern);
if (searchPatternLength == 0 ||
StringUtils::Compare(searchPattern, "*") == 0 ||
StringUtils::Compare(searchPattern, "*.*") == 0)
{
// All files
}
else if (searchPattern[0] == '*' && searchPatternLength < fullPathLength && StringUtils::Compare(fullPath + fullPathLength - searchPatternLength + 1, searchPattern + 1, searchPatternLength - 1) == 0)
{
// Path ending
}
else if (searchPattern[0] == '*' && searchPatternLength > 2 && searchPattern[searchPatternLength-1] == '*')
{
// Contains pattern
bool match = false;
for (int32 i = 0; i < pathLength - searchPatternLength - 1; i++)
{
int32 len = Math::Min(searchPatternLength - 2, pathLength - i);
if (StringUtils::Compare(&entry->d_name[i], &searchPattern[1], len) == 0)
{
match = true;
break;
}
}
if (!match)
continue;
}
else
{
// TODO: implement all cases in a generic way
LOG(Warning, "DirectoryGetFiles: Wildcard filter is not implemented");
continue;
}
// Add file
results.Add(String(fullPath));
if (FileSystemBase::PathFilterHelper(fullPath, searchPattern))
results.Add(String(fullPath));
}
}

View File

@@ -52,9 +52,12 @@ private:
void OnShaderReloading(Asset* obj)
{
_psDofDepthBlurGeneration->ReleaseGPU();
_psBokehGeneration->ReleaseGPU();
_psBokeh->ReleaseGPU();
_psBokehComposite->ReleaseGPU();
if (_psBokehGeneration)
_psBokehGeneration->ReleaseGPU();
if (_psBokeh)
_psBokeh->ReleaseGPU();
if (_psBokehComposite)
_psBokehComposite->ReleaseGPU();
invalidateResources();
}
#endif

View File

@@ -551,9 +551,6 @@ bool GlobalSurfaceAtlasPass::Init()
// Check platform support
const auto device = GPUDevice::Instance;
_supported = device->GetFeatureLevel() >= FeatureLevel::SM5 && device->Limits.HasCompute && device->Limits.HasTypedUAVLoad;
#if PLATFORM_APPLE_FAMILY
_supported = false; // Vulkan over Metal has some issues in complex scenes with DDGI
#endif
return false;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -9,7 +9,7 @@
#if defined(__clang__)
// Helper utility to override vtable entry with automatic restore
// See BindingsGenerator.Cpp.cs that generates virtuall method wrappers for scripting to properly call overriden base method
// See BindingsGenerator.Cpp.cs that generates virtual method wrappers for scripting to properly call overriden base method
struct FLAXENGINE_API VTableFunctionInjector
{
void** VTableAddr;
@@ -100,3 +100,50 @@ T& InternalGetReference(T* obj)
DebugLog::ThrowNullReference();
return *obj;
}
#ifdef USE_MONO_AOT_COOP
// Cooperative Suspend - where threads suspend themselves when the runtime requests it.
// https://www.mono-project.com/docs/advanced/runtime/docs/coop-suspend/
typedef struct _MonoStackData {
void* stackpointer;
const char* function_name;
} MonoStackData;
#if BUILD_DEBUG
#define MONO_STACKDATA(x) MonoStackData x = { &x, __func__ }
#else
#define MONO_STACKDATA(x) MonoStackData x = { &x, NULL }
#endif
#define MONO_THREAD_INFO_TYPE struct MonoThreadInfo
DLLIMPORT extern "C" MONO_THREAD_INFO_TYPE* mono_thread_info_attach(void);
DLLIMPORT extern "C" void* mono_threads_enter_gc_safe_region_with_info(MONO_THREAD_INFO_TYPE* info, MonoStackData* stackdata);
DLLIMPORT extern "C" void mono_threads_exit_gc_safe_region_internal(void* cookie, MonoStackData* stackdata);
#ifndef _MONO_UTILS_FORWARD_
typedef struct _MonoDomain MonoDomain;
DLLIMPORT extern "C" MonoDomain* mono_domain_get(void);
#endif
#define MONO_ENTER_GC_SAFE \
do { \
MONO_STACKDATA(__gc_safe_dummy); \
void* __gc_safe_cookie = mono_threads_enter_gc_safe_region_internal(&__gc_safe_dummy)
#define MONO_EXIT_GC_SAFE \
mono_threads_exit_gc_safe_region_internal(__gc_safe_cookie, &__gc_safe_dummy); \
} while (0)
#define MONO_ENTER_GC_SAFE_WITH_INFO(info) \
do { \
MONO_STACKDATA(__gc_safe_dummy); \
void* __gc_safe_cookie = mono_threads_enter_gc_safe_region_with_info((info), &__gc_safe_dummy)
#define MONO_EXIT_GC_SAFE_WITH_INFO MONO_EXIT_GC_SAFE
#define MONO_THREAD_INFO_GET(info) if (!info && mono_domain_get()) info = mono_thread_info_attach()
#else
#define MONO_THREAD_INFO_TYPE void
#define MONO_ENTER_GC_SAFE
#define MONO_EXIT_GC_SAFE
#define MONO_ENTER_GC_SAFE_WITH_INFO(info)
#define MONO_EXIT_GC_SAFE_WITH_INFO
#define MONO_THREAD_INFO_GET(info)
#define mono_thread_info_attach() nullptr
#endif

View File

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

View File

@@ -568,10 +568,14 @@ bool Scripting::Load()
#endif
// Load FlaxEngine
const String flaxEnginePath = Globals::BinariesFolder / TEXT("FlaxEngine.CSharp.dll");
auto* flaxEngineModule = (NativeBinaryModule*)GetBinaryModuleFlaxEngine();
if (!flaxEngineModule->Assembly->IsLoaded())
{
String flaxEnginePath = Globals::BinariesFolder / TEXT("FlaxEngine.CSharp.dll");
#if USE_MONO_AOT
if (!FileSystem::FileExists(flaxEnginePath))
flaxEnginePath = Globals::BinariesFolder / TEXT("Dotnet") / TEXT("FlaxEngine.CSharp.dll");
#endif
if (flaxEngineModule->Assembly->Load(flaxEnginePath))
{
LOG(Error, "Failed to load FlaxEngine C# assembly.");

View File

@@ -198,7 +198,7 @@ namespace FlaxEngine
private static void OnLocalizationChanged()
{
// Invariant-globalization only (see InitHostfxr with Mono)
#if !(PLATFORM_IOS || PLATFORM_SWITCH)
#if !(PLATFORM_IOS || PLATFORM_SWITCH || PLATFORM_PS4 || PLATFORM_PS5)
var currentThread = Thread.CurrentThread;
var language = Localization.CurrentLanguage;
if (language != null)
@@ -261,7 +261,7 @@ namespace FlaxEngine
internal static ManagedHandle CultureInfoToManaged(int lcid)
{
#if PLATFORM_IOS || PLATFORM_SWITCH
#if PLATFORM_IOS || PLATFORM_SWITCH || PLATFORM_PS4 || PLATFORM_PS5
// Invariant-globalization only (see InitHostfxr with Mono)
lcid = 0;
#endif

View File

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

View File

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

View File

@@ -15,6 +15,7 @@ private:
Array<char> _funcNameDefineBuffer;
void* _compiler;
void* _library;
void* _builder;
void* _containerReflection;
public:

View File

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

View File

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

View File

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

View File

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

View File

@@ -15,6 +15,7 @@
#include "Engine/Profiler/ProfilerMemory.h"
#if USE_CSHARP
#include "Engine/Scripting/ManagedCLR/MCore.h"
#include "Engine/Scripting/Internal/InternalCalls.h"
#endif
#define JOB_SYSTEM_ENABLED 1
@@ -184,6 +185,7 @@ int32 JobSystemThread::Run()
JobData data;
Function<void(int32)> job;
bool attachCSharpThread = true;
MONO_THREAD_INFO_TYPE* monoThreadInfo = nullptr;
while (Platform::AtomicRead(&ExitFlag) == 0)
{
// Try to get a job
@@ -205,6 +207,7 @@ int32 JobSystemThread::Run()
{
MCore::Thread::Attach();
attachCSharpThread = false;
monoThreadInfo = mono_thread_info_attach();
}
#endif
@@ -244,9 +247,11 @@ int32 JobSystemThread::Run()
else
{
// Wait for signal
MONO_ENTER_GC_SAFE_WITH_INFO(monoThreadInfo);
JobsMutex.Lock();
JobsSignal.Wait(JobsMutex);
JobsMutex.Unlock();
MONO_EXIT_GC_SAFE_WITH_INFO;
}
}
return 0;

View File

@@ -15,6 +15,7 @@
#include "Engine/Platform/CPUInfo.h"
#include "Engine/Platform/Thread.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Scripting/Internal/InternalCalls.h"
FLAXENGINE_API bool IsInMainThread()
{
@@ -117,6 +118,7 @@ int32 ThreadPool::ThreadProc()
Platform::SetThreadAffinityMask(THREAD_POOL_AFFINITY_MASK((int32)index));
#endif
ThreadPoolTask* task;
MONO_THREAD_INFO_TYPE* monoThreadInfo = nullptr;
// Work until end
while (Platform::AtomicRead(&ThreadPoolImpl::ExitFlag) == 0)
@@ -125,12 +127,15 @@ int32 ThreadPool::ThreadProc()
if (ThreadPoolImpl::Jobs.try_dequeue(task))
{
task->Execute();
MONO_THREAD_INFO_GET(monoThreadInfo);
}
else
{
MONO_ENTER_GC_SAFE_WITH_INFO(monoThreadInfo);
ThreadPoolImpl::JobsMutex.Lock();
ThreadPoolImpl::JobsSignal.Wait(ThreadPoolImpl::JobsMutex);
ThreadPoolImpl::JobsMutex.Unlock();
MONO_EXIT_GC_SAFE_WITH_INFO;
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
// Copyright (c) Wojciech Figat. All rights reserved.
package com.flaxengine;

View File

@@ -329,7 +329,7 @@ float3 GetInscatteredLight(AtmosphericFogData atmosphericFog, in float3 viewPosi
if (intersectAtmosphere(viewPosition, viewDir, offset, maxPathLength))
{
return float3(offset / 10, 0, 0);
#if 0
float pathLength = distance(viewPosition, surfacePos);
//return pathLength.xxx;
@@ -413,6 +413,7 @@ float3 GetInscatteredLight(AtmosphericFogData atmosphericFog, in float3 viewPosi
float sunIntensity = 10;
inscatteredLight *= sunIntensity;
}
#endif
}
return inscatteredLight;
@@ -420,8 +421,6 @@ float3 GetInscatteredLight(AtmosphericFogData atmosphericFog, in float3 viewPosi
float4 GetAtmosphericFog(AtmosphericFogData atmosphericFog, float viewFar, float3 viewPosition, float3 viewVector, float sceneDepth, float3 sceneColor)
{
float4 result = float4(1, 0, 0, 1);
#if 0
float scale = 0.00001f * atmosphericFog.AtmosphericFogDistanceScale;// convert cm to km
@@ -536,8 +535,6 @@ float4 GetAtmosphericFog(AtmosphericFogData atmosphericFog, float viewFar, float
//return float4(sun + groundColor + inscatterColor, 1);
#endif
return result;
}
float4 GetAtmosphericFog(AtmosphericFogData atmosphericFog, float viewFar, float3 worldPosition, float3 cameraPosition)

View File

@@ -96,34 +96,9 @@
// Compiler support for HLSL 2021 that is stricter (need to use or/and/select for vector-based logical operators)
#if !defined(__DXC_VERSION_MAJOR) || (__DXC_VERSION_MAJOR <= 1 && __DXC_VERSION_MINOR < 7)
bool InternalAnd(bool a, bool b) { return bool(a && b); }
bool2 InternalAnd(bool2 a, bool2 b) { return bool2(a.x && b.x, a.y && b.y); }
bool3 InternalAnd(bool3 a, bool3 b) { return bool3(a.x && b.x, a.y && b.y, a.z && b.z); }
bool4 InternalAnd(bool4 a, bool4 b) { return bool4(a.x && b.x, a.y && b.y, a.z && b.z, a.w && b.w); }
bool InternalOr(bool a, bool b) { return bool(a || b); }
bool2 InternalOr(bool2 a, bool2 b) { return bool2(a.x || b.x, a.y || b.y); }
bool3 InternalOr(bool3 a, bool3 b) { return bool3(a.x || b.x, a.y || b.y, a.z || b.z); }
bool4 InternalOr(bool4 a, bool4 b) { return bool4(a.x || b.x, a.y || b.y, a.z || b.z, a.w || b.w); }
#define SELECT_INTERNAL(type) \
type InternalSelect(bool c, type a, type b) { return type (c ? a.x : b.x); } \
type##2 InternalSelect(bool c, type##2 a, type##2 b) { return type##2(c ? a.x : b.x, c ? a.y : b.y); } \
type##2 InternalSelect(bool2 c, type a, type b) { return type##2(c.x ? a : b, c.y ? a : b); } \
type##2 InternalSelect(bool2 c, type##2 a, type##2 b) { return type##2(c.x ? a.x : b.x, c.y ? a.y : b.y); } \
type##3 InternalSelect(bool c, type##3 a, type##3 b) { return type##3(c ? a.x : b.x, c ? a.y : b.y, c ? a.z : b.z); } \
type##3 InternalSelect(bool3 c, type a, type b) { return type##3(c.x ? a : b, c.y ? a : b, c.z ? a : b); } \
type##3 InternalSelect(bool3 c, type##3 a, type##3 b) { return type##3(c.x ? a.x : b.x, c.y ? a.y : b.y, c.z ? a.z : b.z); } \
type##4 InternalSelect(bool c, type##4 a, type##4 b) { return type##4(c ? a.x : b.x, c ? a.y : b.y, c ? a.z : b.z, c ? a.w : b.w); } \
type##4 InternalSelect(bool4 c, type a, type b) { return type##4(c.x ? a : b, c.y ? a : b, c.z ? a : b, c.w ? a : b); } \
type##4 InternalSelect(bool4 c, type##4 a, type##4 b) { return type##4(c.x ? a.x : b.x, c.y ? a.y : b.y, c.z ? a.z : b.z, c.w ? a.w : b.w); }
SELECT_INTERNAL(uint)
SELECT_INTERNAL(float)
#undef SELECT_INTERNAL
#define and(a, b) InternalAnd(a, b)
#define or(a, b) InternalOr(a, b)
#define select(c, a, b) InternalSelect(c, a, b)
#define and(a, b) (a) && (b)
#define or(a, b) (a) || (b)
#define select(c, a, b) (c) ? (a) : (b)
#endif

View File

@@ -311,7 +311,7 @@ float3 AOMultiBounce(float visibility, float3 albedo)
float2 Flipbook(float2 uv, float frame, float2 sizeXY, float2 flipXY = 0.0f)
{
float tile = (int)fmod(frame, sizeXY.x * sizeXY.y);
float tile = (float)(int)fmod(frame, sizeXY.x * sizeXY.y);
float2 tileCount = float2(1.0, 1.0) / sizeXY;
float tileY = abs(flipXY.y * sizeXY.y - (floor(tile * tileCount.x) + flipXY.y * 1));
float tileX = abs(flipXY.x * sizeXY.x - ((tile - sizeXY.x * floor(tile * tileCount.x)) + flipXY.x * 1));

View File

@@ -16,13 +16,10 @@ struct BVHNode
int Count; // Negative for non-leaf nodes
};
struct BVHBuffers
{
StructuredBuffer<BVHNode> BVHBuffer;
ByteAddressBuffer VertexBuffer;
ByteAddressBuffer IndexBuffer;
uint VertexStride;
};
// Pass all data via separate params (SPIR-V doesn't support buffers in structures)
#define BVHBuffers_Param StructuredBuffer<BVHNode> BVHBuffer, ByteAddressBuffer VertexBuffer, ByteAddressBuffer IndexBuffer, uint VertexStride
#define BVHBuffers_Init(BVHBuffer, VertexBuffer, IndexBuffer, VertexStride) BVHBuffer, VertexBuffer, IndexBuffer, VertexStride
#define BVHBuffers_Pass BVHBuffers_Init(BVHBuffer, VertexBuffer, IndexBuffer, VertexStride)
struct BVHHit
{
@@ -30,11 +27,11 @@ struct BVHHit
bool IsBackface;
};
float3 LoadVertexBVH(BVHBuffers bvh, uint index)
float3 LoadVertexBVH(BVHBuffers_Param, uint index)
{
int addr = index << 2u;
uint vertexIndex = bvh.IndexBuffer.Load(addr);
return asfloat(bvh.VertexBuffer.Load3(vertexIndex * bvh.VertexStride));
uint vertexIndex = IndexBuffer.Load(addr);
return asfloat(VertexBuffer.Load3(vertexIndex * VertexStride));
}
// [https://tavianator.com/2011/ray_box.html]
@@ -52,7 +49,7 @@ float RayTestBoxBVH(float3 rayPos, float3 rayDir, float3 boxMin, float3 boxMax)
}
// Performs raytracing against the BVH acceleration structure to find the closest intersection with a triangle.
bool RayCastBVH(BVHBuffers bvh, float3 rayPos, float3 rayDir, out BVHHit hit, float maxDistance = 1000000.0f)
bool RayCastBVH(BVHBuffers_Param, float3 rayPos, float3 rayDir, out BVHHit hit, float maxDistance = 1000000.0f)
{
hit = (BVHHit)0;
hit.Distance = maxDistance;
@@ -66,7 +63,7 @@ bool RayCastBVH(BVHBuffers bvh, float3 rayPos, float3 rayDir, out BVHHit hit, fl
LOOP
while (stackCount > 0)
{
BVHNode node = bvh.BVHBuffer[stack[--stackCount]];
BVHNode node = BVHBuffer[stack[--stackCount]];
// Raytrace bounds
float boundsHit = RayTestBoxBVH(rayPos, rayDir, node.BoundsMin, node.BoundsMax);
@@ -82,9 +79,9 @@ bool RayCastBVH(BVHBuffers bvh, float3 rayPos, float3 rayDir, out BVHHit hit, fl
for (uint i = indexStart; i < indexEnd;)
{
// Load triangle
float3 v0 = LoadVertexBVH(bvh, i++);
float3 v1 = LoadVertexBVH(bvh, i++);
float3 v2 = LoadVertexBVH(bvh, i++);
float3 v0 = LoadVertexBVH(BVHBuffers_Pass, i++);
float3 v1 = LoadVertexBVH(BVHBuffers_Pass, i++);
float3 v2 = LoadVertexBVH(BVHBuffers_Pass, i++);
// Raytrace triangle
float distance;
@@ -109,7 +106,7 @@ bool RayCastBVH(BVHBuffers bvh, float3 rayPos, float3 rayDir, out BVHHit hit, fl
}
// Performs a query against the BVH acceleration structure to find the closest distance to a triangle from a given point.
bool PointQueryBVH(BVHBuffers bvh, float3 pos, out BVHHit hit, float maxDistance = 1000000.0f)
bool PointQueryBVH(BVHBuffers_Param, float3 pos, out BVHHit hit, float maxDistance = 1000000.0f)
{
hit = (BVHHit)0;
hit.Distance = maxDistance;
@@ -123,7 +120,7 @@ bool PointQueryBVH(BVHBuffers bvh, float3 pos, out BVHHit hit, float maxDistance
LOOP
while (stackCount > 0)
{
BVHNode node = bvh.BVHBuffer[stack[--stackCount]];
BVHNode node = BVHBuffer[stack[--stackCount]];
// Skip too far nodes
if (PointDistanceBox(node.BoundsMin, node.BoundsMax, pos) >= hit.Distance)
@@ -138,9 +135,9 @@ bool PointQueryBVH(BVHBuffers bvh, float3 pos, out BVHHit hit, float maxDistance
for (uint i = indexStart; i < indexEnd;)
{
// Load triangle
float3 v0 = LoadVertexBVH(bvh, i++);
float3 v1 = LoadVertexBVH(bvh, i++);
float3 v2 = LoadVertexBVH(bvh, i++);
float3 v0 = LoadVertexBVH(BVHBuffers_Pass, i++);
float3 v1 = LoadVertexBVH(BVHBuffers_Pass, i++);
float3 v2 = LoadVertexBVH(BVHBuffers_Pass, i++);
// Check triangle
float distance = sqrt(DistancePointToTriangle2(pos, v0, v1, v2));

View File

@@ -77,15 +77,9 @@ void CS_RasterizeTriangles(uint3 GroupId : SV_GroupID, uint3 GroupThreadID : SV_
int3 voxelCoord = GetVoxelCoord(voxelIndex);
float3 voxelPos = GetVoxelPos(voxelCoord);
BVHBuffers bvh;
bvh.BVHBuffer = BVHBuffer;
bvh.VertexBuffer = VertexBuffer;
bvh.IndexBuffer = IndexBuffer;
bvh.VertexStride = VertexStride;
// Point query to find the distance to the closest surface
BVHHit hit;
PointQueryBVH(bvh, voxelPos, hit, MaxDistance);
PointQueryBVH(BVHBuffers_Init(BVHBuffer, VertexBuffer, IndexBuffer, VertexStride), voxelPos, hit, MaxDistance);
float sdf = hit.Distance;
// Raycast triangles around voxel to count triangle backfaces hit
@@ -104,7 +98,7 @@ void CS_RasterizeTriangles(uint3 GroupId : SV_GroupID, uint3 GroupThreadID : SV_
for (uint i = 0; i < CLOSEST_CACHE_SIZE; i++)
{
float3 rayDir = closestDirections[i];
if (RayCastBVH(bvh, voxelPos, rayDir, hit, MaxDistance))
if (RayCastBVH(BVHBuffers_Init(BVHBuffer, VertexBuffer, IndexBuffer, VertexStride), voxelPos, rayDir, hit, MaxDistance))
{
sdf = min(sdf, hit.Distance);
if (hit.IsBackface)

View File

@@ -227,7 +227,8 @@ ShadowSample SampleDirectionalLightShadowCascade(LightData light, Buffer<float4>
// Increase the sharpness for higher cascades to match the filter radius
const float SharpnessScale[MaxNumCascades] = { 1.0f, 1.5f, 3.0f, 3.5f };
shadow.Sharpness *= SharpnessScale[cascadeIndex];
result.TransmissionShadow = 1;
#if defined(USE_GBUFFER_CUSTOM_DATA)
// Subsurface shadowing
BRANCH
@@ -239,8 +240,6 @@ ShadowSample SampleDirectionalLightShadowCascade(LightData light, Buffer<float4>
result.TransmissionShadow = CalculateSubsurfaceOcclusion(opacity, shadowPosition.z, shadowMapDepth);
result.TransmissionShadow = PostProcessShadow(shadow, result.TransmissionShadow);
}
#else
result.TransmissionShadow = 1;
#endif
result.SurfaceShadow = PostProcessShadow(shadow, result.SurfaceShadow);

View File

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

View File

@@ -462,7 +462,7 @@ namespace Flax.Build
else
{
// Copy to the destination folder
Utilities.FileCopy(assemblyPath, Path.Combine(dotnetOutputPath, assemblyFileName));
Utilities.FileCopy(assemblyPath, Path.Combine(dotnetOutputPath, assemblyFileName), Utilities.CopyMode.OverrideIfNewer);
}
};
if (Configuration.MaxConcurrency > 1 && Configuration.ConcurrencyProcessorScale > 0.0f && !dotnetAotDebug)

View File

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

View File

@@ -42,7 +42,8 @@ namespace Flax.Build
BinaryModuleName = "FlaxEngine";
options.ScriptingAPI.Defines.Add("FLAX");
options.ScriptingAPI.Defines.Add("FLAX_ASSERTIONS");
options.ScriptingAPI.FileReferences.Add(Utilities.RemovePathRelativeParts(Path.Combine(Globals.EngineRoot, "Source", "Platforms", "DotNet", "Newtonsoft.Json.dll")));
var newtonsoftJsonPath = options.Platform?.HasDynamicCodeExecutionSupport ?? true ? "Newtonsoft.Json.dll" : "AOT/Newtonsoft.Json.dll";
options.ScriptingAPI.FileReferences.Add(Utilities.RemovePathRelativeParts(Path.Combine(Globals.EngineRoot, "Source", "Platforms", "DotNet", newtonsoftJsonPath)));
options.ScriptingAPI.SystemReferences.Add("System.ComponentModel.TypeConverter");
}
}

View File

@@ -142,7 +142,7 @@ namespace Flax.Build
}
/// <summary>
/// Returns true if this build target should use separate (aka main-only) executable file and separate runtime (in shared library). Used on platforms that don't support linking again executable file but only shared library (see HasExecutableFileReferenceSupport).
/// Returns true if this build target should use separate (aka main-only) executable file and separate runtime (in shared library). Used on platforms that don't support linking (or symbol lookup) against executable file but only shared library (see HasExecutableFileReferenceSupport).
/// </summary>
public virtual bool UseSeparateMainExecutable(BuildOptions buildOptions)
{
@@ -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);

View File

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

View File

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

View File

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

View File

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

View File

@@ -144,24 +144,51 @@ namespace Flax.Build
return new TwoWayEnumerator<T>(source.GetEnumerator());
}
/// <summary>
/// File copy modes.
/// </summary>
public enum CopyMode
{
/// <summary>
/// Copies the file to the destination, fails if it already exists.
/// </summary>
New,
/// <summary>
/// If destination file exists, it will be overriden.
/// </summary>
OverrideIfExists,
/// <summary>
/// If destination file exists, has the same size and is newer than source file, it won't be overriden (avoids unnecessary copies).
/// </summary>
OverrideIfNewer,
}
/// <summary>
/// Copies the file.
/// </summary>
/// <param name="srcFilePath">The source file path.</param>
/// <param name="dstFilePath">The destination file path.</param>
/// <param name="overwrite"><see langword="true" /> if the destination file can be overwritten; otherwise, <see langword="false" />.</param>
public static void FileCopy(string srcFilePath, string dstFilePath, bool overwrite = true)
/// <param name="mode">Copy operation modes.</param>
public static void FileCopy(string srcFilePath, string dstFilePath, CopyMode mode = CopyMode.OverrideIfExists)
{
if (string.IsNullOrEmpty(srcFilePath))
throw new ArgumentNullException(nameof(srcFilePath));
if (string.IsNullOrEmpty(dstFilePath))
throw new ArgumentNullException(nameof(dstFilePath));
if (mode == CopyMode.OverrideIfNewer &&
File.Exists(dstFilePath) &&
File.GetLastWriteTime(srcFilePath) <= File.GetLastWriteTime(dstFilePath) &&
new FileInfo(dstFilePath).Length == new FileInfo(srcFilePath).Length)
return;
Log.Verbose(srcFilePath + " -> " + dstFilePath);
try
{
File.Copy(srcFilePath, dstFilePath, overwrite);
File.Copy(srcFilePath, dstFilePath, mode != CopyMode.New);
}
catch (Exception ex)
{
@@ -173,6 +200,17 @@ namespace Flax.Build
}
}
/// <summary>
/// Copies the file.
/// </summary>
/// <param name="srcFilePath">The source file path.</param>
/// <param name="dstFilePath">The destination file path.</param>
/// <param name="overwrite"><see langword="true" /> if the destination file can be overwritten; otherwise, <see langword="false" />.</param>
public static void FileCopy(string srcFilePath, string dstFilePath, bool overwrite)
{
FileCopy(srcFilePath, dstFilePath, overwrite ? CopyMode.OverrideIfExists : CopyMode.New);
}
/// <summary>
/// Copies the directories.
/// </summary>