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