diff --git a/Content/Engine/DefaultTerrainMaterial.flax b/Content/Engine/DefaultTerrainMaterial.flax
index 9d956c6ab..355158581 100644
--- a/Content/Engine/DefaultTerrainMaterial.flax
+++ b/Content/Engine/DefaultTerrainMaterial.flax
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:876072ac78d8f346101e45a2b7f52f759bc1e437ccefe1a79ee4fbad68b2c04f
+oid sha256:cce61627c31b12d2d79abf59c485889044b4b9a803c0d2b7a988dc25906d6a35
size 30264
diff --git a/Source/Editor/Cooker/CookingData.h b/Source/Editor/Cooker/CookingData.h
index 06e48cc95..51df2a1ac 100644
--- a/Source/Editor/Cooker/CookingData.h
+++ b/Source/Editor/Cooker/CookingData.h
@@ -19,6 +19,10 @@ class PlatformTools;
#define GAME_BUILD_DOTNET_VER TEXT("")
#endif
+// Range of dotnet runtime versions
+#define GAME_BUILD_DOTNET_RUNTIME_MIN_VER 8
+#define GAME_BUILD_DOTNET_RUNTIME_MAX_VER 9
+
///
/// Game building options. Used as flags.
///
diff --git a/Source/Editor/Cooker/Steps/DeployDataStep.cpp b/Source/Editor/Cooker/Steps/DeployDataStep.cpp
index ff22247ef..6b1a6829e 100644
--- a/Source/Editor/Cooker/Steps/DeployDataStep.cpp
+++ b/Source/Editor/Cooker/Steps/DeployDataStep.cpp
@@ -117,7 +117,11 @@ bool DeployDataStep::Perform(CookingData& data)
for (String& version : versions)
{
version = String(StringUtils::GetFileName(version));
- if (!version.StartsWith(TEXT("8."))) // Check for major part of 8.0
+ const int32 dot = version.Find('.');
+ int majorVersion = 0;
+ if (dot != -1)
+ StringUtils::Parse(version.Substring(0, dot).Get(), &majorVersion);
+ if (majorVersion < GAME_BUILD_DOTNET_RUNTIME_MIN_VER || majorVersion > GAME_BUILD_DOTNET_RUNTIME_MAX_VER) // Check for major part
version.Clear();
}
Sorting::QuickSort(versions);
diff --git a/Source/Editor/Modules/SourceCodeEditing/CodeDocsModule.cs b/Source/Editor/Modules/SourceCodeEditing/CodeDocsModule.cs
index a26fe9924..311767caf 100644
--- a/Source/Editor/Modules/SourceCodeEditing/CodeDocsModule.cs
+++ b/Source/Editor/Modules/SourceCodeEditing/CodeDocsModule.cs
@@ -293,7 +293,7 @@ namespace FlaxEditor.Modules.SourceCodeEditing
var assemblyPath = Utils.GetAssemblyLocation(assembly);
var assemblyName = assembly.GetName().Name;
var xmlFilePath = Path.ChangeExtension(assemblyPath, ".xml");
- if (!File.Exists(assemblyPath))
+ if (!File.Exists(assemblyPath) && !string.IsNullOrEmpty(assemblyPath))
{
var uri = new UriBuilder(assemblyPath);
var path = Uri.UnescapeDataString(uri.Path);
diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs
index b3f9ce05a..928c02dcc 100644
--- a/Source/Editor/Modules/UIModule.cs
+++ b/Source/Editor/Modules/UIModule.cs
@@ -540,6 +540,7 @@ namespace FlaxEditor.Modules
cm.AddSeparator();
cm.AddButton("Open project...", OpenProject);
cm.AddButton("Reload project", ReloadProject);
+ cm.AddButton("Open project folder", () => FileSystem.ShowFileExplorer(Editor.Instance.GameProject.ProjectFolderPath));
cm.AddSeparator();
cm.AddButton("Exit", "Alt+F4", () => Editor.Windows.MainWindow.Close(ClosingReason.User));
diff --git a/Source/Editor/Scripting/TypeUtils.cs b/Source/Editor/Scripting/TypeUtils.cs
index 5d91f7c6a..7ed8db187 100644
--- a/Source/Editor/Scripting/TypeUtils.cs
+++ b/Source/Editor/Scripting/TypeUtils.cs
@@ -41,6 +41,8 @@ namespace FlaxEngine.Utilities
public static string GetTypeDisplayName(this Type type)
{
// Special display for in-built basic types
+ if (type == null)
+ return "Null";
if (type == typeof(bool))
return "Bool";
if (type == typeof(float))
diff --git a/Source/Editor/Windows/Assets/BehaviorTreeWindow.cs b/Source/Editor/Windows/Assets/BehaviorTreeWindow.cs
index f4e7a4f6a..3d6399365 100644
--- a/Source/Editor/Windows/Assets/BehaviorTreeWindow.cs
+++ b/Source/Editor/Windows/Assets/BehaviorTreeWindow.cs
@@ -288,7 +288,11 @@ namespace FlaxEditor.Windows.Assets
// Use blackboard from the root node
var rootNode = _surface.FindNode(19, 2) as Surface.Archetypes.BehaviorTree.Node;
if (rootNode != null)
+ {
+ rootNode.ValuesChanged -= UpdateKnowledge;
rootNode.ValuesChanged += UpdateKnowledge;
+ }
+
var rootInstance = rootNode?.Instance as BehaviorTreeRootNode;
var blackboardType = TypeUtils.GetType(rootInstance?.BlackboardType);
if (blackboardType)
diff --git a/Source/Engine/Core/Collections/BitArray.h b/Source/Engine/Core/Collections/BitArray.h
index 1b601c72f..0a748f4bd 100644
--- a/Source/Engine/Core/Collections/BitArray.h
+++ b/Source/Engine/Core/Collections/BitArray.h
@@ -90,12 +90,10 @@ public:
///
/// The other collection to move.
FORCE_INLINE BitArray(BitArray&& other) noexcept
+ : _count(0)
+ , _capacity(0)
{
- _count = other._count;
- _capacity = other._capacity;
- other._count = 0;
- other._capacity = 0;
- _allocation.Swap(other._allocation);
+ Swap(other);
}
///
@@ -130,11 +128,9 @@ public:
if (this != &other)
{
_allocation.Free();
- _count = other._count;
- _capacity = other._capacity;
- other._count = 0;
- other._capacity = 0;
- _allocation.Swap(other._allocation);
+ _count = 0;
+ _capacity = 0;
+ Swap(other);
}
return *this;
}
@@ -337,9 +333,38 @@ public:
/// The other collection.
void Swap(BitArray& other)
{
+ if IF_CONSTEXPR (AllocationType::HasSwap)
+ _allocation.Swap(other._allocation);
+ else
+ {
+ // Move to temp
+ const int32 oldItemsCapacity = ToItemCount(_capacity);
+ const int32 otherItemsCapacity = ToItemCount(other._capacity);
+ AllocationData oldAllocation;
+ if (oldItemsCapacity)
+ {
+ oldAllocation.Allocate(oldItemsCapacity);
+ Memory::MoveItems(oldAllocation.Get(), _allocation.Get(), oldItemsCapacity);
+ _allocation.Free();
+ }
+
+ // Move other to source
+ if (otherItemsCapacity)
+ {
+ _allocation.Allocate(otherItemsCapacity);
+ Memory::MoveItems(_allocation.Get(), other._allocation.Get(), otherItemsCapacity);
+ other._allocation.Free();
+ }
+
+ // Move temp to other
+ if (oldItemsCapacity)
+ {
+ other._allocation.Allocate(oldItemsCapacity);
+ Memory::MoveItems(other._allocation.Get(), oldAllocation.Get(), oldItemsCapacity);
+ }
+ }
::Swap(_count, other._count);
::Swap(_capacity, other._capacity);
- _allocation.Swap(other._allocation);
}
public:
diff --git a/Source/Engine/Core/Collections/OrderedDictionary.cs b/Source/Engine/Core/Collections/OrderedDictionary.cs
index ab3467d73..aa2fce31b 100644
--- a/Source/Engine/Core/Collections/OrderedDictionary.cs
+++ b/Source/Engine/Core/Collections/OrderedDictionary.cs
@@ -1,5 +1,7 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
+#if !NET9_0_OR_GREATER
+
using System;
using System.Collections;
using System.Collections.Generic;
@@ -674,3 +676,5 @@ namespace FlaxEngine.Collections
public object Current => Entry;
}
}
+
+#endif
diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp
index 15f7d1ce1..029012fa8 100644
--- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp
+++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp
@@ -23,18 +23,18 @@ ID3D11ShaderResourceView* EmptySRHandles[GPU_MAX_SR_BINDED] = {};
// Ensure to match the indirect commands arguments layout
static_assert(sizeof(GPUDispatchIndirectArgs) == sizeof(uint32) * 3, "Wrong size of GPUDrawIndirectArgs.");
static_assert(OFFSET_OF(GPUDispatchIndirectArgs, ThreadGroupCountX) == sizeof(uint32) * 0, "Wrong offset for GPUDrawIndirectArgs::ThreadGroupCountX");
-static_assert(OFFSET_OF(GPUDispatchIndirectArgs, ThreadGroupCountY) == sizeof(uint32) * 1,"Wrong offset for GPUDrawIndirectArgs::ThreadGroupCountY");
+static_assert(OFFSET_OF(GPUDispatchIndirectArgs, ThreadGroupCountY) == sizeof(uint32) * 1, "Wrong offset for GPUDrawIndirectArgs::ThreadGroupCountY");
static_assert(OFFSET_OF(GPUDispatchIndirectArgs, ThreadGroupCountZ) == sizeof(uint32) * 2, "Wrong offset for GPUDrawIndirectArgs::ThreadGroupCountZ");
//
static_assert(sizeof(GPUDrawIndirectArgs) == sizeof(D3D11_DRAW_INSTANCED_INDIRECT_ARGS), "Wrong size of GPUDrawIndirectArgs.");
static_assert(OFFSET_OF(GPUDrawIndirectArgs, VerticesCount) == OFFSET_OF(D3D11_DRAW_INSTANCED_INDIRECT_ARGS, VertexCountPerInstance), "Wrong offset for GPUDrawIndirectArgs::VerticesCount");
-static_assert(OFFSET_OF(GPUDrawIndirectArgs, InstanceCount) == OFFSET_OF(D3D11_DRAW_INSTANCED_INDIRECT_ARGS, InstanceCount),"Wrong offset for GPUDrawIndirectArgs::InstanceCount");
+static_assert(OFFSET_OF(GPUDrawIndirectArgs, InstanceCount) == OFFSET_OF(D3D11_DRAW_INSTANCED_INDIRECT_ARGS, InstanceCount), "Wrong offset for GPUDrawIndirectArgs::InstanceCount");
static_assert(OFFSET_OF(GPUDrawIndirectArgs, StartVertex) == OFFSET_OF(D3D11_DRAW_INSTANCED_INDIRECT_ARGS, StartVertexLocation), "Wrong offset for GPUDrawIndirectArgs::StartVertex");
static_assert(OFFSET_OF(GPUDrawIndirectArgs, StartInstance) == OFFSET_OF(D3D11_DRAW_INSTANCED_INDIRECT_ARGS, StartInstanceLocation), "Wrong offset for GPUDrawIndirectArgs::StartInstance");
//
static_assert(sizeof(GPUDrawIndexedIndirectArgs) == sizeof(D3D11_DRAW_INDEXED_INSTANCED_INDIRECT_ARGS), "Wrong size of GPUDrawIndexedIndirectArgs.");
static_assert(OFFSET_OF(GPUDrawIndexedIndirectArgs, IndicesCount) == OFFSET_OF(D3D11_DRAW_INDEXED_INSTANCED_INDIRECT_ARGS, IndexCountPerInstance), "Wrong offset for GPUDrawIndexedIndirectArgs::IndicesCount");
-static_assert(OFFSET_OF(GPUDrawIndexedIndirectArgs, InstanceCount) == OFFSET_OF(D3D11_DRAW_INDEXED_INSTANCED_INDIRECT_ARGS, InstanceCount),"Wrong offset for GPUDrawIndexedIndirectArgs::InstanceCount");
+static_assert(OFFSET_OF(GPUDrawIndexedIndirectArgs, InstanceCount) == OFFSET_OF(D3D11_DRAW_INDEXED_INSTANCED_INDIRECT_ARGS, InstanceCount), "Wrong offset for GPUDrawIndexedIndirectArgs::InstanceCount");
static_assert(OFFSET_OF(GPUDrawIndexedIndirectArgs, StartIndex) == OFFSET_OF(D3D11_DRAW_INDEXED_INSTANCED_INDIRECT_ARGS, StartIndexLocation), "Wrong offset for GPUDrawIndexedIndirectArgs::StartIndex");
static_assert(OFFSET_OF(GPUDrawIndexedIndirectArgs, StartVertex) == OFFSET_OF(D3D11_DRAW_INDEXED_INSTANCED_INDIRECT_ARGS, BaseVertexLocation), "Wrong offset for GPUDrawIndexedIndirectArgs::StartVertex");
static_assert(OFFSET_OF(GPUDrawIndexedIndirectArgs, StartInstance) == OFFSET_OF(D3D11_DRAW_INDEXED_INSTANCED_INDIRECT_ARGS, StartInstanceLocation), "Wrong offset for GPUDrawIndexedIndirectArgs::StartInstance");
@@ -559,15 +559,13 @@ void GPUContextDX11::SetState(GPUPipelineState* state)
#endif
GPUShaderProgramPSDX11* ps = nullptr;
D3D11_PRIMITIVE_TOPOLOGY primitiveTopology = D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED;
-
if (state)
{
ASSERT(_currentState->IsValid());
-
blendState = _currentState->BlendState;
rasterizerState = _device->RasterizerStates[_currentState->RasterizerStateIndex];
- depthStencilState = _device->DepthStencilStates[_currentState->DepthStencilStateIndex];
-
+ //depthStencilState = _device->DepthStencilStates2[_currentState->DepthStencilStateIndex];
+ depthStencilState = _currentState->DepthStencilState;
ASSERT(_currentState->VS != nullptr);
vs = _currentState->VS;
#if GPU_ALLOW_TESSELLATION_SHADERS
@@ -578,7 +576,6 @@ void GPUContextDX11::SetState(GPUPipelineState* state)
gs = _currentState->GS;
#endif
ps = _currentState->PS;
-
primitiveTopology = _currentState->PrimitiveTopology;
}
@@ -858,13 +855,13 @@ void GPUContextDX11::flushSRVs()
{
_srMaskDirtyGraphics = 0;
FLUSH_STAGE(VS);
- #if GPU_ALLOW_TESSELLATION_SHADERS
+#if GPU_ALLOW_TESSELLATION_SHADERS
FLUSH_STAGE(HS);
FLUSH_STAGE(DS);
- #endif
- #if GPU_ALLOW_GEOMETRY_SHADERS
+#endif
+#if GPU_ALLOW_GEOMETRY_SHADERS
FLUSH_STAGE(GS);
- #endif
+#endif
FLUSH_STAGE(PS);
}
}
diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp
index 724a5ea71..ae24d3879 100644
--- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp
+++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp
@@ -24,6 +24,56 @@
#define DX11_FORCE_USE_DX10 0
#define DX11_FORCE_USE_DX10_1 0
+static D3D11_COMPARISON_FUNC ToDX11(ComparisonFunc value)
+{
+ switch (value)
+ {
+ case ComparisonFunc::Never:
+ return D3D11_COMPARISON_NEVER;
+ case ComparisonFunc::Less:
+ return D3D11_COMPARISON_LESS;
+ case ComparisonFunc::Equal:
+ return D3D11_COMPARISON_EQUAL;
+ case ComparisonFunc::LessEqual:
+ return D3D11_COMPARISON_LESS_EQUAL;
+ case ComparisonFunc::Greater:
+ return D3D11_COMPARISON_GREATER;
+ case ComparisonFunc::NotEqual:
+ return D3D11_COMPARISON_NOT_EQUAL;
+ case ComparisonFunc::GreaterEqual:
+ return D3D11_COMPARISON_GREATER_EQUAL;
+ case ComparisonFunc::Always:
+ return D3D11_COMPARISON_ALWAYS;
+ default:
+ return (D3D11_COMPARISON_FUNC)-1;
+ }
+}
+
+static D3D11_STENCIL_OP ToDX11(StencilOperation value)
+{
+ switch (value)
+ {
+ case StencilOperation::Keep:
+ return D3D11_STENCIL_OP_KEEP;
+ case StencilOperation::Zero:
+ return D3D11_STENCIL_OP_ZERO;
+ case StencilOperation::Replace:
+ return D3D11_STENCIL_OP_REPLACE;
+ case StencilOperation::IncrementSaturated:
+ return D3D11_STENCIL_OP_INCR_SAT;
+ case StencilOperation::DecrementSaturated:
+ return D3D11_STENCIL_OP_DECR_SAT;
+ case StencilOperation::Invert:
+ return D3D11_STENCIL_OP_INVERT;
+ case StencilOperation::Increment:
+ return D3D11_STENCIL_OP_INCR;
+ case StencilOperation::Decrement:
+ return D3D11_STENCIL_OP_DECR;
+ default:
+ return (D3D11_STENCIL_OP)-1;
+ }
+}
+
static bool TryCreateDevice(IDXGIAdapter* adapter, D3D_FEATURE_LEVEL maxFeatureLevel, D3D_FEATURE_LEVEL* featureLevel)
{
ID3D11Device* device = nullptr;
@@ -251,22 +301,66 @@ GPUDeviceDX11::GPUDeviceDX11(IDXGIFactory* dxgiFactory, GPUAdapterDX* adapter)
, _factoryDXGI(dxgiFactory)
{
Platform::MemoryClear(RasterizerStates, sizeof(RasterizerStates));
- Platform::MemoryClear(DepthStencilStates, sizeof(DepthStencilStates));
+}
+
+ID3D11DepthStencilState* GPUDeviceDX11::GetDepthStencilState(const void* descriptionPtr)
+{
+ const GPUPipelineState::Description& description = *(const GPUPipelineState::Description*)descriptionPtr;
+ DepthStencilMode key;
+ Platform::MemoryClear(&key, sizeof(key)); // Ensure to clear any padding bytes for raw memory compare/hashing
+ key.DepthEnable = description.DepthEnable ? 1 : 0;
+ key.DepthWriteEnable = description.DepthWriteEnable ? 1 : 0;
+ key.DepthClipEnable = description.DepthClipEnable ? 1 : 0;
+ key.StencilEnable = description.StencilEnable ? 1 : 0;
+ key.StencilReadMask = description.StencilReadMask;
+ key.StencilWriteMask = description.StencilWriteMask;
+ key.DepthFunc = description.DepthFunc;
+ key.StencilFunc = description.StencilFunc;
+ key.StencilFailOp = description.StencilFailOp;
+ key.StencilDepthFailOp = description.StencilDepthFailOp;
+ key.StencilPassOp = description.StencilPassOp;
+
+ // Use lookup
+ ID3D11DepthStencilState* state = nullptr;
+ if (DepthStencilStates.TryGet(key, state))
+ return state;
+
+ // Try again but with lock to prevent race condition with double-adding the same thing
+ ScopeLock lock(StatesWriteLocker);
+ if (DepthStencilStates.TryGet(key, state))
+ return state;
+
+ // Prepare description
+ D3D11_DEPTH_STENCIL_DESC desc;
+ desc.DepthEnable = !!description.DepthEnable;
+ desc.DepthWriteMask = description.DepthWriteEnable ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO;
+ desc.DepthFunc = ToDX11(description.DepthFunc);
+ desc.StencilEnable = !!description.StencilEnable;
+ desc.StencilReadMask = description.StencilReadMask;
+ desc.StencilWriteMask = description.StencilWriteMask;
+ desc.FrontFace.StencilFailOp = ToDX11(description.StencilFailOp);
+ desc.FrontFace.StencilDepthFailOp = ToDX11(description.StencilDepthFailOp);
+ desc.FrontFace.StencilPassOp = ToDX11(description.StencilPassOp);
+ desc.FrontFace.StencilFunc = ToDX11(description.StencilFunc);
+ desc.BackFace = desc.FrontFace;
+
+ // Create object and cache it
+ VALIDATE_DIRECTX_CALL(_device->CreateDepthStencilState(&desc, &state));
+ DepthStencilStates.Add(key, state);
+ return state;
}
ID3D11BlendState* GPUDeviceDX11::GetBlendState(const BlendingMode& blending)
{
// Use lookup
- ID3D11BlendState* blendState = nullptr;
- if (BlendStates.TryGet(blending, blendState))
- return blendState;
-
- // Make it safe
- ScopeLock lock(BlendStatesWriteLocker);
-
- // Try again to prevent race condition with double-adding the same thing
- if (BlendStates.TryGet(blending, blendState))
- return blendState;
+ ID3D11BlendState* state = nullptr;
+ if (BlendStates.TryGet(blending, state))
+ return state;
+
+ // Try again but with lock to prevent race condition with double-adding the same thing
+ ScopeLock lock(StatesWriteLocker);
+ if (BlendStates.TryGet(blending, state))
+ return state;
// Prepare description
D3D11_BLEND_DESC desc;
@@ -285,13 +379,10 @@ ID3D11BlendState* GPUDeviceDX11::GetBlendState(const BlendingMode& blending)
desc.RenderTarget[i] = desc.RenderTarget[0];
#endif
- // Create object
- VALIDATE_DIRECTX_CALL(_device->CreateBlendState(&desc, &blendState));
-
- // Cache blend state
- BlendStates.Add(blending, blendState);
-
- return blendState;
+ // Create object and cache it
+ VALIDATE_DIRECTX_CALL(_device->CreateBlendState(&desc, &state));
+ BlendStates.Add(blending, state);
+ return state;
}
bool GPUDeviceDX11::Init()
@@ -326,9 +417,9 @@ bool GPUDeviceDX11::Init()
#if PLATFORM_WINDOWS
// Detect RenderDoc usage (UUID {A7AA6116-9C8D-4BBA-9083-B4D816B71B78})
IUnknown* unknown = nullptr;
- const GUID uuidRenderDoc = { 0xa7aa6116, 0x9c8d, 0x4bba, {0x90, 0x83, 0xb4, 0xd8, 0x16, 0xb7, 0x1b, 0x78}};
+ const GUID uuidRenderDoc = { 0xa7aa6116, 0x9c8d, 0x4bba, { 0x90, 0x83, 0xb4, 0xd8, 0x16, 0xb7, 0x1b, 0x78 } };
HRESULT hr = _device->QueryInterface(uuidRenderDoc, (void**)&unknown);
- if(SUCCEEDED(hr) && unknown)
+ if (SUCCEEDED(hr) && unknown)
{
IsDebugToolAttached = true;
unknown->Release();
@@ -560,31 +651,6 @@ bool GPUDeviceDX11::Init()
#undef CREATE_RASTERIZER_STATE
}
- // Depth Stencil States
- {
- D3D11_DEPTH_STENCIL_DESC dsDesc;
- dsDesc.StencilEnable = FALSE;
- dsDesc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK;
- dsDesc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK;
- const D3D11_DEPTH_STENCILOP_DESC defaultStencilOp = { D3D11_STENCIL_OP_KEEP, D3D11_STENCIL_OP_KEEP, D3D11_STENCIL_OP_KEEP, D3D11_COMPARISON_ALWAYS };
- dsDesc.FrontFace = defaultStencilOp;
- dsDesc.BackFace = defaultStencilOp;
- int32 index;
-#define CREATE_DEPTH_STENCIL_STATE(depthEnable, depthWrite) \
- dsDesc.DepthEnable = depthEnable; \
- dsDesc.DepthWriteMask = depthWrite ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; \
- for(int32 depthFunc = 1; depthFunc <= 8; depthFunc++) { \
- dsDesc.DepthFunc = (D3D11_COMPARISON_FUNC)depthFunc; \
- index = (int32)depthFunc + (depthEnable ? 0 : 9) + (depthWrite ? 0 : 18); \
- HRESULT result = _device->CreateDepthStencilState(&dsDesc, &DepthStencilStates[index]); \
- LOG_DIRECTX_RESULT_WITH_RETURN(result, true); }
- CREATE_DEPTH_STENCIL_STATE(false, false);
- CREATE_DEPTH_STENCIL_STATE(false, true);
- CREATE_DEPTH_STENCIL_STATE(true, true);
- CREATE_DEPTH_STENCIL_STATE(true, false);
-#undef CREATE_DEPTH_STENCIL_STATE
- }
-
_state = DeviceState::Ready;
return GPUDeviceDX::Init();
}
@@ -624,20 +690,15 @@ void GPUDeviceDX11::Dispose()
SAFE_RELEASE(_samplerPointWrap);
SAFE_RELEASE(_samplerShadow);
SAFE_RELEASE(_samplerShadowLinear);
- //
for (auto i = BlendStates.Begin(); i.IsNotEnd(); ++i)
- {
i->Value->Release();
- }
+ for (auto i = DepthStencilStates.Begin(); i.IsNotEnd(); ++i)
+ i->Value->Release();
BlendStates.Clear();
for (uint32 i = 0; i < ARRAY_COUNT(RasterizerStates); i++)
{
SAFE_RELEASE(RasterizerStates[i]);
}
- for (uint32 i = 0; i < ARRAY_COUNT(DepthStencilStates); i++)
- {
- SAFE_RELEASE(DepthStencilStates[i]);
- }
// Clear DirectX stuff
SAFE_DELETE(_mainContext);
diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.h b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.h
index 42bb6122e..ffe1688f1 100644
--- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.h
+++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.h
@@ -11,6 +11,7 @@
#if GRAPHICS_API_DIRECTX11
class Engine;
+enum class StencilOperation : byte;
class GPUContextDX11;
class GPUSwapChainDX11;
@@ -23,6 +24,34 @@ class GPUDeviceDX11 : public GPUDeviceDX
friend GPUSwapChainDX11;
private:
+ struct DepthStencilMode
+ {
+ int8 DepthEnable : 1;
+ int8 DepthWriteEnable : 1;
+ int8 DepthClipEnable : 1;
+ int8 StencilEnable : 1;
+ uint8 StencilReadMask;
+ uint8 StencilWriteMask;
+ ComparisonFunc DepthFunc;
+ ComparisonFunc StencilFunc;
+ StencilOperation StencilFailOp;
+ StencilOperation StencilDepthFailOp;
+ StencilOperation StencilPassOp;
+
+ bool operator==(const DepthStencilMode& other) const
+ {
+ return Platform::MemoryCompare(this, &other, sizeof(DepthStencilMode)) == 0;
+ }
+
+ friend uint32 GetHash(const DepthStencilMode& key)
+ {
+ uint32 hash = 0;
+ for (int32 i = 0; i < sizeof(DepthStencilMode) / 4; i++)
+ CombineHash(hash, ((uint32*)&key)[i]);
+ return hash;
+ }
+ };
+
// Private Stuff
ID3D11Device* _device = nullptr;
ID3D11DeviceContext* _imContext = nullptr;
@@ -40,10 +69,10 @@ private:
ID3D11SamplerState* _samplerShadowLinear = nullptr;
// Shared data for pipeline states
- CriticalSection BlendStatesWriteLocker;
+ CriticalSection StatesWriteLocker;
Dictionary BlendStates;
+ Dictionary DepthStencilStates;
ID3D11RasterizerState* RasterizerStates[3 * 2 * 2]; // Index = CullMode[0-2] + Wireframe[0?3] + DepthClipEnable[0?6]
- ID3D11DepthStencilState* DepthStencilStates[9 * 2 * 2]; // Index = ComparisonFunc[0-8] + DepthTestEnable[0?9] + DepthWriteEnable[0?18]
public:
static GPUDevice* Create();
@@ -75,6 +104,7 @@ public:
return _mainContext;
}
+ ID3D11DepthStencilState* GetDepthStencilState(const void* descriptionPtr);
ID3D11BlendState* GetBlendState(const BlendingMode& blending);
public:
diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUPipelineStateDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUPipelineStateDX11.cpp
index b1517b4fb..32822f8a2 100644
--- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUPipelineStateDX11.cpp
+++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUPipelineStateDX11.cpp
@@ -6,6 +6,7 @@
void GPUPipelineStateDX11::OnReleaseGPU()
{
+ DepthStencilState = nullptr;
BlendState = nullptr;
VS = nullptr;
#if GPU_ALLOW_TESSELLATION_SHADERS
@@ -58,8 +59,8 @@ bool GPUPipelineStateDX11::Init(const Description& desc)
#endif
// States
- DepthStencilStateIndex = static_cast(desc.DepthFunc) + (desc.DepthEnable ? 0 : 9) + (desc.DepthWriteEnable ? 0 : 18);
RasterizerStateIndex = static_cast(desc.CullMode) + (desc.Wireframe ? 0 : 3) + (desc.DepthClipEnable ? 0 : 6);
+ DepthStencilState = _device->GetDepthStencilState(&desc);
BlendState = _device->GetBlendState(desc.BlendMode);
// Calculate approx. memory usage (just to set sth)
diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUPipelineStateDX11.h b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUPipelineStateDX11.h
index 0fad61f70..12b9ea434 100644
--- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUPipelineStateDX11.h
+++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUPipelineStateDX11.h
@@ -15,8 +15,8 @@ class GPUPipelineStateDX11 : public GPUResourceDX11
{
public:
- int32 DepthStencilStateIndex;
int32 RasterizerStateIndex;
+ ID3D11DepthStencilState* DepthStencilState = nullptr;
ID3D11BlendState* BlendState = nullptr;
GPUShaderProgramVSDX11* VS = nullptr;
#if GPU_ALLOW_TESSELLATION_SHADERS
diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp
index 78c047cfc..0b251e5a6 100644
--- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp
+++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp
@@ -77,7 +77,7 @@ GPUDevice* GPUDeviceDX12::Create()
#endif
#ifdef __ID3D12DeviceRemovedExtendedDataSettings_FWD_DEFINED__
ComPtr dredSettings;
- VALIDATE_DIRECTX_CALL(D3D12GetDebugInterface(IID_PPV_ARGS(&dredSettings)));
+ D3D12GetDebugInterface(IID_PPV_ARGS(&dredSettings));
if (dredSettings)
{
// Turn on AutoBreadcrumbs and Page Fault reporting
diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp
index 5023bb21f..a8b34f781 100644
--- a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp
+++ b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp
@@ -723,11 +723,14 @@ void GPUContextVulkan::FrameBegin()
Platform::MemoryCopy(_samplerHandles, _device->HelperResources.GetStaticSamplers(), sizeof(VkSampler) * GPU_STATIC_SAMPLERS_COUNT);
Platform::MemoryClear(_samplerHandles + GPU_STATIC_SAMPLERS_COUNT, sizeof(_samplerHandles) - sizeof(VkSampler) * GPU_STATIC_SAMPLERS_COUNT);
+ // Init command buffer
+ const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer();
+ vkCmdSetStencilReference(cmdBuffer->GetHandle(), VK_STENCIL_FRONT_AND_BACK, _stencilRef);
+
#if VULKAN_RESET_QUERY_POOLS
// Reset pending queries
if (_device->QueriesToReset.HasItems())
{
- const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer();
for (auto query : _device->QueriesToReset)
query->Reset(cmdBuffer);
_device->QueriesToReset.Clear();
diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.cpp
index 3f3b3074c..17fbd6a36 100644
--- a/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.cpp
+++ b/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.cpp
@@ -34,6 +34,49 @@ static VkStencilOp ToVulkanStencilOp(const StencilOperation value)
}
}
+static VkBlendFactor ToVulkanBlendFactor(const BlendingMode::Blend value)
+{
+ switch (value)
+ {
+ case BlendingMode::Blend::Zero:
+ return VK_BLEND_FACTOR_ZERO;
+ case BlendingMode::Blend::One:
+ return VK_BLEND_FACTOR_ONE;
+ case BlendingMode::Blend::SrcColor:
+ return VK_BLEND_FACTOR_SRC_COLOR;
+ case BlendingMode::Blend::InvSrcColor:
+ return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
+ case BlendingMode::Blend::SrcAlpha:
+ return VK_BLEND_FACTOR_SRC_ALPHA;
+ case BlendingMode::Blend::InvSrcAlpha:
+ return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+ case BlendingMode::Blend::DestAlpha:
+ return VK_BLEND_FACTOR_DST_ALPHA;
+ case BlendingMode::Blend::InvDestAlpha:
+ return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
+ case BlendingMode::Blend::DestColor:
+ return VK_BLEND_FACTOR_DST_COLOR;
+ case BlendingMode::Blend::InvDestColor:
+ return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;
+ case BlendingMode::Blend::SrcAlphaSat:
+ return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE;
+ case BlendingMode::Blend::BlendFactor:
+ return VK_BLEND_FACTOR_CONSTANT_COLOR;
+ case BlendingMode::Blend::BlendInvFactor:
+ return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR;
+ case BlendingMode::Blend::Src1Color:
+ return VK_BLEND_FACTOR_SRC1_COLOR;
+ case BlendingMode::Blend::InvSrc1Color:
+ return VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR;
+ case BlendingMode::Blend::Src1Alpha:
+ return VK_BLEND_FACTOR_SRC1_ALPHA;
+ case BlendingMode::Blend::InvSrc1Alpha:
+ return VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA;
+ default:
+ return VK_BLEND_FACTOR_ZERO;
+ }
+}
+
GPUShaderProgramCSVulkan::~GPUShaderProgramCSVulkan()
{
if (_pipelineState)
@@ -316,7 +359,13 @@ bool GPUPipelineStateVulkan::Init(const Description& desc)
_dynamicStates[_descDynamic.dynamicStateCount++] = VK_DYNAMIC_STATE_VIEWPORT;
_dynamicStates[_descDynamic.dynamicStateCount++] = VK_DYNAMIC_STATE_SCISSOR;
_dynamicStates[_descDynamic.dynamicStateCount++] = VK_DYNAMIC_STATE_STENCIL_REFERENCE;
- static_assert(ARRAY_COUNT(_dynamicStates) <= 3, "Invalid dynamic states array.");
+#define IsBlendUsingBlendFactor(blend) blend == BlendingMode::Blend::BlendFactor || blend == BlendingMode::Blend::BlendInvFactor
+ if (desc.BlendMode.BlendEnable && (
+ IsBlendUsingBlendFactor(desc.BlendMode.SrcBlend) || IsBlendUsingBlendFactor(desc.BlendMode.SrcBlendAlpha) ||
+ IsBlendUsingBlendFactor(desc.BlendMode.DestBlend) || IsBlendUsingBlendFactor(desc.BlendMode.DestBlendAlpha)))
+ _dynamicStates[_descDynamic.dynamicStateCount++] = VK_DYNAMIC_STATE_BLEND_CONSTANTS;
+#undef IsBlendUsingBlendFactor
+ static_assert(ARRAY_COUNT(_dynamicStates) <= 4, "Invalid dynamic states array.");
_desc.pDynamicState = &_descDynamic;
// Multisample
@@ -337,7 +386,7 @@ bool GPUPipelineStateVulkan::Init(const Description& desc)
_descDepthStencil.front.failOp = ToVulkanStencilOp(desc.StencilFailOp);
_descDepthStencil.front.depthFailOp = ToVulkanStencilOp(desc.StencilDepthFailOp);
_descDepthStencil.front.passOp = ToVulkanStencilOp(desc.StencilPassOp);
- _descDepthStencil.front = _descDepthStencil.back;
+ _descDepthStencil.back = _descDepthStencil.front;
_desc.pDepthStencilState = &_descDepthStencil;
DepthReadEnable = desc.DepthEnable && desc.DepthFunc != ComparisonFunc::Always;
DepthWriteEnable = _descDepthStencil.depthWriteEnable;
@@ -370,21 +419,21 @@ bool GPUPipelineStateVulkan::Init(const Description& desc)
{
auto& blend = _descColorBlendAttachments[0];
blend.blendEnable = desc.BlendMode.BlendEnable;
- blend.srcColorBlendFactor = RenderToolsVulkan::ToVulkanBlendFactor(desc.BlendMode.SrcBlend);
- blend.dstColorBlendFactor = RenderToolsVulkan::ToVulkanBlendFactor(desc.BlendMode.DestBlend);
+ blend.srcColorBlendFactor = ToVulkanBlendFactor(desc.BlendMode.SrcBlend);
+ blend.dstColorBlendFactor = ToVulkanBlendFactor(desc.BlendMode.DestBlend);
blend.colorBlendOp = RenderToolsVulkan::ToVulkanBlendOp(desc.BlendMode.BlendOp);
- blend.srcAlphaBlendFactor = RenderToolsVulkan::ToVulkanBlendFactor(desc.BlendMode.SrcBlendAlpha);
- blend.dstAlphaBlendFactor = RenderToolsVulkan::ToVulkanBlendFactor(desc.BlendMode.DestBlendAlpha);
+ blend.srcAlphaBlendFactor = ToVulkanBlendFactor(desc.BlendMode.SrcBlendAlpha);
+ blend.dstAlphaBlendFactor = ToVulkanBlendFactor(desc.BlendMode.DestBlendAlpha);
blend.alphaBlendOp = RenderToolsVulkan::ToVulkanBlendOp(desc.BlendMode.BlendOpAlpha);
blend.colorWriteMask = (VkColorComponentFlags)desc.BlendMode.RenderTargetWriteMask;
}
for (int32 i = 1; i < GPU_MAX_RT_BINDED; i++)
_descColorBlendAttachments[i] = _descColorBlendAttachments[i - 1];
_descColorBlend.pAttachments = _descColorBlendAttachments;
- _descColorBlend.blendConstants[0] = 0.0f;
- _descColorBlend.blendConstants[1] = 0.0f;
- _descColorBlend.blendConstants[2] = 0.0f;
- _descColorBlend.blendConstants[3] = 0.0f;
+ _descColorBlend.blendConstants[0] = 1.0f;
+ _descColorBlend.blendConstants[1] = 1.0f;
+ _descColorBlend.blendConstants[2] = 1.0f;
+ _descColorBlend.blendConstants[3] = 1.0f;
_desc.pColorBlendState = &_descColorBlend;
ASSERT(DSWriteContainer.DescriptorWrites.IsEmpty());
diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.h
index 73e68a897..9e21adbb5 100644
--- a/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.h
+++ b/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.h
@@ -98,7 +98,7 @@ private:
#endif
VkPipelineViewportStateCreateInfo _descViewport;
VkPipelineDynamicStateCreateInfo _descDynamic;
- VkDynamicState _dynamicStates[3];
+ VkDynamicState _dynamicStates[4];
VkPipelineMultisampleStateCreateInfo _descMultisample;
VkPipelineDepthStencilStateCreateInfo _descDepthStencil;
VkPipelineRasterizationStateCreateInfo _descRasterization;
diff --git a/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.cpp
index 5c0a66d75..7ee231472 100644
--- a/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.cpp
+++ b/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.cpp
@@ -122,28 +122,6 @@ VkFormat RenderToolsVulkan::PixelFormatToVkFormat[110] =
VK_FORMAT_G8_B8R8_2PLANE_420_UNORM, // NV12
};
-VkBlendFactor RenderToolsVulkan::BlendToVkBlendFactor[20] =
-{
- VK_BLEND_FACTOR_MAX_ENUM,
- VK_BLEND_FACTOR_ZERO, // Zero
- VK_BLEND_FACTOR_ONE, // One
- VK_BLEND_FACTOR_SRC_COLOR, // SrcColor
- VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR, // InvSrcColor
- VK_BLEND_FACTOR_SRC_ALPHA, // SrcAlpha
- VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, // InvSrcAlpha
- VK_BLEND_FACTOR_DST_ALPHA, // DestAlpha
- VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA, // InvDestAlpha
- VK_BLEND_FACTOR_DST_COLOR, // DestColor,
- VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR, // InvDestColor
- VK_BLEND_FACTOR_SRC_ALPHA_SATURATE, // SrcAlphaSat
- VK_BLEND_FACTOR_CONSTANT_ALPHA, // BlendFactor
- VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA, // BlendInvFactor
- VK_BLEND_FACTOR_SRC1_COLOR, // Src1Color
- VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR, // InvSrc1Color
- VK_BLEND_FACTOR_SRC1_ALPHA, // Src1Alpha
- VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA, // InvSrc1Alpha
-};
-
VkBlendOp RenderToolsVulkan::OperationToVkBlendOp[6] =
{
VK_BLEND_OP_MAX_ENUM,
diff --git a/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h
index afe16a6bc..6bcd5fe7f 100644
--- a/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h
+++ b/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h
@@ -164,16 +164,6 @@ public:
return PixelFormatToVkFormat[(int32)value];
}
- ///
- /// Converts Flax blend mode to the Vulkan blend factor.
- ///
- /// The Flax blend mode.
- /// The Vulkan blend factor.
- static FORCE_INLINE VkBlendFactor ToVulkanBlendFactor(const BlendingMode::Blend value)
- {
- return BlendToVkBlendFactor[(int32)value];
- }
-
///
/// Converts Flax blend operation to the Vulkan blend operation.
///
diff --git a/Source/Engine/Networking/NetworkReplicator.cpp b/Source/Engine/Networking/NetworkReplicator.cpp
index 18518a16f..45bbd7887 100644
--- a/Source/Engine/Networking/NetworkReplicator.cpp
+++ b/Source/Engine/Networking/NetworkReplicator.cpp
@@ -489,7 +489,6 @@ void SetupObjectSpawnMessageItem(SpawnItem* e, NetworkMessage& msg)
NetworkMessageObjectSpawnItem msgDataItem;
msgDataItem.ObjectId = item.ObjectId;
msgDataItem.ParentId = item.ParentId;
- if (NetworkManager::IsClient())
{
// Remap local client object ids into server ids
IdsRemappingTable.KeyOf(msgDataItem.ObjectId, &msgDataItem.ObjectId);
@@ -580,11 +579,13 @@ void SendObjectSpawnMessage(const SpawnGroup& group, const Array
void SendObjectRoleMessage(const NetworkReplicatedObject& item, const NetworkClient* excludedClient = nullptr)
{
NetworkMessageObjectRole msgData;
+ Guid objectId = item.ObjectId;
+ IdsRemappingTable.KeyOf(objectId, &objectId);
msgData.OwnerClientId = item.OwnerClientId;
auto peer = NetworkManager::Peer;
NetworkMessage msg = peer->BeginSendMessage();
msg.WriteStructure(msgData);
- msg.WriteNetworkId(item.ObjectId);
+ msg.WriteNetworkId(objectId);
if (NetworkManager::IsClient())
{
NetworkManager::Peer->EndSendMessage(NetworkChannelType::ReliableOrdered, msg);
@@ -1325,6 +1326,7 @@ void NetworkReplicator::MapObjectId(Guid& objectId)
void NetworkReplicator::AddObjectIdMapping(const ScriptingObject* obj, const Guid& objectId)
{
CHECK(obj);
+ CHECK(objectId.IsValid());
const Guid id = obj->GetID();
NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Remap object ID={} into object {}:{}", objectId, id.ToString(), obj->GetType().ToString());
IdsRemappingTable[objectId] = id;
@@ -1688,7 +1690,6 @@ void NetworkInternal::NetworkReplicatorUpdate()
NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Despawn object ID={}", e.Id.ToString());
NetworkMessageObjectDespawn msgData;
Guid objectId = e.Id;
- if (isClient)
{
// Remap local client object ids into server ids
IdsRemappingTable.KeyOf(objectId, &objectId);
@@ -1917,7 +1918,6 @@ void NetworkInternal::NetworkReplicatorUpdate()
NetworkMessageObjectReplicate msgData;
msgData.OwnerFrame = NetworkManager::Frame;
Guid objectId = item.ObjectId, parentId = item.ParentId;
- if (isClient)
{
// Remap local client object ids into server ids
IdsRemappingTable.KeyOf(objectId, &objectId);
@@ -2020,7 +2020,6 @@ void NetworkInternal::NetworkReplicatorUpdate()
NetworkMessageObjectRpc msgData;
Guid msgObjectId = item.ObjectId;
Guid msgParentId = item.ParentId;
- if (isClient)
{
// Remap local client object ids into server ids
IdsRemappingTable.KeyOf(msgObjectId, &msgObjectId);
diff --git a/Source/Engine/Renderer/ContrastAdaptiveSharpeningPass.cpp b/Source/Engine/Renderer/ContrastAdaptiveSharpeningPass.cpp
index 2d93d525d..0e5e24dd1 100644
--- a/Source/Engine/Renderer/ContrastAdaptiveSharpeningPass.cpp
+++ b/Source/Engine/Renderer/ContrastAdaptiveSharpeningPass.cpp
@@ -35,16 +35,17 @@ void ContrastAdaptiveSharpeningPass::Dispose()
bool ContrastAdaptiveSharpeningPass::setupResources()
{
// Lazy-load shader
- if (!_shader)
+ if (_lazyInit && !_shader)
{
+ _lazyInit = false;
_shader = Content::LoadAsyncInternal(TEXT("Shaders/CAS"));
if (!_shader)
- return false;
+ return true;
#if COMPILE_WITH_DEV_ENV
_shader.Get()->OnReloading.Bind(this);
#endif
}
- if (!_shader->IsLoaded())
+ if (!_shader || !_shader->IsLoaded())
return true;
const auto shader = _shader->GetShader();
diff --git a/Source/Engine/Renderer/ContrastAdaptiveSharpeningPass.h b/Source/Engine/Renderer/ContrastAdaptiveSharpeningPass.h
index d98f7fa3c..ba317c01d 100644
--- a/Source/Engine/Renderer/ContrastAdaptiveSharpeningPass.h
+++ b/Source/Engine/Renderer/ContrastAdaptiveSharpeningPass.h
@@ -13,6 +13,7 @@ class ContrastAdaptiveSharpeningPass : public RendererPass _shader;
GPUPipelineState* _psCAS = nullptr;
+ bool _lazyInit = true;
public:
bool CanRender(const RenderContext& renderContext);
diff --git a/Source/Engine/Scripting/BinaryModule.cpp b/Source/Engine/Scripting/BinaryModule.cpp
index 2f3483a07..a44bfc4da 100644
--- a/Source/Engine/Scripting/BinaryModule.cpp
+++ b/Source/Engine/Scripting/BinaryModule.cpp
@@ -846,12 +846,12 @@ namespace
MMethod* ManagedBinaryModule::FindMethod(MClass* mclass, const ScriptingTypeMethodSignature& signature)
{
+#if USE_CSHARP
if (!mclass)
return nullptr;
const auto& methods = mclass->GetMethods();
for (MMethod* method : methods)
{
-#if USE_CSHARP
if (method->IsStatic() != signature.IsStatic)
continue;
if (method->GetName() != signature.Name)
@@ -872,8 +872,8 @@ MMethod* ManagedBinaryModule::FindMethod(MClass* mclass, const ScriptingTypeMeth
}
if (isValid && VariantTypeEquals(signature.ReturnType, method->GetReturnType()))
return method;
-#endif
}
+#endif
return nullptr;
}
@@ -1274,6 +1274,7 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp
// Marshal parameters
void** params = (void**)alloca(parametersCount * sizeof(void*));
+ void** outParams = nullptr;
bool failed = false;
bool hasOutParams = false;
for (int32 paramIdx = 0; paramIdx < parametersCount; paramIdx++)
@@ -1290,6 +1291,14 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp
LOG(Error, "Failed to marshal parameter {5}:{4} of method '{0}.{1}' (args count: {2}), value type: {6}, value: {3}", String(mMethod->GetParentClass()->GetFullName()), String(mMethod->GetName()), parametersCount, paramValue, MCore::Type::ToString(paramType), paramIdx, paramValue.Type);
return true;
}
+ if (isOut && MCore::Type::IsReference(paramType) && MCore::Type::GetType(paramType) == MTypes::Object)
+ {
+ // Object passed as out param so pass pointer to the value storage for proper marshalling
+ if (!outParams)
+ outParams = (void**)alloca(parametersCount * sizeof(void*));
+ outParams[paramIdx] = params[paramIdx];
+ params[paramIdx] = &outParams[paramIdx];
+ }
}
// Invoke the method
@@ -1354,6 +1363,13 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp
}
break;
}
+ default:
+ {
+ MType* paramType = mMethod->GetParameterType(paramIdx);
+ if (MCore::Type::IsReference(paramType) && MCore::Type::GetType(paramType) == MTypes::Object)
+ paramValue = MUtils::UnboxVariant((MObject*)outParams[paramIdx]);
+ break;
+ }
}
}
}
diff --git a/Source/Engine/Scripting/ManagedCLR/MUtils.cpp b/Source/Engine/Scripting/ManagedCLR/MUtils.cpp
index 810abbe48..2bba64d22 100644
--- a/Source/Engine/Scripting/ManagedCLR/MUtils.cpp
+++ b/Source/Engine/Scripting/ManagedCLR/MUtils.cpp
@@ -878,7 +878,7 @@ MClass* MUtils::GetClass(const VariantType& value)
case VariantType::Blob:
return MCore::Array::GetClass(MCore::TypeCache::Byte);
case VariantType::Float2:
- return Double2::TypeInitializer.GetClass();
+ return Float2::TypeInitializer.GetClass();
case VariantType::Float3:
return Float3::TypeInitializer.GetClass();
case VariantType::Float4:
diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp
index 38df5c16f..ddafab614 100644
--- a/Source/Engine/Scripting/Runtime/DotNet.cpp
+++ b/Source/Engine/Scripting/Runtime/DotNet.cpp
@@ -1758,7 +1758,7 @@ bool InitHostfxr()
// Warn user about missing .Net
#if PLATFORM_DESKTOP
- Platform::OpenUrl(TEXT("https://dotnet.microsoft.com/en-us/download/dotnet/8.0"));
+ Platform::OpenUrl(TEXT("https://dotnet.microsoft.com/en-us/download/dotnet"));
#endif
#if USE_EDITOR
LOG(Fatal, "Missing .NET 8 or later SDK installation required to run Flax Editor.");
diff --git a/Source/Engine/Tests/TestCollections.cpp b/Source/Engine/Tests/TestCollections.cpp
index c54e98c15..f9b0e47ca 100644
--- a/Source/Engine/Tests/TestCollections.cpp
+++ b/Source/Engine/Tests/TestCollections.cpp
@@ -7,6 +7,24 @@
#include "Engine/Core/Collections/Dictionary.h"
#include
+const bool TestBits[] = { true, false, true, false };
+
+template
+void InitBitArray(BitArray& array)
+{
+ array.Add(TestBits, ARRAY_COUNT(TestBits));
+}
+
+template
+void CheckBitArray(const BitArray& array)
+{
+ CHECK(array.Count() == ARRAY_COUNT(TestBits));
+ for (int32 i = 0; i < ARRAY_COUNT(TestBits); i++)
+ {
+ CHECK(array[i] == TestBits[i]);
+ }
+}
+
TEST_CASE("Array")
{
SECTION("Test Allocators")
@@ -84,6 +102,44 @@ TEST_CASE("BitArray")
}
}
+ SECTION("Test Move/Copy")
+ {
+ BitArray<> array1;
+ BitArray> array2;
+ BitArray> array3;
+ BitArray> array4;
+
+ InitBitArray(array1);
+ InitBitArray(array2);
+ InitBitArray(array3);
+ InitBitArray(array4);
+
+ CheckBitArray(array1);
+ CheckBitArray(array2);
+ CheckBitArray(array3);
+ CheckBitArray(array4);
+
+ BitArray<> arrayClone1 = array1;
+ BitArray> arrayClone2(array1);
+ BitArray> arrayClone3(MoveTemp(array1));
+ BitArray<> arrayClone4(MoveTemp(array1));
+ BitArray> arrayClone5 = MoveTemp(array2);
+ BitArray> arrayClone6 = MoveTemp(array3);
+ BitArray> arrayClone7 = MoveTemp(array4);
+
+ CheckBitArray(arrayClone1);
+ CheckBitArray(arrayClone2);
+ CheckBitArray(arrayClone4);
+ CheckBitArray(arrayClone5);
+ CheckBitArray(arrayClone6);
+ CheckBitArray(arrayClone7);
+
+ CHECK(array1.Count() == 0);
+ CHECK(array2.Count() == 0);
+ CHECK(array3.Count() == 0);
+ CHECK(array4.Count() == 0);
+ }
+
// Generate some random data for testing
BitArray<> testData;
testData.Resize(32);
diff --git a/Source/Engine/Tests/TestScripting.h b/Source/Engine/Tests/TestScripting.h
index a308a9237..91b18efa5 100644
--- a/Source/Engine/Tests/TestScripting.h
+++ b/Source/Engine/Tests/TestScripting.h
@@ -35,6 +35,16 @@ API_STRUCT(NoDefault) struct TestDefaultValues
API_FIELD() float TestFloat10 = 1.f;
// Default value case 11
API_FIELD() float TestFloat11 = 1;
+
+ // Test code injection parsing
+ API_INJECT_CODE(csharp, ""
+"namespace Test"
+"{"
+"\t/// \n\t/// Test code injection parsing\n\t/// "
+" public static class TestClass"
+" {"
+" }"
+"}")
};
// Test interface (name conflict with namespace)
diff --git a/Source/Engine/UI/GUI/CanvasContainer.cs b/Source/Engine/UI/GUI/CanvasContainer.cs
index 82c68a2cb..15e77985e 100644
--- a/Source/Engine/UI/GUI/CanvasContainer.cs
+++ b/Source/Engine/UI/GUI/CanvasContainer.cs
@@ -29,6 +29,33 @@ namespace FlaxEngine.GUI
return ((CanvasRootControl)a).Canvas.Order - ((CanvasRootControl)b).Canvas.Order;
}
+ private bool RayCast3D(ref Float2 location, out Control hit, out Float2 hitLocation)
+ {
+ hit = null;
+ hitLocation = Float2.Zero;
+
+ // Calculate 3D mouse ray
+ UICanvas.CalculateRay(ref location, out Ray ray);
+
+ // Test 3D canvases
+ var layerMask = MainRenderTask.Instance?.ViewLayersMask ?? LayersMask.Default;
+ for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
+ {
+ var child = (CanvasRootControl)_children[i];
+ if (child.Visible && child.Enabled && child.Is3D && layerMask.HasLayer(child.Canvas.Layer))
+ {
+ if (child.Intersects3D(ref ray, out var childLocation))
+ {
+ hit = child;
+ hitLocation = childLocation;
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
///
public override void OnChildrenChanged()
{
@@ -63,8 +90,20 @@ namespace FlaxEngine.GUI
///
public override bool RayCast(ref Float2 location, out Control hit)
{
- // Ignore self
- return RayCastChildren(ref location, out hit);
+ // Ignore self and only test children
+ if (RayCastChildren(ref location, out hit))
+ return true;
+
+ // Test 3D
+ if (RayCast3D(ref location, out hit, out var hitLocation))
+ {
+ // Test deeper to hit actual control not the root
+ hit.RayCast(ref hitLocation, out hit);
+ if (hit != null)
+ return true;
+ }
+
+ return false;
}
///
@@ -81,22 +120,10 @@ namespace FlaxEngine.GUI
// 2D GUI first
base.OnMouseEnter(location);
- // Calculate 3D mouse ray
- UICanvas.CalculateRay(ref location, out Ray ray);
-
// Test 3D
- var layerMask = MainRenderTask.Instance?.ViewLayersMask ?? LayersMask.Default;
- for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
+ if (RayCast3D(ref location, out var hit, out var hitLocation))
{
- var child = (CanvasRootControl)_children[i];
- if (child.Visible && child.Enabled && child.Is3D && layerMask.HasLayer(child.Canvas.Layer))
- {
- if (child.Intersects3D(ref ray, out var childLocation))
- {
- child.OnMouseEnter(childLocation);
- return;
- }
- }
+ hit.OnMouseEnter(hitLocation);
}
}
@@ -170,22 +197,11 @@ namespace FlaxEngine.GUI
if (base.OnMouseWheel(location, delta))
return true;
- // Calculate 3D mouse ray
- UICanvas.CalculateRay(ref location, out Ray ray);
-
// Test 3D
- var layerMask = MainRenderTask.Instance?.ViewLayersMask ?? LayersMask.Default;
- for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
+ if (RayCast3D(ref location, out var hit, out var hitLocation))
{
- var child = (CanvasRootControl)_children[i];
- if (child.Visible && child.Enabled && child.Is3D && layerMask.HasLayer(child.Canvas.Layer))
- {
- if (child.Intersects3D(ref ray, out var childLocation))
- {
- child.OnMouseWheel(childLocation, delta);
- return true;
- }
- }
+ hit.OnMouseWheel(hitLocation, delta);
+ return true;
}
return false;
@@ -198,22 +214,11 @@ namespace FlaxEngine.GUI
if (base.OnMouseDown(location, button))
return true;
- // Calculate 3D mouse ray
- UICanvas.CalculateRay(ref location, out Ray ray);
-
// Test 3D
- var layerMask = MainRenderTask.Instance?.ViewLayersMask ?? LayersMask.Default;
- for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
+ if (RayCast3D(ref location, out var hit, out var hitLocation))
{
- var child = (CanvasRootControl)_children[i];
- if (child.Visible && child.Enabled && child.Is3D && layerMask.HasLayer(child.Canvas.Layer))
- {
- if (child.Intersects3D(ref ray, out var childLocation))
- {
- child.OnMouseDown(childLocation, button);
- return true;
- }
- }
+ hit.OnMouseDown(hitLocation, button);
+ return true;
}
return false;
@@ -226,22 +231,11 @@ namespace FlaxEngine.GUI
if (base.OnMouseUp(location, button))
return true;
- // Calculate 3D mouse ray
- UICanvas.CalculateRay(ref location, out Ray ray);
-
// Test 3D
- var layerMask = MainRenderTask.Instance?.ViewLayersMask ?? LayersMask.Default;
- for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
+ if (RayCast3D(ref location, out var hit, out var hitLocation))
{
- var child = (CanvasRootControl)_children[i];
- if (child.Visible && child.Enabled && child.Is3D && layerMask.HasLayer(child.Canvas.Layer))
- {
- if (child.Intersects3D(ref ray, out var childLocation))
- {
- child.OnMouseUp(childLocation, button);
- return true;
- }
- }
+ hit.OnMouseUp(hitLocation, button);
+ return true;
}
return false;
@@ -254,22 +248,11 @@ namespace FlaxEngine.GUI
if (base.OnMouseDoubleClick(location, button))
return true;
- // Calculate 3D mouse ray
- UICanvas.CalculateRay(ref location, out Ray ray);
-
// Test 3D
- var layerMask = MainRenderTask.Instance?.ViewLayersMask ?? LayersMask.Default;
- for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
+ if (RayCast3D(ref location, out var hit, out var hitLocation))
{
- var child = (CanvasRootControl)_children[i];
- if (child.Visible && child.Enabled && child.Is3D && layerMask.HasLayer(child.Canvas.Layer))
- {
- if (child.Intersects3D(ref ray, out var childLocation))
- {
- child.OnMouseDoubleClick(childLocation, button);
- return true;
- }
- }
+ hit.OnMouseDoubleClick(hitLocation, button);
+ return true;
}
return false;
diff --git a/Source/Engine/UI/GUI/Common/TextBox.cs b/Source/Engine/UI/GUI/Common/TextBox.cs
index f99d7140a..da8b299cb 100644
--- a/Source/Engine/UI/GUI/Common/TextBox.cs
+++ b/Source/Engine/UI/GUI/Common/TextBox.cs
@@ -24,6 +24,18 @@ namespace FlaxEngine.GUI
get => _watermarkText;
set => _watermarkText = value;
}
+
+ ///
+ /// Whether to Obfuscate the text with a different character.
+ ///
+ [EditorOrder(21), Tooltip("Whether to Obfuscate the text with a different character.")]
+ public bool ObfuscateText = false;
+
+ ///
+ /// The character to Obfuscate the text.
+ ///
+ [EditorOrder(22), VisibleIf(nameof(ObfuscateText)), Tooltip("The character to Obfuscate the text.")]
+ public char ObfuscateCharacter = '\u25cf';
///
/// The text case.
@@ -159,6 +171,9 @@ namespace FlaxEngine.GUI
private string ConvertedText()
{
+ if (ObfuscateText)
+ return new string(ObfuscateCharacter, _text.Length);
+
string text = _text;
switch (CaseOption)
{
diff --git a/Source/Engine/UI/GUI/RootControl.cs b/Source/Engine/UI/GUI/RootControl.cs
index 43fce3d25..a325018a8 100644
--- a/Source/Engine/UI/GUI/RootControl.cs
+++ b/Source/Engine/UI/GUI/RootControl.cs
@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace FlaxEngine.GUI
{
///
- /// GUI root control that is represented by a window or an canvas and can contain children but has no parent at all. It's a source of the input events.
+ /// GUI root control that is represented by a window or a canvas and can contain children but has no parent at all. It's a source of the input events.
///
public abstract class RootControl : ContainerControl
{
diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs
index fd2fff04a..d85f1f342 100644
--- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs
+++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs
@@ -1526,16 +1526,54 @@ namespace Flax.Build.Bindings
return desc;
}
+ private static string ParseString(ref ParsingContext context)
+ {
+ // Read string (support multi-line string literals)
+ string str = string.Empty;
+ int startLine = -1;
+ while (true)
+ {
+ var token = context.Tokenizer.NextToken();
+ if (token.Type == TokenType.String)
+ {
+ if (startLine == -1)
+ startLine = context.Tokenizer.CurrentLine;
+ else if (startLine != context.Tokenizer.CurrentLine)
+ str += "\n";
+ var tokenStr = token.Value;
+ if (tokenStr.Length >= 2 && tokenStr[0] == '\"' && tokenStr[^1] == '\"')
+ tokenStr = tokenStr.Substring(1, tokenStr.Length - 2);
+ str += tokenStr;
+ }
+ else if (token.Type == TokenType.EndOfFile)
+ {
+ break;
+ }
+ else
+ {
+ if (str == string.Empty)
+ throw new Exception($"Expected {TokenType.String}, but got {token} at line {context.Tokenizer.CurrentLine}.");
+ context.Tokenizer.PreviousToken();
+ break;
+ }
+ }
+
+ // Apply automatic formatting for special characters
+ str = str.Replace("\\\"", "\"");
+ str = str.Replace("\\n", "\n");
+ str = str.Replace("\\\n", "\n");
+ str = str.Replace("\\\r\n", "\n");
+ str = str.Replace("\t", " ");
+ str = str.Replace("\\t", " ");
+ return str;
+ }
+
private static InjectCodeInfo ParseInjectCode(ref ParsingContext context)
{
context.Tokenizer.ExpectToken(TokenType.LeftParent);
var desc = new InjectCodeInfo();
context.Tokenizer.SkipUntil(TokenType.Comma, out desc.Lang);
- desc.Code = context.Tokenizer.ExpectToken(TokenType.String).Value.Replace("\\\"", "\"");
- desc.Code = desc.Code.Substring(1, desc.Code.Length - 2);
- desc.Code = desc.Code.Replace("\\\n", "\n");
- desc.Code = desc.Code.Replace("\\\r\n", "\n");
- desc.Code = desc.Code.Replace("\t", " ");
+ desc.Code = ParseString(ref context);
context.Tokenizer.ExpectToken(TokenType.RightParent);
return desc;
}
diff --git a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs
index 0e200ea39..093b2b39e 100644
--- a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs
+++ b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs
@@ -135,7 +135,7 @@ namespace Flax.Build
///
/// The maximum SDK version.
///
- public static Version MaximumVersion => new Version(8, 0);
+ public static Version MaximumVersion => new Version(9, 0);
///
public override TargetPlatform[] Platforms
@@ -166,10 +166,11 @@ namespace Flax.Build
///
public string CSharpLanguageVersion => Version.Major switch
{
- 8 => "12.0",
- 7 => "11.0",
- 6 => "10.0",
- 5 => "9.0",
+ _ when Version.Major >= 9 => "13.0",
+ _ when Version.Major >= 8 => "12.0",
+ _ when Version.Major >= 7 => "11.0",
+ _ when Version.Major >= 6 => "10.0",
+ _ when Version.Major >= 5 => "9.0",
_ => "7.3",
};